Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 1 | """Template test runner class for running TOSA tests.""" |
Jeremy Johnson | f0348ea | 2023-09-27 16:10:59 +0100 | [diff] [blame] | 2 | # Copyright (c) 2020-2023, ARM Limited. |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 3 | # SPDX-License-Identifier: Apache-2.0 |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 4 | import json |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 5 | from enum import IntEnum |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 6 | |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 7 | import conformance.model_files as cmf |
| 8 | import schemavalidation.schemavalidation as sch |
| 9 | from checker.color_print import LogColors |
| 10 | from checker.color_print import print_color |
| 11 | from checker.color_print import set_print_in_color |
| 12 | from checker.tosa_result_checker import set_print_result |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 13 | from checker.tosa_result_checker import test_check |
| 14 | from json2fbbin import json2fbbin |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 15 | from runner.tosa_test_presets import TOSA_REFCOMPLIANCE_RUNNER |
| 16 | |
| 17 | |
| 18 | def isComplianceModeDotProduct(testDesc): |
| 19 | """Checks the test descriptor for DOT_PRODUCT compliance mode.""" |
| 20 | if ( |
| 21 | "meta" in testDesc |
| 22 | and "compliance" in testDesc["meta"] |
| 23 | and "tensors" in testDesc["meta"]["compliance"] |
| 24 | ): |
| 25 | for _, t in testDesc["meta"]["compliance"]["tensors"].items(): |
| 26 | if "mode" in t and t["mode"] == "DOT_PRODUCT": |
| 27 | return True |
| 28 | return False |
| 29 | |
| 30 | |
| 31 | def getRunnerResultFilePath(resultFilePath, sutModule): |
| 32 | """Return the result file path with the runner specific naming.""" |
| 33 | return resultFilePath.with_suffix(f".{sutModule}{resultFilePath.suffix}") |
| 34 | |
| 35 | |
| 36 | def getBoundsResultFilePath(resultFilePath, sutModule=None): |
| 37 | """Return the bounds result file with/without runner specific naming.""" |
| 38 | boundsFilePath = resultFilePath.parent / f"bounds_{resultFilePath.name}" |
| 39 | if sutModule is not None: |
| 40 | boundsFilePath = boundsFilePath.with_suffix( |
| 41 | f".{sutModule}{boundsFilePath.suffix}" |
| 42 | ) |
| 43 | return boundsFilePath |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 44 | |
Kevin Cheng | 550ccc5 | 2021-03-03 11:21:43 -0800 | [diff] [blame] | 45 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 46 | class TosaTestInvalid(Exception): |
| 47 | """Exception raised for errors loading test description. |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 48 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 49 | Attributes: |
| 50 | path - full path to missing test description file |
| 51 | exception = underlying exception |
| 52 | """ |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 53 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 54 | def __init__(self, path, exception): |
| 55 | """Initialize test not found error.""" |
| 56 | self.path = path |
| 57 | self.exception = exception |
| 58 | self.message = "Invalid test, could not read test description {}: {}".format( |
| 59 | self.path, str(self.exception) |
| 60 | ) |
| 61 | super().__init__(self.message) |
Kevin Cheng | 550ccc5 | 2021-03-03 11:21:43 -0800 | [diff] [blame] | 62 | |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 63 | |
| 64 | class TosaTestRunner: |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 65 | """TOSA Test Runner template class for systems under test.""" |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 66 | |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 67 | def __init__(self, args, runnerArgs, testDirPath): |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 68 | """Initialize and load JSON meta data file.""" |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 69 | self.args = args |
| 70 | self.runnerArgs = runnerArgs |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 71 | self.testDir = str(testDirPath) |
| 72 | self.testDirPath = testDirPath |
| 73 | self.testName = self.testDirPath.name |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 74 | self.verify_lib_path = cmf.find_tosa_file( |
| 75 | cmf.TosaFileType.VERIFY_LIBRARY, args.ref_model_path |
| 76 | ) |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 77 | |
Jeremy Johnson | 015c355 | 2022-02-23 12:15:03 +0000 | [diff] [blame] | 78 | set_print_in_color(not args.no_color) |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 79 | # Stop the result checker printing anything - we will do it |
| 80 | set_print_result(False) |
Jeremy Johnson | 015c355 | 2022-02-23 12:15:03 +0000 | [diff] [blame] | 81 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 82 | # Check if we want to run binary and if its already converted |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 83 | descFilePath = testDirPath / "desc.json" |
| 84 | descBinFilePath = testDirPath / "desc_binary.json" |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 85 | if args.binary: |
| 86 | if descBinFilePath.is_file(): |
| 87 | descFilePath = descBinFilePath |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 88 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 89 | try: |
| 90 | # Load the json test file |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 91 | with descFilePath.open("r") as fd: |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 92 | self.testDesc = json.load(fd) |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 93 | # Validate the json with the schema |
| 94 | sch.TestDescSchemaValidator().validate_config(self.testDesc) |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 95 | except Exception as e: |
| 96 | raise TosaTestInvalid(str(descFilePath), e) |
| 97 | |
| 98 | # Convert to binary if needed |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 99 | tosaFilePath = testDirPath / self.testDesc["tosa_file"] |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 100 | if args.binary and tosaFilePath.suffix == ".json": |
| 101 | # Convert tosa JSON to binary |
| 102 | json2fbbin.json_to_fbbin( |
Jeremy Johnson | f0348ea | 2023-09-27 16:10:59 +0100 | [diff] [blame] | 103 | args.flatc_path, |
| 104 | args.schema_path, |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 105 | tosaFilePath, |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 106 | testDirPath, |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 107 | ) |
| 108 | # Write new desc_binary file |
| 109 | self.testDesc["tosa_file"] = tosaFilePath.stem + ".tosa" |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 110 | with descBinFilePath.open("w") as fd: |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 111 | json.dump(self.testDesc, fd, indent=2) |
| 112 | descFilePath = descBinFilePath |
| 113 | |
| 114 | # Set location of desc.json (or desc_binary.json) file in use |
| 115 | self.descFile = str(descFilePath) |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 116 | self.descFilePath = descFilePath |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 117 | |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 118 | # Check for compliance mode - need to run refmodel to get results |
| 119 | if "meta" in self.testDesc and "compliance" in self.testDesc["meta"]: |
| 120 | self.complianceMode = True |
| 121 | if "expected_result" in self.testDesc: |
| 122 | if self.args.verbose: |
| 123 | print("Warning: fixing conflicting compliance mode in test.desc") |
| 124 | self.testDesc.pop("expected_result") |
| 125 | else: |
| 126 | self.complianceMode = False |
| 127 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 128 | def skipTest(self): |
Jeremy Johnson | 8858862 | 2022-07-12 16:42:29 +0100 | [diff] [blame] | 129 | """Check if the test is skipped due to test type or profile selection.""" |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 130 | expectedFailure = self.testDesc["expected_failure"] |
| 131 | if self.args.test_type == "negative" and not expectedFailure: |
Jeremy Johnson | 8858862 | 2022-07-12 16:42:29 +0100 | [diff] [blame] | 132 | return True, "non-negative type" |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 133 | elif self.args.test_type == "positive" and expectedFailure: |
Jeremy Johnson | 8858862 | 2022-07-12 16:42:29 +0100 | [diff] [blame] | 134 | return True, "non-positive type" |
| 135 | if self.args.profile: |
| 136 | profile = self.testDesc["profile"] if "profile" in self.testDesc else [] |
| 137 | if self.args.profile not in profile: |
| 138 | return True, "non-{} profile".format(self.args.profile) |
| 139 | return False, "" |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 140 | |
| 141 | def runTestGraph(self): |
| 142 | """Override with function that calls system under test.""" |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 143 | pass |
| 144 | |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 145 | def testResult(self, tosaGraphResult, graphMessage=None): |
| 146 | """Work out test result based on graph result and output files.""" |
| 147 | expectedFailure = self.testDesc["expected_failure"] |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 148 | print_check_result = False |
| 149 | |
| 150 | sutModule = self.__module__ |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 151 | |
| 152 | if tosaGraphResult == TosaTestRunner.TosaGraphResult.TOSA_VALID: |
| 153 | if expectedFailure: |
| 154 | result = TosaTestRunner.Result.UNEXPECTED_PASS |
| 155 | resultMessage = "Expected failure test incorrectly passed" |
| 156 | else: |
| 157 | # Work through all the results produced by the testing, assuming success |
| 158 | # but overriding this with any failures found |
| 159 | result = TosaTestRunner.Result.EXPECTED_PASS |
| 160 | messages = [] |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 161 | |
| 162 | # Go through each output result checking it |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 163 | for resultNum, resultFileName in enumerate(self.testDesc["ofm_file"]): |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 164 | resultFilePath = self.testDirPath / resultFileName |
| 165 | |
| 166 | # Work out the file to check against (if any) |
| 167 | if self.complianceMode and sutModule != TOSA_REFCOMPLIANCE_RUNNER: |
| 168 | conformanceFilePath = getRunnerResultFilePath( |
| 169 | resultFilePath, TOSA_REFCOMPLIANCE_RUNNER |
| 170 | ) |
| 171 | if isComplianceModeDotProduct(self.testDesc): |
| 172 | conformanceBoundsPath = getBoundsResultFilePath( |
| 173 | resultFilePath, TOSA_REFCOMPLIANCE_RUNNER |
| 174 | ) |
| 175 | else: |
| 176 | # Not expecting a bounds file for this test |
| 177 | conformanceBoundsPath = None |
| 178 | elif "expected_result_file" in self.testDesc: |
| 179 | conformanceBoundsPath = None |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 180 | try: |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 181 | conformanceFilePath = ( |
| 182 | self.testDirPath |
| 183 | / self.testDesc["expected_result_file"][resultNum] |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 184 | ) |
| 185 | except IndexError: |
| 186 | result = TosaTestRunner.Result.INTERNAL_ERROR |
| 187 | msg = "Internal error: Missing expected_result_file {} in {}".format( |
| 188 | resultNum, self.descFile |
| 189 | ) |
| 190 | messages.append(msg) |
| 191 | print(msg) |
| 192 | break |
| 193 | else: |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 194 | # Nothing to check against |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 195 | conformanceFilePath = None |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 196 | conformanceBoundsPath = None |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 197 | |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 198 | if conformanceFilePath: |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 199 | print_check_result = True # Result from checker |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 200 | chkResult, tolerance, msg = test_check( |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 201 | conformanceFilePath, |
| 202 | resultFilePath, |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 203 | test_name=self.testName, |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 204 | test_desc=self.testDesc, |
| 205 | bnd_result_path=conformanceBoundsPath, |
| 206 | ofm_name=self.testDesc["ofm_name"][resultNum], |
| 207 | verify_lib_path=self.verify_lib_path, |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 208 | ) |
| 209 | # Change EXPECTED_PASS assumption if we have any failures |
| 210 | if chkResult != 0: |
| 211 | result = TosaTestRunner.Result.UNEXPECTED_FAILURE |
| 212 | messages.append(msg) |
| 213 | if self.args.verbose: |
| 214 | print(msg) |
| 215 | else: |
| 216 | # No conformance file to verify, just check results file exists |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 217 | if not resultFilePath.is_file(): |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 218 | result = TosaTestRunner.Result.UNEXPECTED_FAILURE |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 219 | msg = f"Results file is missing: {resultFilePath}" |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 220 | messages.append(msg) |
| 221 | print(msg) |
| 222 | |
Jeremy Johnson | e4b08ff | 2022-09-15 10:38:17 +0100 | [diff] [blame] | 223 | if resultFilePath.is_file(): |
| 224 | # Move the resultFilePath to allow subsequent system under |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 225 | # tests to create them and to test they have been created |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 226 | # and to enable compliance testing against refmodel results |
| 227 | resultFilePath.rename( |
| 228 | getRunnerResultFilePath(resultFilePath, sutModule) |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 229 | ) |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 230 | if ( |
| 231 | isComplianceModeDotProduct(self.testDesc) |
| 232 | and sutModule == TOSA_REFCOMPLIANCE_RUNNER |
| 233 | ): |
| 234 | boundsFilePath = getBoundsResultFilePath(resultFilePath) |
| 235 | if boundsFilePath.is_file(): |
| 236 | boundsFilePath = boundsFilePath.rename( |
| 237 | getBoundsResultFilePath(resultFilePath, sutModule) |
| 238 | ) |
| 239 | else: |
| 240 | result = TosaTestRunner.Result.INTERNAL_ERROR |
| 241 | msg = f"Internal error: Missing expected dot product compliance bounds file {boundsFilePath}" |
| 242 | messages.append(msg) |
| 243 | print(msg) |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 244 | |
| 245 | resultMessage = "\n".join(messages) if len(messages) > 0 else None |
| 246 | else: |
| 247 | if ( |
| 248 | expectedFailure |
| 249 | and tosaGraphResult == TosaTestRunner.TosaGraphResult.TOSA_ERROR |
| 250 | ): |
| 251 | result = TosaTestRunner.Result.EXPECTED_FAILURE |
| 252 | resultMessage = None |
| 253 | else: |
| 254 | result = TosaTestRunner.Result.UNEXPECTED_FAILURE |
| 255 | resultMessage = graphMessage |
| 256 | |
Jeremy Johnson | e2b5e87 | 2023-09-14 17:02:09 +0100 | [diff] [blame^] | 257 | status = "Result" if print_check_result else "Result code" |
| 258 | if ( |
| 259 | result == TosaTestRunner.Result.EXPECTED_FAILURE |
| 260 | or result == TosaTestRunner.Result.EXPECTED_PASS |
| 261 | ): |
| 262 | print_color(LogColors.GREEN, f"{sutModule}: {status} PASS {self.testName}") |
| 263 | else: |
| 264 | print_color(LogColors.RED, f"{sutModule}: {status} FAIL {self.testName}") |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 265 | |
| 266 | return result, resultMessage |
| 267 | |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 268 | class Result(IntEnum): |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 269 | """Test result codes.""" |
| 270 | |
Eric Kunze | e5e2676 | 2020-10-13 16:11:07 -0700 | [diff] [blame] | 271 | EXPECTED_PASS = 0 |
| 272 | EXPECTED_FAILURE = 1 |
| 273 | UNEXPECTED_PASS = 2 |
| 274 | UNEXPECTED_FAILURE = 3 |
| 275 | INTERNAL_ERROR = 4 |
Jeremy Johnson | be1a940 | 2021-12-15 17:14:56 +0000 | [diff] [blame] | 276 | SKIPPED = 5 |
| 277 | |
| 278 | class TosaGraphResult(IntEnum): |
| 279 | """The tosa_graph_result codes.""" |
| 280 | |
| 281 | TOSA_VALID = 0 |
| 282 | TOSA_UNPREDICTABLE = 1 |
| 283 | TOSA_ERROR = 2 |
| 284 | OTHER_ERROR = 3 |