hdk/common/verif/scripts/log_regression_results.py (100 lines of code) (raw):

#!/usr/bin/env python3 # ============================================================================= # Amazon FPGA Hardware Development Kit # # Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Amazon Software License (the "License"). You may not use # this file except in compliance with the License. A copy of the License is # located at # # http://aws.amazon.com/asl/ # # or in the "license" file accompanying this file. This file is distributed on # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or # implied. See the License for the specific language governing permissions and # limitations under the License. # ============================================================================= import os import sys from glob import glob from argparse import ArgumentParser, Namespace from typing import Dict, List parser = ArgumentParser(prog="Regression Log Generator", description="Generate a log for all tests ran for the simulator specified") parser.add_argument('--simulator', dest='simulator', required=True) parser.add_argument('--cl_dir', dest='cl_dir', required=True) parser.add_argument('--regression_results_log', dest='regression_results_log', required=True) parser.add_argument('--sim_dir_extension', dest='sim_dir_extension', required=True) if __name__ == '__main__': args = parser.parse_args() is_python3 = sys.version_info >= (3, 0) and sys.version_info < (4, 0) is_greater_than_3_8 = sys.version_info >= (3, 8) if not (is_python3 and is_greater_than_3_8): raise Exception("The Python script experienced a failure. Please review and make sure Python 3.8+ is installed\n") class LogGenerator: def __init__(self, args: Namespace) -> None: self.simulator: str = args.simulator self.cl_dir: str = args.cl_dir self.regression_results_log: str = args.regression_results_log self.sim_dir_extension: str = args.sim_dir_extension self.makefile_testlist_path: str = f"{self.cl_dir}/verif/scripts/Makefile.tests" self.test_results: Dict[str,str] = {} def generate_regression_log(self) -> None: self.update_test_results() self.warn_of_test_count_mismatch() with open(self.regression_results_log, 'w') as f: table: str = print_table(self.test_results) f.write(table) def update_test_results(self) -> str: all_logs: List[str] = glob(f"{self.cl_dir}/verif/sim/{self.simulator}/*{self.sim_dir_extension}/*.log") test_logs = [path for path in all_logs if self.is_test_log(path) and "backup" not in path] for test_log in test_logs: test_name: str = self.get_test_name_as_test_dirname(test_log) self.test_results[test_name] = self.get_test_result(test_log) @staticmethod def is_test_log(log_path: str) -> bool: test_log_name: str = os.path.basename(log_path).replace('.log', '') test_name: str = LogGenerator.get_test_name_as_test_dirname(log_path) parent_dir: str = os.path.basename(os.path.dirname(log_path)) return test_name in parent_dir and test_log_name in parent_dir and test_log_name in test_name @staticmethod def get_test_name_as_test_dirname(log_path: str) -> str: path_to_parent: str = os.path.dirname(log_path) return os.path.basename(path_to_parent) def get_test_result(self, test_log: str) -> str: result_signature: str = "*** TEST " with open(test_log) as f: for line in f: line: str = line.strip() if result_signature in line: return line.split(result_signature)[1].strip().split()[0].strip() return "NONE (possible compile error)" def warn_of_test_count_mismatch(self) -> None: expected_test_list: List[str] = self.get_expected_test_list(self.makefile_testlist_path) num_test_results: int = len(self.test_results.values()) if num_test_results != len(expected_test_list): found_tests: List[str] = list(self.test_results.keys()) print(f""" WARNING: Found {num_test_results} test results in 'sim' directory, but found {len(expected_test_list)} in {self.makefile_testlist_path}. EXPECTED: {expected_test_list} FOUND in 'sim' dir: {found_tests} MISSING: {list(set(expected_test_list) - set(found_tests))} ADDITIONAL: {list(set(found_tests) - set(expected_test_list))} """) @staticmethod def get_expected_test_list(makefile_testlist_path: str) -> List[str]: makefile_test_signature: str = "TEST=" expected_tests: List[str] = [] with open(makefile_testlist_path) as f: for line in f: line = line.strip() if makefile_test_signature in line and not line.startswith('#'): test_name: str = line.split(makefile_test_signature)[1].strip() expected_tests.append(test_name) return expected_tests def print_table(data: Dict[str, str]) -> str: max_key_length: int = max([len(key) for key in data.keys()]) max_value_length: int = max([len(value) for value in data.values()]) test_name_header: str = f"{'TEST NAME':<{max_key_length}}" test_result_header: str = f"{'TEST RESULT':<{max_value_length}}" # This is in case the column values are shorter than the header text max_key_length = len(test_name_header) max_value_length = len(test_result_header) rows: List[str] = get_rows(data, max_key_length, max_value_length) border: str = get_border(max_key_length, max_value_length) content: str = "".join([f"{row}\n" for row in rows]) table: str = f""" {border} {f"| {test_name_header} | {test_result_header} |"} {border} {content.strip()} {border}""" print(table) return table def get_border(max_key_length: int, max_value_length: int) -> str: SPACE_PADDING = 2 border_sections = ['-' * (val + SPACE_PADDING) for val in [max_key_length, max_value_length]] return f"|{'|'.join(border_sections)}|" def get_rows(data: Dict[str, str], max_key_length: int, max_value_length: int) -> List[str]: rows: List[str] = [] for key, value in data.items(): rows.append(f"| {str(key):<{max_key_length}} " + '|' + f" {str(value):<{max_value_length}} |") return rows if __name__ == '__main__': try: generator = LogGenerator(args) generator.generate_regression_log() except: raise Exception("The Python script experienced a failure. Please review and make sure Python 3.8+ is installed\n")