| 
						
						
						
					 | 
					 | 
					@ -1,7 +1,10 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#!/usr/bin/env python3 | 
					 | 
					 | 
					 | 
					#!/usr/bin/env python3 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import argparse | 
					 | 
					 | 
					 | 
					import argparse | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					import concurrent.futures | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import os | 
					 | 
					 | 
					 | 
					import os | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					import sys | 
					 | 
					 | 
					 | 
					import sys | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					from collections import defaultdict | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					from tqdm import tqdm | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from typing import Any, Dict | 
					 | 
					 | 
					 | 
					from typing import Any, Dict | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from selfdrive.car.car_helpers import interface_names | 
					 | 
					 | 
					 | 
					from selfdrive.car.car_helpers import interface_names | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -11,7 +14,6 @@ from selfdrive.test.process_replay.process_replay import CONFIGS, PROC_REPLAY_DI | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from selfdrive.version import get_commit | 
					 | 
					 | 
					 | 
					from selfdrive.version import get_commit | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					from tools.lib.logreader import LogReader | 
					 | 
					 | 
					 | 
					from tools.lib.logreader import LogReader | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					original_segments = [ | 
					 | 
					 | 
					 | 
					original_segments = [ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ("BODY", "bd6a637565e91581|2022-04-04--22-05-08--0"),        # COMMA.BODY | 
					 | 
					 | 
					 | 
					  ("BODY", "bd6a637565e91581|2022-04-04--22-05-08--0"),        # COMMA.BODY | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"),     # HYUNDAI.SONATA | 
					 | 
					 | 
					 | 
					  ("HYUNDAI", "02c45f73a2e5c6e9|2021-01-01--19-08-22--1"),     # HYUNDAI.SONATA | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -54,6 +56,28 @@ BASE_URL = "https://commadataci.blob.core.windows.net/openpilotci/" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit") | 
					 | 
					 | 
					 | 
					REF_COMMIT_FN = os.path.join(PROC_REPLAY_DIR, "ref_commit") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def run_test_process(data): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  segment, cfg, args, cur_log_fn, lr, ref_commit = data | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  res = None | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  if not args.upload_only: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    ref_log_fn = os.path.join(PROC_REPLAY_DIR, f"{segment}_{cfg.proc_name}_{ref_commit}.bz2") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    res, log_msgs = test_process(cfg, lr, ref_log_fn, args.ignore_fields, args.ignore_msgs) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    # save logs so we can upload when updating refs | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    save_log(cur_log_fn, log_msgs) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  if args.update_refs or args.upload_only: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    print(f'Uploading: {os.path.basename(cur_log_fn)}') | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    assert os.path.exists(cur_log_fn), f"Cannot find log to upload: {cur_log_fn}" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    upload_file(cur_log_fn, os.path.basename(cur_log_fn)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    os.remove(cur_log_fn) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  return (segment, cfg.proc_name, res) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					def get_logreader(segment): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  r, n = segment.rsplit("--", 1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  lr = LogReader(get_url(r, n)) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  return (segment, lr) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					def test_process(cfg, lr, ref_log_fn, ignore_fields=None, ignore_msgs=None): | 
					 | 
					 | 
					 | 
					def test_process(cfg, lr, ref_log_fn, ignore_fields=None, ignore_msgs=None): | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if ignore_fields is None: | 
					 | 
					 | 
					 | 
					  if ignore_fields is None: | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ignore_fields = [] | 
					 | 
					 | 
					 | 
					    ignore_fields = [] | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -127,6 +151,7 @@ if __name__ == "__main__": | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                      help="Updates reference logs using current commit") | 
					 | 
					 | 
					 | 
					                      help="Updates reference logs using current commit") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  parser.add_argument("--upload-only", action="store_true", | 
					 | 
					 | 
					 | 
					  parser.add_argument("--upload-only", action="store_true", | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                      help="Skips testing processes and uploads logs from previous test run") | 
					 | 
					 | 
					 | 
					                      help="Skips testing processes and uploads logs from previous test run") | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  parser.add_argument("-j", "--jobs", type=int, default=1) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  args = parser.parse_args() | 
					 | 
					 | 
					 | 
					  args = parser.parse_args() | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  full_test = all(len(x) == 0 for x in (args.whitelist_procs, args.whitelist_cars, args.blacklist_procs, args.blacklist_cars, args.ignore_fields, args.ignore_msgs)) | 
					 | 
					 | 
					 | 
					  full_test = all(len(x) == 0 for x in (args.whitelist_procs, args.whitelist_cars, args.blacklist_procs, args.blacklist_cars, args.ignore_fields, args.ignore_msgs)) | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -154,37 +179,31 @@ if __name__ == "__main__": | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    untested = (set(interface_names) - set(excluded_interfaces)) - tested_cars | 
					 | 
					 | 
					 | 
					    untested = (set(interface_names) - set(excluded_interfaces)) - tested_cars | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    assert len(untested) == 0, f"Cars missing routes: {str(untested)}" | 
					 | 
					 | 
					 | 
					    assert len(untested) == 0, f"Cars missing routes: {str(untested)}" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  results: Any = {} | 
					 | 
					 | 
					 | 
					  with concurrent.futures.ProcessPoolExecutor(max_workers=args.jobs) as pool: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for car_brand, segment in segments: | 
					 | 
					 | 
					 | 
					    if not args.upload_only: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (len(args.whitelist_cars) and car_brand.upper() not in args.whitelist_cars) or \ | 
					 | 
					 | 
					 | 
					      lreaders: Any = {} | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					       (not len(args.whitelist_cars) and car_brand.upper() in args.blacklist_cars): | 
					 | 
					 | 
					 | 
					      p1 = pool.map(get_logreader, [seg for car, seg in segments]) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      continue | 
					 | 
					 | 
					 | 
					      for (segment, lr) in tqdm(p1, desc="Getting Logs", total=len(segments)): | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					        lreaders[segment] = lr | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    print(f"***** testing route segment {segment} *****\n") | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					    pool_args: Any = [] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    results[segment] = {} | 
					 | 
					 | 
					 | 
					    for car_brand, segment in segments: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					      if (len(args.whitelist_cars) and car_brand.upper() not in args.whitelist_cars) or \ | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    r, n = segment.rsplit("--", 1) | 
					 | 
					 | 
					 | 
					         (not len(args.whitelist_cars) and car_brand.upper() in args.blacklist_cars): | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    lr = LogReader(get_url(r, n)) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for cfg in CONFIGS: | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if (len(args.whitelist_procs) and cfg.proc_name not in args.whitelist_procs) or \ | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					         (not len(args.whitelist_procs) and cfg.proc_name in args.blacklist_procs): | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        continue | 
					 | 
					 | 
					 | 
					        continue | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					      for cfg in CONFIGS: | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{cur_commit}.bz2") | 
					 | 
					 | 
					 | 
					        if (len(args.whitelist_procs) and cfg.proc_name not in args.whitelist_procs) or \ | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if not args.upload_only: | 
					 | 
					 | 
					 | 
					           (not len(args.whitelist_procs) and cfg.proc_name in args.blacklist_procs): | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        ref_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{ref_commit}.bz2") | 
					 | 
					 | 
					 | 
					          continue | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        results[segment][cfg.proc_name], log_msgs = test_process(cfg, lr, ref_log_fn, args.ignore_fields, args.ignore_msgs) | 
					 | 
					 | 
					 | 
					        cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{cur_commit}.bz2") | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					        lr = None if args.upload_only else lreaders[segment] | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        # save logs so we can upload when updating refs | 
					 | 
					 | 
					 | 
					        pool_args.append((segment, cfg, args, cur_log_fn, lr, ref_commit)) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        save_log(cur_log_fn, log_msgs) | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					    results: Any = defaultdict(dict) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      if upload: | 
					 | 
					 | 
					 | 
					    p2 = pool.map(run_test_process, pool_args) | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        print(f'Uploading: {os.path.basename(cur_log_fn)}') | 
					 | 
					 | 
					 | 
					    for (segment, proc, result) in tqdm(p2, desc="Running Tests", total=len(pool_args)): | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        assert os.path.exists(cur_log_fn), f"Cannot find log to upload: {cur_log_fn}" | 
					 | 
					 | 
					 | 
					      if isinstance(result, list): | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        upload_file(cur_log_fn, os.path.basename(cur_log_fn)) | 
					 | 
					 | 
					 | 
					        results[segment][proc] = result | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        os.remove(cur_log_fn) | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  diff1, diff2, failed = format_diff(results, ref_commit) | 
					 | 
					 | 
					 | 
					  diff1, diff2, failed = format_diff(results, ref_commit) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if not args.upload_only: | 
					 | 
					 | 
					 | 
					  if not args.upload_only: | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |