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.
		
		
		
		
			
				
					161 lines
				
				5.1 KiB
			
		
		
			
		
	
	
					161 lines
				
				5.1 KiB
			| 
											5 years ago
										 | #!/usr/bin/env python3
 | ||
|  | import math
 | ||
|  | import os
 | ||
|  | import random
 | ||
|  | import shutil
 | ||
|  | import subprocess
 | ||
|  | import time
 | ||
|  | import unittest
 | ||
|  | from pathlib import Path
 | ||
| 
											5 years ago
										 | 
 | ||
|  | from parameterized import parameterized
 | ||
| 
											5 years ago
										 | from tqdm import trange
 | ||
|  | 
 | ||
|  | from common.params import Params
 | ||
|  | from common.timeout import Timeout
 | ||
| 
											3 years ago
										 | from system.hardware import TICI
 | ||
| 
											3 years ago
										 | from system.loggerd.config import ROOT
 | ||
| 
											5 years ago
										 | from selfdrive.manager.process_config import managed_processes
 | ||
| 
											5 years ago
										 | from tools.lib.logreader import LogReader
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 | SEGMENT_LENGTH = 2
 | ||
| 
											4 years ago
										 | FULL_SIZE = 2507572
 | ||
|  | CAMERAS = [
 | ||
|  |   ("fcamera.hevc", 20, FULL_SIZE, "roadEncodeIdx"),
 | ||
|  |   ("dcamera.hevc", 20, FULL_SIZE, "driverEncodeIdx"),
 | ||
|  |   ("ecamera.hevc", 20, FULL_SIZE, "wideRoadEncodeIdx"),
 | ||
| 
											4 years ago
										 |   ("qcamera.ts", 20, 130000, None),
 | ||
| 
											4 years ago
										 | ]
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 | # we check frame count, so we don't have to be too strict on size
 | ||
| 
											5 years ago
										 | FILE_SIZE_TOLERANCE = 0.5
 | ||
|  | 
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 | class TestEncoder(unittest.TestCase):
 | ||
|  | 
 | ||
|  |   # TODO: all of loggerd should work on PC
 | ||
|  |   @classmethod
 | ||
|  |   def setUpClass(cls):
 | ||
| 
											4 years ago
										 |     if not TICI:
 | ||
| 
											5 years ago
										 |       raise unittest.SkipTest
 | ||
|  | 
 | ||
|  |   def setUp(self):
 | ||
|  |     self._clear_logs()
 | ||
|  |     os.environ["LOGGERD_TEST"] = "1"
 | ||
| 
											5 years ago
										 |     os.environ["LOGGERD_SEGMENT_LENGTH"] = str(SEGMENT_LENGTH)
 | ||
| 
											5 years ago
										 | 
 | ||
|  |   def tearDown(self):
 | ||
|  |     self._clear_logs()
 | ||
|  | 
 | ||
|  |   def _clear_logs(self):
 | ||
|  |     if os.path.exists(ROOT):
 | ||
|  |       shutil.rmtree(ROOT)
 | ||
|  | 
 | ||
|  |   def _get_latest_segment_path(self):
 | ||
| 
											5 years ago
										 |     last_route = sorted(Path(ROOT).iterdir())[-1]
 | ||
| 
											5 years ago
										 |     return os.path.join(ROOT, last_route)
 | ||
|  | 
 | ||
|  |   # TODO: this should run faster than real time
 | ||
| 
											5 years ago
										 |   @parameterized.expand([(True, ), (False, )])
 | ||
|  |   def test_log_rotation(self, record_front):
 | ||
| 
											4 years ago
										 |     Params().put_bool("RecordFront", record_front)
 | ||
|  | 
 | ||
|  |     managed_processes['sensord'].start()
 | ||
|  |     managed_processes['loggerd'].start()
 | ||
| 
											4 years ago
										 |     managed_processes['encoderd'].start()
 | ||
| 
											4 years ago
										 | 
 | ||
|  |     time.sleep(1.0)
 | ||
|  |     managed_processes['camerad'].start()
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 |     num_segments = int(os.getenv("SEGMENTS", random.randint(10, 15)))
 | ||
| 
											5 years ago
										 | 
 | ||
|  |     # wait for loggerd to make the dir for first segment
 | ||
|  |     route_prefix_path = None
 | ||
| 
											5 years ago
										 |     with Timeout(int(SEGMENT_LENGTH*3)):
 | ||
| 
											5 years ago
										 |       while route_prefix_path is None:
 | ||
|  |         try:
 | ||
|  |           route_prefix_path = self._get_latest_segment_path().rsplit("--", 1)[0]
 | ||
|  |         except Exception:
 | ||
| 
											5 years ago
										 |           time.sleep(0.1)
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 |     def check_seg(i):
 | ||
| 
											5 years ago
										 |       # check each camera file size
 | ||
| 
											5 years ago
										 |       counts = []
 | ||
| 
											4 years ago
										 |       first_frames = []
 | ||
| 
											5 years ago
										 |       for camera, fps, size, encode_idx_name in CAMERAS:
 | ||
| 
											5 years ago
										 |         if not record_front and "dcamera" in camera:
 | ||
