You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					125 lines
				
				4.0 KiB
			
		
		
			
		
	
	
					125 lines
				
				4.0 KiB
			| 
								 
											5 years ago
										 
									 | 
							
								#!/usr/bin/env python3
							 | 
						||
| 
								 | 
							
								import math
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import random
							 | 
						||
| 
								 | 
							
								import shutil
							 | 
						||
| 
								 | 
							
								import subprocess
							 | 
						||
| 
								 | 
							
								import time
							 | 
						||
| 
								 | 
							
								import unittest
							 | 
						||
| 
								 | 
							
								from parameterized import parameterized
							 | 
						||
| 
								 | 
							
								from pathlib import Path
							 | 
						||
| 
								 | 
							
								from tqdm import trange
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from common.params import Params
							 | 
						||
| 
								 | 
							
								from common.timeout import Timeout
							 | 
						||
| 
								 | 
							
								from selfdrive.hardware import EON, TICI
							 | 
						||
| 
								 | 
							
								from selfdrive.test.helpers import with_processes
							 | 
						||
| 
								 | 
							
								from selfdrive.loggerd.config import ROOT, CAMERA_FPS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# baseline file sizes for a 2s segment, in bytes
							 | 
						||
| 
								 | 
							
								FULL_SIZE = 1253786
							 | 
						||
| 
								 | 
							
								if EON:
							 | 
						||
