blob: d1755e600da1b101e0beb3faa24dcd7c1ab1474f [file] [log] [blame]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00001"""TOSA verification runner script."""
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
4import argparse
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00005import importlib
Jeremy Johnsone2b5e872023-09-14 17:02:09 +01006import json
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00007import os
8import queue
9import threading
10import traceback
11from datetime import datetime
12from pathlib import Path
13
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +010014import conformance.model_files as cmf
Jeremy Johnsone2b5e872023-09-14 17:02:09 +010015import runner.tosa_test_presets as ttp
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000016from json2numpy import json2numpy
17from runner.tosa_test_runner import TosaTestInvalid
18from runner.tosa_test_runner import TosaTestRunner
19from xunit import xunit
20
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000021
22def parseArgs(argv):
23 """Parse the arguments and return the settings."""
24 parser = argparse.ArgumentParser()
25 group = parser.add_mutually_exclusive_group(required=True)
26 group.add_argument(
27 "-t",
28 "--test",
29 dest="test",
30 type=str,
31 nargs="+",
32 help="Test(s) to run",
33 )
34 group.add_argument(
35 "-T",
36 "--test-list",
37 dest="test_list_file",
38 type=Path,
39 help="File containing list of tests to run (one per line)",
40 )
41 parser.add_argument(
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +010042 "-r",
43 "--recursive",
44 dest="recursive_tests",
45 action="store_true",
46 help="Recursively search for tests",
47 )
48 parser.add_argument(
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000049 "--ref-model-path",
50 dest="ref_model_path",
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +010051 type=Path,
52 help="Path to TOSA reference model executable",
53 )
54 parser.add_argument(
55 "--operator-fbs",
56 "--schema-path",
57 dest="schema_path",
58 type=Path,
59 help=(
60 "Path to TOSA reference model flat buffer schema. Defaults to "
61 f"`{cmf.DEFAULT_REF_MODEL_SCHEMA_PATH}` in parents parent directory of `ref-model-path`"
62 ),
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000063 )
64 parser.add_argument(
65 "--flatc-path",
66 dest="flatc_path",
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +010067 type=Path,
68 help=(
69 "Path to flatc executable. Defaults to "
70 f"`{cmf.DEFAULT_REF_MODEL_BUILD_FLATC_PATH}` in parent directory of `ref-model-path`"
71 ),
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +000072 )
73 parser.add_argument(
74 "--ref-debug",
75 dest="ref_debug",
76 default="",
77 type=str,
78 help="Reference debug flag (low, med, high)",
79 )
80 parser.add_argument(
81 "--ref-intermediates",
82 dest="ref_intermediates",
83 default=0,
84 type=int,
85 help="Reference model dumps intermediate tensors",
86 )
87 parser.add_argument(
88 "-b",
89 "--binary",
90 dest="binary",
91 action="store_true",
92 help="Convert to using binary flatbuffers instead of JSON",
93 )
94 parser.add_argument(
95 "-v", "--verbose", dest="verbose", action="count", help="Verbose operation"
96 )
97 parser.add_argument(
98 "-j", "--jobs", dest="jobs", type=int, default=1, help="Number of parallel jobs"
99 )
100 parser.add_argument(
101 "--sut-module",
102 "-s",
103 dest="sut_module",
104 type=str,
105 nargs="+",
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100106 default=[ttp.TOSA_REFMODEL_RUNNER],
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000107 help="System under test module to load (derives from TosaTestRunner). May be repeated",
108 )
109 parser.add_argument(
110 "--sut-module-args",
111 dest="sut_module_args",
112 type=str,
113 nargs="+",
114 default=[],
115 help="System under test module arguments. Use sutmodulename:argvalue to pass an argument. May be repeated.",
116 )
117 parser.add_argument(
118 "--xunit-file",
119 dest="xunit_file",
120 type=str,
121 default="result.xml",
122 help="XUnit output file",
123 )
124 parser.add_argument(
125 "--test-type",
126 dest="test_type",
127 type=str,
128 default="both",
129 choices=["positive", "negative", "both"],
Jeremy Johnson88588622022-07-12 16:42:29 +0100130 help="Filter tests based on expected failure status",
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000131 )
Jeremy Johnson015c3552022-02-23 12:15:03 +0000132 parser.add_argument(
133 "--no-color",
134 "--no-colour",
135 dest="no_color",
136 action="store_true",
137 help="Disable color output",
138 )
Jeremy Johnson88588622022-07-12 16:42:29 +0100139 parser.add_argument(
140 "--profile",
141 dest="profile",
142 type=str,
143 choices=["tosa-bi", "tosa-mi"],
144 help="Filter tests based on profile",
145 )
Jerry Gea793f462023-04-11 00:05:02 +0000146 parser.add_argument(
147 "--tosa_level",
148 dest="tosa_level",
149 default="EIGHTK",
150 type=str,
151 help="A TOSA level defines operator parameter ranges that an implementation shall support."
152 "Config tosa_level for running the reference model only. Default is EIGHTK",
153 )
Tai Lya4d748b2023-03-28 22:06:56 +0000154 parser.add_argument(
155 "-p",
156 "--precise-mode",
157 dest="precise_mode",
158 action="store_true",
159 help="Run the reference model in precise mode (FP64)",
160 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000161
162 args = parser.parse_args(argv)
163
164 # Autodetect CPU count
165 if args.jobs <= 0:
166 args.jobs = os.cpu_count()
167
168 return args
169
170
171EXCLUSION_PREFIX = ["test", "model", "desc"]
172
173
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100174def convert2Numpy(test_path):
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000175 """Convert all the JSON numpy files back into binary numpy."""
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100176 jsons = test_path.glob("*.json")
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100177 for j in jsons:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000178 for exclude in EXCLUSION_PREFIX:
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100179 if j.name.startswith(exclude):
180 j = None
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100181 break
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100182 if j:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100183 # debug print(f"Converting {json}")
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100184 json2numpy.json_to_npy(j)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000185
186
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100187def workerThread(task_queue, runnerList, complianceRunner, args, result_queue):
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000188 """Worker thread that runs the next test from the queue."""
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100189 complianceRunnerList = runnerList.copy()
190 complianceRunnerList.insert(0, (complianceRunner, []))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000191 while True:
192 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100193 test_path = task_queue.get(block=False)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000194 except queue.Empty:
195 break
196
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100197 if test_path is None:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000198 break
199
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100200 try:
201 # Check for compliance test
202 desc = test_path / "desc.json"
203 with desc.open("r") as fd:
204 j = json.load(fd)
205 compliance = "compliance" in j["meta"]
206 except Exception:
207 compliance = False
208
209 if compliance:
210 # Run compliance first to create output files!
211 currentRunners = complianceRunnerList
212 else:
213 currentRunners = runnerList
214
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000215 msg = ""
216 converted = False
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100217 for runnerModule, runnerArgs in currentRunners:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000218 try:
219 start_time = datetime.now()
220 # Set up system under test runner
221 runnerName = runnerModule.__name__
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100222 runner = runnerModule.TosaSUTRunner(args, runnerArgs, test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000223
Jeremy Johnson88588622022-07-12 16:42:29 +0100224 skip, reason = runner.skipTest()
225 if skip:
226 msg = "Skipping {} test".format(reason)
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100227 print("{} {}".format(msg, test_path))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000228 rc = TosaTestRunner.Result.SKIPPED
229 else:
230 # Convert JSON data files into numpy format on first pass
231 if not converted:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100232 convert2Numpy(test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000233 converted = True
234
235 if args.verbose:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100236 print(
237 "Running runner {} with test {}".format(
238 runnerName, test_path
239 )
240 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000241 try:
242 grc, gmsg = runner.runTestGraph()
243 rc, msg = runner.testResult(grc, gmsg)
244 except Exception as e:
245 msg = "System Under Test error: {}".format(e)
246 print(msg)
247 print(
248 "".join(
249 traceback.format_exception(
250 etype=type(e), value=e, tb=e.__traceback__
251 )
252 )
253 )
254 rc = TosaTestRunner.Result.INTERNAL_ERROR
255 except Exception as e:
256 msg = "Internal error: {}".format(e)
257 print(msg)
258 if not isinstance(e, TosaTestInvalid):
259 # Show stack trace on unexpected exceptions
260 print(
261 "".join(
262 traceback.format_exception(
263 etype=type(e), value=e, tb=e.__traceback__
264 )
265 )
266 )
267 rc = TosaTestRunner.Result.INTERNAL_ERROR
268 finally:
269 end_time = datetime.now()
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100270 result_queue.put(
271 (runnerName, test_path, rc, msg, end_time - start_time)
272 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000273
274 task_queue.task_done()
275
276 return True
277
278
279def loadSUTRunnerModules(args):
280 """Load in the system under test modules.
281
282 Returns a list of tuples of (runner_module, [argument list])
283 """
284 runnerList = []
285 # Remove any duplicates from the list
286 sut_module_list = list(set(args.sut_module))
287 for r in sut_module_list:
288 if args.verbose:
289 print("Loading module {}".format(r))
290
291 runner = importlib.import_module(r)
292
293 # Look for arguments associated with this runner
294 runnerArgPrefix = "{}:".format(r)
295 runnerArgList = []
296 for a in args.sut_module_args:
297 if a.startswith(runnerArgPrefix):
298 runnerArgList.append(a[len(runnerArgPrefix) :])
299 runnerList.append((runner, runnerArgList))
300
301 return runnerList
302
303
304def createXUnitResults(xunitFile, runnerList, resultLists, verbose):
305 """Create the xunit results file."""
306 xunit_result = xunit.xunit_results()
307
308 for runnerModule, _ in runnerList:
309 # Create test suite per system under test (runner)
310 runner = runnerModule.__name__
311 xunit_suite = xunit_result.create_suite(runner)
312
313 # Sort by test name
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100314 for test_path, rc, msg, time_delta in sorted(
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000315 resultLists[runner], key=lambda tup: tup[0]
316 ):
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100317 test_name = str(test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000318 xt = xunit.xunit_test(test_name, runner)
319
320 xt.time = str(
321 float(time_delta.seconds) + (float(time_delta.microseconds) * 1e-6)
322 )
323
324 testMsg = rc.name if not msg else "{}: {}".format(rc.name, msg)
325
326 if (
327 rc == TosaTestRunner.Result.EXPECTED_PASS
328 or rc == TosaTestRunner.Result.EXPECTED_FAILURE
329 ):
330 if verbose:
331 print("{} {} ({})".format(rc.name, test_name, runner))
332 elif rc == TosaTestRunner.Result.SKIPPED:
333 xt.skipped()
334 if verbose:
335 print("{} {} ({})".format(rc.name, test_name, runner))
336 else:
337 xt.failed(testMsg)
338 print("{} {} ({})".format(rc.name, test_name, runner))
339
340 xunit_suite.tests.append(xt)
341
342 xunit_result.write_results(xunitFile)
343
344
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100345def getTestsInPath(path):
346 # Recursively find any tests in this directory
347 desc_path = path / "desc.json"
348 if desc_path.is_file():
349 return [path]
350 elif path.is_dir():
351 path_list = []
352 for p in path.glob("*"):
353 path_list.extend(getTestsInPath(p))
354 return path_list
355 else:
356 return []
357
358
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000359def main(argv=None):
360 """Start worker threads to do the testing and outputs the results."""
361 args = parseArgs(argv)
362
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +0100363 # Set up some defaults
364 if args.ref_model_path is None:
365 args.ref_model_path = cmf.find_tosa_file(
366 cmf.TosaFileType.REF_MODEL, Path("reference_model"), False
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000367 )
Jeremy Johnsonf0348ea2023-09-27 16:10:59 +0100368 if args.flatc_path is None:
369 args.flatc_path = cmf.find_tosa_file(
370 cmf.TosaFileType.FLATC, args.ref_model_path
371 )
372 if args.schema_path is None:
373 args.schema_path = cmf.find_tosa_file(
374 cmf.TosaFileType.SCHEMA, args.ref_model_path
375 )
376
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100377 # Always check as it will be needed for compliance
378 if not args.ref_model_path.is_file():
379 print(
380 f"Argument error: Reference Model not found - ({str(args.ref_model_path)})"
381 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000382 exit(2)
383
384 if args.test_list_file:
385 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100386 with args.test_list_file.open("r") as f:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000387 args.test = f.read().splitlines()
388 except Exception as e:
389 print(
390 "Argument error: Cannot read list of tests in {}\n{}".format(
391 args.test_list_file, e
392 )
393 )
394 exit(2)
395
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100396 # Load in the runner modules and the ref model compliance module
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000397 runnerList = loadSUTRunnerModules(args)
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100398 complianceRunner = importlib.import_module(ttp.TOSA_REFCOMPLIANCE_RUNNER)
399 # Create a separate reporting runner list as the compliance runner may not
400 # be always run - depends on compliance testing
401 fullRunnerList = runnerList + [(complianceRunner, [])]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000402
403 threads = []
404 taskQueue = queue.Queue()
405 resultQueue = queue.Queue()
406
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100407 for tdir in args.test:
408 tpath = Path(tdir)
409 if tpath.is_file():
410 if tpath.name != "README":
411 print(
412 "Warning: Skipping test {} as not a valid directory".format(tpath)
413 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000414 else:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100415 if args.recursive_tests:
416 tpath_list = getTestsInPath(tpath)
417 else:
418 tpath_list = [tpath]
419
420 for t in tpath_list:
421 taskQueue.put((t))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000422
423 print(
424 "Running {} tests on {} system{} under test".format(
425 taskQueue.qsize(), len(runnerList), "s" if len(runnerList) > 1 else ""
426 )
427 )
428
429 for i in range(args.jobs):
430 t = threading.Thread(
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100431 target=workerThread,
432 args=(taskQueue, runnerList, complianceRunner, args, resultQueue),
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000433 )
434 t.setDaemon(True)
435 t.start()
436 threads.append(t)
437
438 taskQueue.join()
439
440 # Set up results lists for each system under test
441 resultLists = {}
442 results = {}
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100443 for runnerModule, _ in fullRunnerList:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000444 runner = runnerModule.__name__
445 resultLists[runner] = []
446 results[runner] = [0] * len(TosaTestRunner.Result)
447
448 while True:
449 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100450 runner, test_path, rc, msg, time_delta = resultQueue.get(block=False)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000451 resultQueue.task_done()
452 except queue.Empty:
453 break
454
455 # Limit error messages to make results easier to digest
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100456 if msg and len(msg) > ttp.MAX_XUNIT_TEST_MESSAGE:
457 half = int(ttp.MAX_XUNIT_TEST_MESSAGE / 2)
458 trimmed = len(msg) - ttp.MAX_XUNIT_TEST_MESSAGE
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000459 msg = "{} ...\nskipped {} bytes\n... {}".format(
460 msg[:half], trimmed, msg[-half:]
461 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100462 resultLists[runner].append((test_path, rc, msg, time_delta))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000463 results[runner][rc] += 1
464
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100465 createXUnitResults(args.xunit_file, fullRunnerList, resultLists, args.verbose)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000466
467 # Print out results for each system under test
Jeremy Johnsone2b5e872023-09-14 17:02:09 +0100468 for runnerModule, _ in fullRunnerList:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000469 runner = runnerModule.__name__
470 resultSummary = []
471 for result in TosaTestRunner.Result:
472 resultSummary.append(
473 "{} {}".format(results[runner][result], result.name.lower())
474 )
475 print("Totals ({}): {}".format(runner, ", ".join(resultSummary)))
476
477 return 0
478
479
480if __name__ == "__main__":
481 exit(main())