blob: 814c864ec2c2d72eaa863cc2dbccc3b98f9189cb [file] [log] [blame]
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00001"""TOSA verification runner script."""
2# Copyright (c) 2020-2022, ARM Limited.
3# SPDX-License-Identifier: Apache-2.0
4import argparse
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +00005import importlib
6import os
7import queue
8import threading
9import traceback
10from datetime import datetime
11from pathlib import Path
12
13from json2numpy import json2numpy
14from runner.tosa_test_runner import TosaTestInvalid
15from runner.tosa_test_runner import TosaTestRunner
16from xunit import xunit
17
18TOSA_REFMODEL_RUNNER = "runner.tosa_refmodel_sut_run"
19MAX_XUNIT_TEST_MESSAGE = 1000
20
21
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 "--operator-fbs",
50 dest="operator_fbs",
51 default="conformance_tests/third_party/serialization_lib/schema/tosa.fbs",
52 type=str,
53 help="flat buffer syntax file",
54 )
55 parser.add_argument(
56 "--ref-model-path",
57 dest="ref_model_path",
58 default="reference_model/build/reference_model/tosa_reference_model",
59 type=str,
60 help="Path to reference model executable",
61 )
62 parser.add_argument(
63 "--flatc-path",
64 dest="flatc_path",
65 default="reference_model/build/thirdparty/serialization_lib/third_party/flatbuffers/flatc",
66 type=str,
67 help="Path to flatc compiler executable",
68 )
69 parser.add_argument(
70 "--ref-debug",
71 dest="ref_debug",
72 default="",
73 type=str,
74 help="Reference debug flag (low, med, high)",
75 )
76 parser.add_argument(
77 "--ref-intermediates",
78 dest="ref_intermediates",
79 default=0,
80 type=int,
81 help="Reference model dumps intermediate tensors",
82 )
83 parser.add_argument(
84 "-b",
85 "--binary",
86 dest="binary",
87 action="store_true",
88 help="Convert to using binary flatbuffers instead of JSON",
89 )
90 parser.add_argument(
91 "-v", "--verbose", dest="verbose", action="count", help="Verbose operation"
92 )
93 parser.add_argument(
94 "-j", "--jobs", dest="jobs", type=int, default=1, help="Number of parallel jobs"
95 )
96 parser.add_argument(
97 "--sut-module",
98 "-s",
99 dest="sut_module",
100 type=str,
101 nargs="+",
102 default=[TOSA_REFMODEL_RUNNER],
103 help="System under test module to load (derives from TosaTestRunner). May be repeated",
104 )
105 parser.add_argument(
106 "--sut-module-args",
107 dest="sut_module_args",
108 type=str,
109 nargs="+",
110 default=[],
111 help="System under test module arguments. Use sutmodulename:argvalue to pass an argument. May be repeated.",
112 )
113 parser.add_argument(
114 "--xunit-file",
115 dest="xunit_file",
116 type=str,
117 default="result.xml",
118 help="XUnit output file",
119 )
120 parser.add_argument(
121 "--test-type",
122 dest="test_type",
123 type=str,
124 default="both",
125 choices=["positive", "negative", "both"],
Jeremy Johnson88588622022-07-12 16:42:29 +0100126 help="Filter tests based on expected failure status",
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000127 )
Jeremy Johnson015c3552022-02-23 12:15:03 +0000128 parser.add_argument(
129 "--no-color",
130 "--no-colour",
131 dest="no_color",
132 action="store_true",
133 help="Disable color output",
134 )
Jeremy Johnson88588622022-07-12 16:42:29 +0100135 parser.add_argument(
136 "--profile",
137 dest="profile",
138 type=str,
139 choices=["tosa-bi", "tosa-mi"],
140 help="Filter tests based on profile",
141 )
Jerry Gea793f462023-04-11 00:05:02 +0000142 parser.add_argument(
143 "--tosa_level",
144 dest="tosa_level",
145 default="EIGHTK",
146 type=str,
147 help="A TOSA level defines operator parameter ranges that an implementation shall support."
148 "Config tosa_level for running the reference model only. Default is EIGHTK",
149 )
Tai Lya4d748b2023-03-28 22:06:56 +0000150 parser.add_argument(
151 "-p",
152 "--precise-mode",
153 dest="precise_mode",
154 action="store_true",
155 help="Run the reference model in precise mode (FP64)",
156 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000157
158 args = parser.parse_args(argv)
159
160 # Autodetect CPU count
161 if args.jobs <= 0:
162 args.jobs = os.cpu_count()
163
164 return args
165
166
167EXCLUSION_PREFIX = ["test", "model", "desc"]
168
169
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100170def convert2Numpy(test_path):
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000171 """Convert all the JSON numpy files back into binary numpy."""
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100172 jsons = test_path.glob("*.json")
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000173 for json in jsons:
174 for exclude in EXCLUSION_PREFIX:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100175 if json.name.startswith(exclude):
176 json = None
177 break
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000178 if json:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100179 # debug print(f"Converting {json}")
180 json2numpy.json_to_npy(json)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000181
182
183def workerThread(task_queue, runnerList, args, result_queue):
184 """Worker thread that runs the next test from the queue."""
185 while True:
186 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100187 test_path = task_queue.get(block=False)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000188 except queue.Empty:
189 break
190
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100191 if test_path is None:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000192 break
193
194 msg = ""
195 converted = False
196 for runnerModule, runnerArgs in runnerList:
197 try:
198 start_time = datetime.now()
199 # Set up system under test runner
200 runnerName = runnerModule.__name__
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100201 runner = runnerModule.TosaSUTRunner(args, runnerArgs, test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000202
Jeremy Johnson88588622022-07-12 16:42:29 +0100203 skip, reason = runner.skipTest()
204 if skip:
205 msg = "Skipping {} test".format(reason)
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100206 print("{} {}".format(msg, test_path))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000207 rc = TosaTestRunner.Result.SKIPPED
208 else:
209 # Convert JSON data files into numpy format on first pass
210 if not converted:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100211 convert2Numpy(test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000212 converted = True
213
214 if args.verbose:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100215 print(
216 "Running runner {} with test {}".format(
217 runnerName, test_path
218 )
219 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000220 try:
221 grc, gmsg = runner.runTestGraph()
222 rc, msg = runner.testResult(grc, gmsg)
223 except Exception as e:
224 msg = "System Under Test error: {}".format(e)
225 print(msg)
226 print(
227 "".join(
228 traceback.format_exception(
229 etype=type(e), value=e, tb=e.__traceback__
230 )
231 )
232 )
233 rc = TosaTestRunner.Result.INTERNAL_ERROR
234 except Exception as e:
235 msg = "Internal error: {}".format(e)
236 print(msg)
237 if not isinstance(e, TosaTestInvalid):
238 # Show stack trace on unexpected exceptions
239 print(
240 "".join(
241 traceback.format_exception(
242 etype=type(e), value=e, tb=e.__traceback__
243 )
244 )
245 )
246 rc = TosaTestRunner.Result.INTERNAL_ERROR
247 finally:
248 end_time = datetime.now()
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100249 result_queue.put(
250 (runnerName, test_path, rc, msg, end_time - start_time)
251 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000252
253 task_queue.task_done()
254
255 return True
256
257
258def loadSUTRunnerModules(args):
259 """Load in the system under test modules.
260
261 Returns a list of tuples of (runner_module, [argument list])
262 """
263 runnerList = []
264 # Remove any duplicates from the list
265 sut_module_list = list(set(args.sut_module))
266 for r in sut_module_list:
267 if args.verbose:
268 print("Loading module {}".format(r))
269
270 runner = importlib.import_module(r)
271
272 # Look for arguments associated with this runner
273 runnerArgPrefix = "{}:".format(r)
274 runnerArgList = []
275 for a in args.sut_module_args:
276 if a.startswith(runnerArgPrefix):
277 runnerArgList.append(a[len(runnerArgPrefix) :])
278 runnerList.append((runner, runnerArgList))
279
280 return runnerList
281
282
283def createXUnitResults(xunitFile, runnerList, resultLists, verbose):
284 """Create the xunit results file."""
285 xunit_result = xunit.xunit_results()
286
287 for runnerModule, _ in runnerList:
288 # Create test suite per system under test (runner)
289 runner = runnerModule.__name__
290 xunit_suite = xunit_result.create_suite(runner)
291
292 # Sort by test name
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100293 for test_path, rc, msg, time_delta in sorted(
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000294 resultLists[runner], key=lambda tup: tup[0]
295 ):
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100296 test_name = str(test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000297 xt = xunit.xunit_test(test_name, runner)
298
299 xt.time = str(
300 float(time_delta.seconds) + (float(time_delta.microseconds) * 1e-6)
301 )
302
303 testMsg = rc.name if not msg else "{}: {}".format(rc.name, msg)
304
305 if (
306 rc == TosaTestRunner.Result.EXPECTED_PASS
307 or rc == TosaTestRunner.Result.EXPECTED_FAILURE
308 ):
309 if verbose:
310 print("{} {} ({})".format(rc.name, test_name, runner))
311 elif rc == TosaTestRunner.Result.SKIPPED:
312 xt.skipped()
313 if verbose:
314 print("{} {} ({})".format(rc.name, test_name, runner))
315 else:
316 xt.failed(testMsg)
317 print("{} {} ({})".format(rc.name, test_name, runner))
318
319 xunit_suite.tests.append(xt)
320
321 xunit_result.write_results(xunitFile)
322
323
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100324def getTestsInPath(path):
325 # Recursively find any tests in this directory
326 desc_path = path / "desc.json"
327 if desc_path.is_file():
328 return [path]
329 elif path.is_dir():
330 path_list = []
331 for p in path.glob("*"):
332 path_list.extend(getTestsInPath(p))
333 return path_list
334 else:
335 return []
336
337
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000338def main(argv=None):
339 """Start worker threads to do the testing and outputs the results."""
340 args = parseArgs(argv)
341
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100342 if (
343 TOSA_REFMODEL_RUNNER in args.sut_module
344 and not Path(args.ref_model_path).is_file()
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000345 ):
346 print(
347 "Argument error: Reference Model not found ({})".format(args.ref_model_path)
348 )
349 exit(2)
350
351 if args.test_list_file:
352 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100353 with args.test_list_file.open("r") as f:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000354 args.test = f.read().splitlines()
355 except Exception as e:
356 print(
357 "Argument error: Cannot read list of tests in {}\n{}".format(
358 args.test_list_file, e
359 )
360 )
361 exit(2)
362
363 runnerList = loadSUTRunnerModules(args)
364
365 threads = []
366 taskQueue = queue.Queue()
367 resultQueue = queue.Queue()
368
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100369 for tdir in args.test:
370 tpath = Path(tdir)
371 if tpath.is_file():
372 if tpath.name != "README":
373 print(
374 "Warning: Skipping test {} as not a valid directory".format(tpath)
375 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000376 else:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100377 if args.recursive_tests:
378 tpath_list = getTestsInPath(tpath)
379 else:
380 tpath_list = [tpath]
381
382 for t in tpath_list:
383 taskQueue.put((t))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000384
385 print(
386 "Running {} tests on {} system{} under test".format(
387 taskQueue.qsize(), len(runnerList), "s" if len(runnerList) > 1 else ""
388 )
389 )
390
391 for i in range(args.jobs):
392 t = threading.Thread(
393 target=workerThread, args=(taskQueue, runnerList, args, resultQueue)
394 )
395 t.setDaemon(True)
396 t.start()
397 threads.append(t)
398
399 taskQueue.join()
400
401 # Set up results lists for each system under test
402 resultLists = {}
403 results = {}
404 for runnerModule, _ in runnerList:
405 runner = runnerModule.__name__
406 resultLists[runner] = []
407 results[runner] = [0] * len(TosaTestRunner.Result)
408
409 while True:
410 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100411 runner, test_path, rc, msg, time_delta = resultQueue.get(block=False)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000412 resultQueue.task_done()
413 except queue.Empty:
414 break
415
416 # Limit error messages to make results easier to digest
417 if msg and len(msg) > MAX_XUNIT_TEST_MESSAGE:
418 half = int(MAX_XUNIT_TEST_MESSAGE / 2)
419 trimmed = len(msg) - MAX_XUNIT_TEST_MESSAGE
420 msg = "{} ...\nskipped {} bytes\n... {}".format(
421 msg[:half], trimmed, msg[-half:]
422 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100423 resultLists[runner].append((test_path, rc, msg, time_delta))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000424 results[runner][rc] += 1
425
426 createXUnitResults(args.xunit_file, runnerList, resultLists, args.verbose)
427
428 # Print out results for each system under test
429 for runnerModule, _ in runnerList:
430 runner = runnerModule.__name__
431 resultSummary = []
432 for result in TosaTestRunner.Result:
433 resultSummary.append(
434 "{} {}".format(results[runner][result], result.name.lower())
435 )
436 print("Totals ({}): {}".format(runner, ", ".join(resultSummary)))
437
438 return 0
439
440
441if __name__ == "__main__":
442 exit(main())