| 
								 | 
							
								  CAMERAS = {
							 | 
						||
| 
								 | 
							
								    "fcamera": FULL_SIZE,
							 | 
						||
| 
								 | 
							
								    "dcamera": 770920,
							 | 
						||
| 
								 | 
							
								    "qcamera": 38533,
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								elif TICI:
							 | 
						||
| 
								 | 
							
								  CAMERAS = {f"{c}camera": FULL_SIZE if c!="q" else 38533 for c in ["f", "e", "d", "q"]}
							 | 
						||
| 
								 | 
							
								else:
							 | 
						||
| 
								 | 
							
								  CAMERAS = {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ALL_CAMERA_COMBINATIONS = [(cameras,) for cameras in [CAMERAS, {k:CAMERAS[k] for k in CAMERAS if k!='dcamera'}]]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								FRAME_TOLERANCE = 0
							 | 
						||
| 
								 | 
							
								FILE_SIZE_TOLERANCE = 0.5
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class TestEncoder(unittest.TestCase):
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # TODO: all of loggerd should work on PC
							 | 
						||
| 
								 | 
							
								  @classmethod
							 | 
						||
| 
								 | 
							
								  def setUpClass(cls):
							 | 
						||
| 
								 | 
							
								    if not (EON or TICI):
							 | 
						||
| 
								 | 
							
								      raise unittest.SkipTest
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def setUp(self):
							 | 
						||
| 
								 | 
							
								    self._clear_logs()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    self.segment_length = 2
							 | 
						||
| 
								 | 
							
								    os.environ["LOGGERD_TEST"] = "1"
							 | 
						||
| 
								 | 
							
								    os.environ["LOGGERD_SEGMENT_LENGTH"] = str(self.segment_length)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def tearDown(self):
							 | 
						||
| 
								 | 
							
								    self._clear_logs()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def _clear_logs(self):
							 | 
						||
| 
								 | 
							
								    if os.path.exists(ROOT):
							 | 
						||
| 
								 | 
							
								      shutil.rmtree(ROOT)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  def _get_latest_segment_path(self):
							 | 
						||
| 
								 | 
							
								    last_route = sorted(Path(ROOT).iterdir(), key=os.path.getmtime)[-1]
							 | 
						||
| 
								 | 
							
								    return os.path.join(ROOT, last_route)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  # TODO: this should run faster than real time
							 | 
						||
| 
								 | 
							
								  @parameterized.expand(ALL_CAMERA_COMBINATIONS)
							 | 
						||
| 
								 | 
							
								  @with_processes(['camerad', 'sensord', 'loggerd'], init_time=5)
							 | 
						||
| 
								 | 
							
								  def test_log_rotation(self, cameras):
							 | 
						||
| 
								 | 
							
								    print("checking targets:", cameras)
							 | 
						||
| 
								 | 
							
								    Params().put("RecordFront", "1" if 'dcamera' in cameras else "0")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    num_segments = random.randint(80, 150)
							 | 
						||
| 
								 | 
							
								    if "CI" in os.environ:
							 | 
						||
| 
								 | 
							
								      num_segments = random.randint(15, 20) # ffprobe is slow on comma two
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # wait for loggerd to make the dir for first segment
							 | 
						||
| 
								 | 
							
								    time.sleep(10)
							 | 
						||
| 
								 | 
							
								    route_prefix_path = None
							 | 
						||
| 
								 | 
							
								    with Timeout(30):
							 | 
						||
| 
								 | 
							
								      while route_prefix_path is None:
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								          route_prefix_path = self._get_latest_segment_path().rsplit("--", 1)[0]
							 | 
						||
| 
								 | 
							
								        except Exception:
							 | 
						||
| 
								 | 
							
								          time.sleep(2)
							 | 
						||
| 
								 | 
							
								          continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for i in trange(num_segments):
							 | 
						||
| 
								 | 
							
								      # poll for next segment
							 | 
						||
| 
								 | 
							
								      if i < num_segments - 1:
							 | 
						||
| 
								 | 
							
								        with Timeout(self.segment_length*3, error_msg=f"timed out waiting for segment {i}"):
							 | 
						||
| 
								 | 
							
								          while True:
							 | 
						||
| 
								 | 
							
								            seg_num = int(self._get_latest_segment_path().rsplit("--", 1)[1])
							 | 
						||
| 
								 | 
							
								            if seg_num > i:
							 | 
						||
| 
								 | 
							
								              break
							 | 
						||
| 
								 | 
							
								            time.sleep(0.1)
							 | 
						||
| 
								 | 
							
								      else:
							 | 
						||
| 
								 | 
							
								        time.sleep(self.segment_length)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      # check each camera file size
							 | 
						||
| 
								 | 
							
								      for camera, size in cameras.items():
							 | 
						||
| 
								 | 
							
								        ext = "ts" if camera=='qcamera' else "hevc"
							 | 
						||
| 
								 | 
							
								        file_path = f"{route_prefix_path}--{i}/{camera}.{ext}"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check file size
							 | 
						||
| 
								 | 
							
								        self.assertTrue(os.path.exists(file_path), f"couldn't find {file_path}")
							 | 
						||
| 
								 | 
							
								        file_size = os.path.getsize(file_path)
							 | 
						||
| 
								 | 
							
								        self.assertTrue(math.isclose(file_size, size, rel_tol=FILE_SIZE_TOLERANCE),
							 | 
						||
| 
								 | 
							
								                        f"{camera} failed size check: expected {size}, got {file_size}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if camera == 'qcamera':
							 | 
						||
| 
								 | 
							
								          continue
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # check frame count
							 | 
						||
| 
								 | 
							
								        cmd = f"ffprobe -v error -count_frames -select_streams v:0 -show_entries stream=nb_read_frames \
							 | 
						||
| 
								 | 
							
								               -of default=nokey=1:noprint_wrappers=1 {file_path}"
							 | 
						||
| 
								 | 
							
								        expected_frames = self.segment_length * CAMERA_FPS // 2 if (EON and camera=='dcamera') else self.segment_length * CAMERA_FPS
							 | 
						||
| 
								 | 
							
								        frame_tolerance = FRAME_TOLERANCE+1 if (EON and camera=='dcamera') or i==0 else FRAME_TOLERANCE
							 | 
						||
| 
								 | 
							
								        frame_count = int(subprocess.check_output(cmd, shell=True, encoding='utf8').strip())
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.assertTrue(abs(expected_frames - frame_count) <= frame_tolerance,
							 | 
						||
| 
								 | 
							
								                        f"{camera} failed frame count check: expected {expected_frames}, got {frame_count}")
							 | 
						||
| 
								 | 
							
								      shutil.rmtree(f"{route_prefix_path}--{i}")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if __name__ == "__main__":
							 | 
						||
| 
								 | 
							
								  unittest.main()
							 |