blob: 876fbdd3390082ec7751642c8d1303efd725823d [file] [log] [blame]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00001"""Template test runner class for running TOSA tests."""
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +01002# Copyright (c) 2020-2023, ARM Limited.
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00003# SPDX-License-Identifier: Apache-2.0
Eric Kunzee5e26762020-10-13 16:11:07 -07004import json
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00005from enum import IntEnum
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00006
Jeremy Johnsone2b5e872023-09-14 17:02:09 +01007import schemavalidation.schemavalidation as sch
8from checker.color_print import LogColors
9from checker.color_print import print_color
10from checker.color_print import set_print_in_color
11from checker.tosa_result_checker import set_print_result
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000012from checker.tosa_result_checker import test_check
Jeremy Johnson65ba8092023-10-09 16:31:13 +010013from generator.datagenerator import GenerateLibrary
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000014from json2fbbin import json2fbbin
Jeremy Johnson65ba8092023-10-09 16:31:13 +010015from json2numpy import json2numpy
Jeremy Johnsone2b5e872023-09-14 17:02:09 +010016from runner.tosa_test_presets import TOSA_REFCOMPLIANCE_RUNNER
17
18
Jeremy Johnson9a758382023-11-07 16:27:35 +000019def isComplianceAbsModeNeeded(testDesc):
20 """Checks the test descriptor for DOT_PRODUCT/ABS_ERROR compliance mode."""
Jeremy Johnsone2b5e872023-09-14 17:02:09 +010021 if (
22 "meta" in testDesc
23 and "compliance" in testDesc["meta"]
24 and "tensors" in testDesc["meta"]["compliance"]
25 ):
26 for _, t in testDesc["meta"]["compliance"]["tensors"].items():
Jeremy Johnson9a758382023-11-07 16:27:35 +000027 if "mode" in t and t["mode"] in ("DOT_PRODUCT", "ABS_ERROR"):
Jeremy Johnsone2b5e872023-09-14 17:02:09 +010028 return True
29 return False
30
31
32def getRunnerResultFilePath(resultFilePath, sutModule):
33 """Return the result file path with the runner specific naming."""
34 return resultFilePath.with_suffix(f".{sutModule}{resultFilePath.suffix}")
35
36
37def getBoundsResultFilePath(resultFilePath, sutModule=None):
38 """Return the bounds result file with/without runner specific naming."""
39 boundsFilePath = resultFilePath.parent / f"bounds_{resultFilePath.name}"
40 if sutModule is not None:
41 boundsFilePath = boundsFilePath.with_suffix(
42 f".{sutModule}{boundsFilePath.suffix}"
43 )
44 return boundsFilePath
Eric Kunzee5e26762020-10-13 16:11:07 -070045
Kevin Cheng550ccc52021-03-03 11:21:43 -080046
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000047class TosaTestInvalid(Exception):
48 """Exception raised for errors loading test description.
Eric Kunzee5e26762020-10-13 16:11:07 -070049
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000050 Attributes:
51 path - full path to missing test description file
52 exception = underlying exception
53 """
Eric Kunzee5e26762020-10-13 16:11:07 -070054
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000055 def __init__(self, path, exception):
56 """Initialize test not found error."""
57 self.path = path
58 self.exception = exception
59 self.message = "Invalid test, could not read test description {}: {}".format(
60 self.path, str(self.exception)
61 )
62 super().__init__(self.message)
Kevin Cheng550ccc52021-03-03 11:21:43 -080063
Eric Kunzee5e26762020-10-13 16:11:07 -070064
65class TosaTestRunner:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000066 """TOSA Test Runner template class for systems under test."""
Eric Kunzee5e26762020-10-13 16:11:07 -070067
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010068 def __init__(self, args, runnerArgs, testDirPath):
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000069 """Initialize and load JSON meta data file."""
Eric Kunzee5e26762020-10-13 16:11:07 -070070 self.args = args
71 self.runnerArgs = runnerArgs
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010072 self.testDir = str(testDirPath)
73 self.testDirPath = testDirPath
74 self.testName = self.testDirPath.name
Jeremy Johnson9c2fe6e2023-10-04 16:55:04 +010075 self.verify_lib_path = args.verify_lib_path
Jeremy Johnson65ba8092023-10-09 16:31:13 +010076 self.generate_lib_path = args.generate_lib_path
Eric Kunzee5e26762020-10-13 16:11:07 -070077
Jeremy Johnson015c3552022-02-23 12:15:03 +000078 set_print_in_color(not args.no_color)
Jeremy Johnsone2b5e872023-09-14 17:02:09 +010079 # Stop the result checker printing anything - we will do it
80 set_print_result(False)
Jeremy Johnson015c3552022-02-23 12:15:03 +000081
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000082 # Check if we want to run binary and if its already converted
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010083 descFilePath = testDirPath / "desc.json"
84 descBinFilePath = testDirPath / "desc_binary.json"
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000085 if args.binary:
86 if descBinFilePath.is_file():
87 descFilePath = descBinFilePath
Eric Kunzee5e26762020-10-13 16:11:07 -070088
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000089 try:
90 # Load the json test file
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010091 with descFilePath.open("r") as fd:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000092 self.testDesc = json.load(fd)
Jeremy Johnsone2b5e872023-09-14 17:02:09 +010093 # Validate the json with the schema
94 sch.TestDescSchemaValidator().validate_config(self.testDesc)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000095 except Exception as e:
96 raise TosaTestInvalid(str(descFilePath), e)
97
98 # Convert to binary if needed
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010099 tosaFilePath = testDirPath / self.testDesc["tosa_file"]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000100 if args.binary and tosaFilePath.suffix == ".json":
101 # Convert tosa JSON to binary
102 json2fbbin.json_to_fbbin(
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +0100103 args.flatc_path,
104 args.schema_path,
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000105 tosaFilePath,
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100106 testDirPath,
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000107 )
108 # Write new desc_binary file
109 self.testDesc["tosa_file"] = tosaFilePath.stem + ".tosa"
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100110 with descBinFilePath.open("w") as fd:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000111 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 Johnsone4b08ff2022-09-15 10:38:17 +0100116 self.descFilePath = descFilePath
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000117
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100118 # 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 Johnsonbe1a9402021-12-15 17:14:56 +0000128 def skipTest(self):
Jeremy Johnson88588622022-07-12 16:42:29 +0100129 """Check if the test is skipped due to test type or profile selection."""
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000130 expectedFailure = self.testDesc["expected_failure"]
131 if self.args.test_type == "negative" and not expectedFailure:
Jeremy Johnson88588622022-07-12 16:42:29 +0100132 return True, "non-negative type"
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000133 elif self.args.test_type == "positive" and expectedFailure:
Jeremy Johnson88588622022-07-12 16:42:29 +0100134 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 Johnsonbe1a9402021-12-15 17:14:56 +0000140
Jeremy Johnson65ba8092023-10-09 16:31:13 +0100141 def _ready_file(self, dataFile, jsonOnly=False):
142 """Convert/create any data file that is missing."""
143 dataPath = self.testDirPath / dataFile
144 if not dataPath.is_file():
145 jsonPath = dataPath.with_suffix(".json")
146 if jsonPath.is_file():
147 # Data files stored as JSON
148 if self.args.verbose:
149 print(f"Readying data file: {dataPath}")
150 json2numpy.json_to_npy(jsonPath)
151 elif not jsonOnly:
152 # Use data generator for all data files
153 if self.args.verbose:
154 print("Readying all data input files")
155 dgl = GenerateLibrary(self.generate_lib_path)
156 dgl.set_config(self.testDesc)
157 dgl.write_numpy_files(self.testDirPath)
158
159 def readyDataFiles(self):
160 """Check that the data files have been created/converted."""
161 for dataFile in self.testDesc["ifm_file"]:
162 self._ready_file(dataFile)
163 # Convert expected result if any
164 if "expected_result_file" in self.testDesc:
165 for dataFile in self.testDesc["expected_result_file"]:
166 self._ready_file(dataFile, jsonOnly=True)
167
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000168 def runTestGraph(self):
169 """Override with function that calls system under test."""
Eric Kunzee5e26762020-10-13 16:11:07 -0700170 pass
171
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000172 def testResult(self, tosaGraphResult, graphMessage=None):
173 """Work out test result based on graph result and output files."""
174 expectedFailure = self.testDesc["expected_failure"]
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100175 print_check_result = False
176
177 sutModule = self.__module__
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000178
179 if tosaGraphResult == TosaTestRunner.TosaGraphResult.TOSA_VALID:
180 if expectedFailure:
181 result = TosaTestRunner.Result.UNEXPECTED_PASS
182 resultMessage = "Expected failure test incorrectly passed"
183 else:
184 # Work through all the results produced by the testing, assuming success
185 # but overriding this with any failures found
186 result = TosaTestRunner.Result.EXPECTED_PASS
187 messages = []
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100188
189 # Go through each output result checking it
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000190 for resultNum, resultFileName in enumerate(self.testDesc["ofm_file"]):
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100191 resultFilePath = self.testDirPath / resultFileName
192
193 # Work out the file to check against (if any)
194 if self.complianceMode and sutModule != TOSA_REFCOMPLIANCE_RUNNER:
195 conformanceFilePath = getRunnerResultFilePath(
196 resultFilePath, TOSA_REFCOMPLIANCE_RUNNER
197 )
Jeremy Johnson9a758382023-11-07 16:27:35 +0000198 if isComplianceAbsModeNeeded(self.testDesc):
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100199 conformanceBoundsPath = getBoundsResultFilePath(
200 resultFilePath, TOSA_REFCOMPLIANCE_RUNNER
201 )
202 else:
203 # Not expecting a bounds file for this test
204 conformanceBoundsPath = None
205 elif "expected_result_file" in self.testDesc:
206 conformanceBoundsPath = None
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000207 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100208 conformanceFilePath = (
209 self.testDirPath
210 / self.testDesc["expected_result_file"][resultNum]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000211 )
212 except IndexError:
213 result = TosaTestRunner.Result.INTERNAL_ERROR
214 msg = "Internal error: Missing expected_result_file {} in {}".format(
215 resultNum, self.descFile
216 )
217 messages.append(msg)
218 print(msg)
219 break
220 else:
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100221 # Nothing to check against
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100222 conformanceFilePath = None
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100223 conformanceBoundsPath = None
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000224
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100225 if conformanceFilePath:
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100226 print_check_result = True # Result from checker
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000227 chkResult, tolerance, msg = test_check(
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100228 conformanceFilePath,
229 resultFilePath,
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000230 test_name=self.testName,
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100231 test_desc=self.testDesc,
232 bnd_result_path=conformanceBoundsPath,
233 ofm_name=self.testDesc["ofm_name"][resultNum],
234 verify_lib_path=self.verify_lib_path,
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000235 )
236 # Change EXPECTED_PASS assumption if we have any failures
237 if chkResult != 0:
238 result = TosaTestRunner.Result.UNEXPECTED_FAILURE
239 messages.append(msg)
240 if self.args.verbose:
241 print(msg)
242 else:
243 # No conformance file to verify, just check results file exists
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100244 if not resultFilePath.is_file():
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000245 result = TosaTestRunner.Result.UNEXPECTED_FAILURE
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100246 msg = f"Results file is missing: {resultFilePath}"
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000247 messages.append(msg)
248 print(msg)
249
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100250 if resultFilePath.is_file():
251 # Move the resultFilePath to allow subsequent system under
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000252 # tests to create them and to test they have been created
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100253 # and to enable compliance testing against refmodel results
254 resultFilePath.rename(
255 getRunnerResultFilePath(resultFilePath, sutModule)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000256 )
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100257 if (
Jeremy Johnson9a758382023-11-07 16:27:35 +0000258 isComplianceAbsModeNeeded(self.testDesc)
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100259 and sutModule == TOSA_REFCOMPLIANCE_RUNNER
260 ):
261 boundsFilePath = getBoundsResultFilePath(resultFilePath)
262 if boundsFilePath.is_file():
263 boundsFilePath = boundsFilePath.rename(
264 getBoundsResultFilePath(resultFilePath, sutModule)
265 )
266 else:
267 result = TosaTestRunner.Result.INTERNAL_ERROR
268 msg = f"Internal error: Missing expected dot product compliance bounds file {boundsFilePath}"
269 messages.append(msg)
270 print(msg)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000271
272 resultMessage = "\n".join(messages) if len(messages) > 0 else None
273 else:
274 if (
275 expectedFailure
276 and tosaGraphResult == TosaTestRunner.TosaGraphResult.TOSA_ERROR
277 ):
278 result = TosaTestRunner.Result.EXPECTED_FAILURE
279 resultMessage = None
280 else:
281 result = TosaTestRunner.Result.UNEXPECTED_FAILURE
282 resultMessage = graphMessage
283
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100284 status = "Result" if print_check_result else "Result code"
285 if (
286 result == TosaTestRunner.Result.EXPECTED_FAILURE
287 or result == TosaTestRunner.Result.EXPECTED_PASS
288 ):
289 print_color(LogColors.GREEN, f"{sutModule}: {status} PASS {self.testName}")
290 else:
291 print_color(LogColors.RED, f"{sutModule}: {status} FAIL {self.testName}")
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000292
293 return result, resultMessage
294
Eric Kunzee5e26762020-10-13 16:11:07 -0700295 class Result(IntEnum):
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000296 """Test result codes."""
297
Eric Kunzee5e26762020-10-13 16:11:07 -0700298 EXPECTED_PASS = 0
299 EXPECTED_FAILURE = 1
300 UNEXPECTED_PASS = 2
301 UNEXPECTED_FAILURE = 3
302 INTERNAL_ERROR = 4
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000303 SKIPPED = 5
304
305 class TosaGraphResult(IntEnum):
306 """The tosa_graph_result codes."""
307
308 TOSA_VALID = 0
309 TOSA_UNPREDICTABLE = 1
310 TOSA_ERROR = 2
311 OTHER_ERROR = 3