1*da0073e9SAndroid Build Coastguard Workerfrom __future__ import annotations 2*da0073e9SAndroid Build Coastguard Worker 3*da0073e9SAndroid Build Coastguard Workerimport json 4*da0073e9SAndroid Build Coastguard Workerimport os 5*da0073e9SAndroid Build Coastguard Workerimport re 6*da0073e9SAndroid Build Coastguard Workerimport time 7*da0073e9SAndroid Build Coastguard Workerfrom collections import defaultdict 8*da0073e9SAndroid Build Coastguard Workerfrom functools import lru_cache 9*da0073e9SAndroid Build Coastguard Workerfrom pathlib import Path 10*da0073e9SAndroid Build Coastguard Workerfrom tempfile import TemporaryDirectory 11*da0073e9SAndroid Build Coastguard Workerfrom typing import Any, cast 12*da0073e9SAndroid Build Coastguard Worker 13*da0073e9SAndroid Build Coastguard Workerimport requests 14*da0073e9SAndroid Build Coastguard Worker 15*da0073e9SAndroid Build Coastguard Workerfrom tools.stats.upload_stats_lib import ( 16*da0073e9SAndroid Build Coastguard Worker _get_request_headers, 17*da0073e9SAndroid Build Coastguard Worker download_s3_artifacts, 18*da0073e9SAndroid Build Coastguard Worker get_job_id, 19*da0073e9SAndroid Build Coastguard Worker unzip, 20*da0073e9SAndroid Build Coastguard Worker upload_workflow_stats_to_s3, 21*da0073e9SAndroid Build Coastguard Worker) 22*da0073e9SAndroid Build Coastguard Worker 23*da0073e9SAndroid Build Coastguard Worker 24*da0073e9SAndroid Build Coastguard WorkerREGEX_JOB_INFO = r"(.*) \/ .*test \(([^,]*), .*\)" 25*da0073e9SAndroid Build Coastguard Worker 26*da0073e9SAndroid Build Coastguard Worker 27*da0073e9SAndroid Build Coastguard Worker@lru_cache(maxsize=1000) 28*da0073e9SAndroid Build Coastguard Workerdef get_job_name(job_id: int) -> str: 29*da0073e9SAndroid Build Coastguard Worker try: 30*da0073e9SAndroid Build Coastguard Worker return cast( 31*da0073e9SAndroid Build Coastguard Worker str, 32*da0073e9SAndroid Build Coastguard Worker requests.get( 33*da0073e9SAndroid Build Coastguard Worker f"https://api.github.com/repos/pytorch/pytorch/actions/jobs/{job_id}", 34*da0073e9SAndroid Build Coastguard Worker headers=_get_request_headers(), 35*da0073e9SAndroid Build Coastguard Worker ).json()["name"], 36*da0073e9SAndroid Build Coastguard Worker ) 37*da0073e9SAndroid Build Coastguard Worker except Exception as e: 38*da0073e9SAndroid Build Coastguard Worker print(f"Failed to get job name for job id {job_id}: {e}") 39*da0073e9SAndroid Build Coastguard Worker return "NoJobName" 40*da0073e9SAndroid Build Coastguard Worker 41*da0073e9SAndroid Build Coastguard Worker 42*da0073e9SAndroid Build Coastguard Worker@lru_cache(maxsize=1000) 43*da0073e9SAndroid Build Coastguard Workerdef get_build_name(job_name: str) -> str: 44*da0073e9SAndroid Build Coastguard Worker try: 45*da0073e9SAndroid Build Coastguard Worker return re.match(REGEX_JOB_INFO, job_name).group(1) # type: ignore[union-attr] 46*da0073e9SAndroid Build Coastguard Worker except AttributeError: 47*da0073e9SAndroid Build Coastguard Worker print(f"Failed to match job name: {job_name}") 48*da0073e9SAndroid Build Coastguard Worker return "NoBuildEnv" 49*da0073e9SAndroid Build Coastguard Worker 50*da0073e9SAndroid Build Coastguard Worker 51*da0073e9SAndroid Build Coastguard Worker@lru_cache(maxsize=1000) 52*da0073e9SAndroid Build Coastguard Workerdef get_test_config(job_name: str) -> str: 53*da0073e9SAndroid Build Coastguard Worker try: 54*da0073e9SAndroid Build Coastguard Worker return re.match(REGEX_JOB_INFO, job_name).group(2) # type: ignore[union-attr] 55*da0073e9SAndroid Build Coastguard Worker except AttributeError: 56*da0073e9SAndroid Build Coastguard Worker print(f"Failed to match job name: {job_name}") 57*da0073e9SAndroid Build Coastguard Worker return "NoTestConfig" 58*da0073e9SAndroid Build Coastguard Worker 59*da0073e9SAndroid Build Coastguard Worker 60*da0073e9SAndroid Build Coastguard Workerdef get_td_exclusions( 61*da0073e9SAndroid Build Coastguard Worker workflow_run_id: int, workflow_run_attempt: int 62*da0073e9SAndroid Build Coastguard Worker) -> dict[str, Any]: 63*da0073e9SAndroid Build Coastguard Worker with TemporaryDirectory() as temp_dir: 64*da0073e9SAndroid Build Coastguard Worker print("Using temporary directory:", temp_dir) 65*da0073e9SAndroid Build Coastguard Worker os.chdir(temp_dir) 66*da0073e9SAndroid Build Coastguard Worker 67*da0073e9SAndroid Build Coastguard Worker # Download and extract all the reports (both GHA and S3) 68*da0073e9SAndroid Build Coastguard Worker s3_paths = download_s3_artifacts( 69*da0073e9SAndroid Build Coastguard Worker "test-jsons", workflow_run_id, workflow_run_attempt 70*da0073e9SAndroid Build Coastguard Worker ) 71*da0073e9SAndroid Build Coastguard Worker for path in s3_paths: 72*da0073e9SAndroid Build Coastguard Worker unzip(path) 73*da0073e9SAndroid Build Coastguard Worker 74*da0073e9SAndroid Build Coastguard Worker grouped_tests: dict[str, Any] = defaultdict(lambda: defaultdict(set)) 75*da0073e9SAndroid Build Coastguard Worker for td_exclusions in Path(".").glob("**/td_exclusions*.json"): 76*da0073e9SAndroid Build Coastguard Worker with open(td_exclusions) as f: 77*da0073e9SAndroid Build Coastguard Worker exclusions = json.load(f) 78*da0073e9SAndroid Build Coastguard Worker for exclusion in exclusions["excluded"]: 79*da0073e9SAndroid Build Coastguard Worker job_id = get_job_id(td_exclusions) 80*da0073e9SAndroid Build Coastguard Worker job_name = get_job_name(job_id) 81*da0073e9SAndroid Build Coastguard Worker build_name = get_build_name(job_name) 82*da0073e9SAndroid Build Coastguard Worker test_config = get_test_config(job_name) 83*da0073e9SAndroid Build Coastguard Worker grouped_tests[build_name][test_config].add(exclusion["test_file"]) 84*da0073e9SAndroid Build Coastguard Worker 85*da0073e9SAndroid Build Coastguard Worker for build_name, build in grouped_tests.items(): 86*da0073e9SAndroid Build Coastguard Worker for test_config, test_files in build.items(): 87*da0073e9SAndroid Build Coastguard Worker grouped_tests[build_name][test_config] = sorted(test_files) 88*da0073e9SAndroid Build Coastguard Worker return grouped_tests 89*da0073e9SAndroid Build Coastguard Worker 90*da0073e9SAndroid Build Coastguard Worker 91*da0073e9SAndroid Build Coastguard Workerdef group_test_cases(test_cases: list[dict[str, Any]]) -> dict[str, Any]: 92*da0073e9SAndroid Build Coastguard Worker start = time.time() 93*da0073e9SAndroid Build Coastguard Worker grouped_tests: dict[str, Any] = defaultdict( 94*da0073e9SAndroid Build Coastguard Worker lambda: defaultdict( 95*da0073e9SAndroid Build Coastguard Worker lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list))) 96*da0073e9SAndroid Build Coastguard Worker ) 97*da0073e9SAndroid Build Coastguard Worker ) 98*da0073e9SAndroid Build Coastguard Worker for test_case in test_cases: 99*da0073e9SAndroid Build Coastguard Worker job_name = get_job_name(test_case["job_id"]) 100*da0073e9SAndroid Build Coastguard Worker build_name = get_build_name(job_name) 101*da0073e9SAndroid Build Coastguard Worker if "bazel" in build_name: 102*da0073e9SAndroid Build Coastguard Worker continue 103*da0073e9SAndroid Build Coastguard Worker test_config = get_test_config(job_name) 104*da0073e9SAndroid Build Coastguard Worker class_name = test_case.pop("classname", "NoClass") 105*da0073e9SAndroid Build Coastguard Worker name = test_case.pop("name", "NoName") 106*da0073e9SAndroid Build Coastguard Worker invoking_file = test_case.pop("invoking_file", "NoFile") 107*da0073e9SAndroid Build Coastguard Worker invoking_file = invoking_file.replace(".", "/") 108*da0073e9SAndroid Build Coastguard Worker test_case.pop("workflow_id") 109*da0073e9SAndroid Build Coastguard Worker test_case.pop("workflow_run_attempt") 110*da0073e9SAndroid Build Coastguard Worker grouped_tests[build_name][test_config][invoking_file][class_name][name].append( 111*da0073e9SAndroid Build Coastguard Worker test_case 112*da0073e9SAndroid Build Coastguard Worker ) 113*da0073e9SAndroid Build Coastguard Worker 114*da0073e9SAndroid Build Coastguard Worker print(f"Time taken to group tests: {time.time() - start}") 115*da0073e9SAndroid Build Coastguard Worker return grouped_tests 116*da0073e9SAndroid Build Coastguard Worker 117*da0073e9SAndroid Build Coastguard Worker 118*da0073e9SAndroid Build Coastguard Workerdef get_reruns(grouped_tests: dict[str, Any]) -> dict[str, Any]: 119*da0073e9SAndroid Build Coastguard Worker reruns: dict[str, Any] = defaultdict( 120*da0073e9SAndroid Build Coastguard Worker lambda: defaultdict( 121*da0073e9SAndroid Build Coastguard Worker lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(list))) 122*da0073e9SAndroid Build Coastguard Worker ) 123*da0073e9SAndroid Build Coastguard Worker ) 124*da0073e9SAndroid Build Coastguard Worker for build_name, build in grouped_tests.items(): 125*da0073e9SAndroid Build Coastguard Worker for test_config, test_config_data in build.items(): 126*da0073e9SAndroid Build Coastguard Worker for invoking_file, invoking_file_data in test_config_data.items(): 127*da0073e9SAndroid Build Coastguard Worker for class_name, class_data in invoking_file_data.items(): 128*da0073e9SAndroid Build Coastguard Worker for test_name, test_data in class_data.items(): 129*da0073e9SAndroid Build Coastguard Worker if len(test_data) > 1: 130*da0073e9SAndroid Build Coastguard Worker if invoking_file in ( 131*da0073e9SAndroid Build Coastguard Worker "distributed/test_distributed_spawn", 132*da0073e9SAndroid Build Coastguard Worker "onnx/test_fx_to_onnx_with_onnxruntime", 133*da0073e9SAndroid Build Coastguard Worker "distributed/algorithms/quantization/test_quantization", 134*da0073e9SAndroid Build Coastguard Worker ): 135*da0073e9SAndroid Build Coastguard Worker continue 136*da0073e9SAndroid Build Coastguard Worker reruns[build_name][test_config][invoking_file][class_name][ 137*da0073e9SAndroid Build Coastguard Worker test_name 138*da0073e9SAndroid Build Coastguard Worker ] = test_data 139*da0073e9SAndroid Build Coastguard Worker return reruns 140*da0073e9SAndroid Build Coastguard Worker 141*da0073e9SAndroid Build Coastguard Worker 142*da0073e9SAndroid Build Coastguard Workerdef get_invoking_file_summary(grouped_tests: dict[str, Any]) -> dict[str, Any]: 143*da0073e9SAndroid Build Coastguard Worker invoking_file_summary: dict[str, Any] = defaultdict( 144*da0073e9SAndroid Build Coastguard Worker lambda: defaultdict(lambda: defaultdict(lambda: {"count": 0, "time": 0.0})) 145*da0073e9SAndroid Build Coastguard Worker ) 146*da0073e9SAndroid Build Coastguard Worker for build_name, build in grouped_tests.items(): 147*da0073e9SAndroid Build Coastguard Worker for test_config, test_config_data in build.items(): 148*da0073e9SAndroid Build Coastguard Worker for invoking_file, invoking_file_data in test_config_data.items(): 149*da0073e9SAndroid Build Coastguard Worker for class_data in invoking_file_data.values(): 150*da0073e9SAndroid Build Coastguard Worker for test_data in class_data.values(): 151*da0073e9SAndroid Build Coastguard Worker invoking_file_summary[build_name][test_config][invoking_file][ 152*da0073e9SAndroid Build Coastguard Worker "count" 153*da0073e9SAndroid Build Coastguard Worker ] += 1 154*da0073e9SAndroid Build Coastguard Worker for i in test_data: 155*da0073e9SAndroid Build Coastguard Worker invoking_file_summary[build_name][test_config][ 156*da0073e9SAndroid Build Coastguard Worker invoking_file 157*da0073e9SAndroid Build Coastguard Worker ]["time"] += i["time"] 158*da0073e9SAndroid Build Coastguard Worker 159*da0073e9SAndroid Build Coastguard Worker return invoking_file_summary 160*da0073e9SAndroid Build Coastguard Worker 161*da0073e9SAndroid Build Coastguard Worker 162*da0073e9SAndroid Build Coastguard Workerdef upload_additional_info( 163*da0073e9SAndroid Build Coastguard Worker workflow_run_id: int, workflow_run_attempt: int, test_cases: list[dict[str, Any]] 164*da0073e9SAndroid Build Coastguard Worker) -> None: 165*da0073e9SAndroid Build Coastguard Worker grouped_tests = group_test_cases(test_cases) 166*da0073e9SAndroid Build Coastguard Worker reruns = get_reruns(grouped_tests) 167*da0073e9SAndroid Build Coastguard Worker exclusions = get_td_exclusions(workflow_run_id, workflow_run_attempt) 168*da0073e9SAndroid Build Coastguard Worker invoking_file_summary = get_invoking_file_summary(grouped_tests) 169*da0073e9SAndroid Build Coastguard Worker 170*da0073e9SAndroid Build Coastguard Worker upload_workflow_stats_to_s3( 171*da0073e9SAndroid Build Coastguard Worker workflow_run_id, 172*da0073e9SAndroid Build Coastguard Worker workflow_run_attempt, 173*da0073e9SAndroid Build Coastguard Worker "additional_info/reruns", 174*da0073e9SAndroid Build Coastguard Worker [reruns], 175*da0073e9SAndroid Build Coastguard Worker ) 176*da0073e9SAndroid Build Coastguard Worker upload_workflow_stats_to_s3( 177*da0073e9SAndroid Build Coastguard Worker workflow_run_id, 178*da0073e9SAndroid Build Coastguard Worker workflow_run_attempt, 179*da0073e9SAndroid Build Coastguard Worker "additional_info/td_exclusions", 180*da0073e9SAndroid Build Coastguard Worker [exclusions], 181*da0073e9SAndroid Build Coastguard Worker ) 182*da0073e9SAndroid Build Coastguard Worker upload_workflow_stats_to_s3( 183*da0073e9SAndroid Build Coastguard Worker workflow_run_id, 184*da0073e9SAndroid Build Coastguard Worker workflow_run_attempt, 185*da0073e9SAndroid Build Coastguard Worker "additional_info/invoking_file_summary", 186*da0073e9SAndroid Build Coastguard Worker [invoking_file_summary], 187*da0073e9SAndroid Build Coastguard Worker ) 188