|  |           continue
 | ||
|  | 
 | ||
|  |         file_path = f"{route_prefix_path}--{i}/{camera}"
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											4 years ago
										 |         # check file exists
 | ||
|  |         self.assertTrue(os.path.exists(file_path), f"segment #{i}: '{file_path}' missing")
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 |         # TODO: this ffprobe call is really slow
 | ||
| 
											5 years ago
										 |         # check frame count
 | ||
| 
											4 years ago
										 |         cmd = f"ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 {file_path}"
 | ||
| 
											5 years ago
										 |         if TICI:
 | ||
|  |           cmd = "LD_LIBRARY_PATH=/usr/local/lib " + cmd
 | ||
|  | 
 | ||
| 
											5 years ago
										 |         expected_frames = fps * SEGMENT_LENGTH
 | ||
|  |         probe = subprocess.check_output(cmd, shell=True, encoding='utf8')
 | ||
|  |         frame_count = int(probe.split('\n')[0].strip())
 | ||
| 
											5 years ago
										 |         counts.append(frame_count)
 | ||
|  | 
 | ||
| 
											4 years ago
										 |         self.assertEqual(frame_count, expected_frames,
 | ||
|  |                          f"segment #{i}: {camera} failed frame count check: expected {expected_frames}, got {frame_count}")
 | ||
| 
											4 years ago
										 | 
 | ||
|  |         # sanity check file size
 | ||
|  |         file_size = os.path.getsize(file_path)
 | ||
| 
											4 years ago
										 |         self.assertTrue(math.isclose(file_size, size, rel_tol=FILE_SIZE_TOLERANCE),
 | ||
|  |                         f"{file_path} size {file_size} isn't close to target size {size}")
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 |         # Check encodeIdx
 | ||
|  |         if encode_idx_name is not None:
 | ||
| 
											4 years ago
										 |           rlog_path = f"{route_prefix_path}--{i}/rlog"
 | ||
| 
											4 years ago
										 |           msgs = [m for m in LogReader(rlog_path) if m.which() == encode_idx_name]
 | ||
|  |           encode_msgs = [getattr(m, encode_idx_name) for m in msgs]
 | ||
| 
											4 years ago
										 | 
 | ||
| 
											4 years ago
										 |           valid = [m.valid for m in msgs]
 | ||
|  |           segment_idxs = [m.segmentId for m in encode_msgs]
 | ||
|  |           encode_idxs = [m.encodeId for m in encode_msgs]
 | ||
|  |           frame_idxs = [m.frameId for m in encode_msgs]
 | ||
| 
											4 years ago
										 | 
 | ||
|  |           # Check frame count
 | ||
|  |           self.assertEqual(frame_count, len(segment_idxs))
 | ||
|  |           self.assertEqual(frame_count, len(encode_idxs))
 | ||
| 
											5 years ago
										 | 
 | ||
|  |           # Check for duplicates or skips
 | ||
| 
											4 years ago
										 |           self.assertEqual(0, segment_idxs[0])
 | ||
|  |           self.assertEqual(len(set(segment_idxs)), len(segment_idxs))
 | ||
|  | 
 | ||
| 
											4 years ago
										 |           self.assertTrue(all(valid))
 | ||
|  | 
 | ||
| 
											4 years ago
										 |           self.assertEqual(expected_frames * i, encode_idxs[0])
 | ||
|  |           first_frames.append(frame_idxs[0])
 | ||
| 
											4 years ago
										 |           self.assertEqual(len(set(encode_idxs)), len(encode_idxs))
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											4 years ago
										 |       self.assertEqual(1, len(set(first_frames)))
 | ||
|  | 
 | ||
| 
											5 years ago
										 |       if TICI:
 | ||
|  |         expected_frames = fps * SEGMENT_LENGTH
 | ||
|  |         self.assertEqual(min(counts), expected_frames)
 | ||
| 
											5 years ago
										 |       shutil.rmtree(f"{route_prefix_path}--{i}")
 | ||
|  | 
 | ||
| 
											4 years ago
										 |     try:
 | ||
| 
											4 years ago
										 |       for i in trange(num_segments):
 | ||
| 
											4 years ago
										 |         # poll for next segment
 | ||
|  |         with Timeout(int(SEGMENT_LENGTH*10), error_msg=f"timed out waiting for segment {i}"):
 | ||
| 
											4 years ago
										 |           while Path(f"{route_prefix_path}--{i+1}") not in Path(ROOT).iterdir():
 | ||
| 
											4 years ago
										 |             time.sleep(0.1)
 | ||
| 
											4 years ago
										 |         check_seg(i)
 | ||
| 
											4 years ago
										 |     finally:
 | ||
|  |       managed_processes['loggerd'].stop()
 | ||
| 
											4 years ago
										 |       managed_processes['encoderd'].stop()
 | ||
| 
											4 years ago
										 |       managed_processes['camerad'].stop()
 | ||
|  |       managed_processes['sensord'].stop()
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 | 
 | ||
| 
											5 years ago
										 | if __name__ == "__main__":
 | ||
|  |   unittest.main()
 |