blob: ddb32a4641ab2e9dbfc67251f178327e05af8094 [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 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000142
143 args = parser.parse_args(argv)
144
145 # Autodetect CPU count
146 if args.jobs <= 0:
147 args.jobs = os.cpu_count()
148
149 return args
150
151
152EXCLUSION_PREFIX = ["test", "model", "desc"]
153
154
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100155def convert2Numpy(test_path):
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000156 """Convert all the JSON numpy files back into binary numpy."""
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100157 jsons = test_path.glob("*.json")
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000158 for json in jsons:
159 for exclude in EXCLUSION_PREFIX:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100160 if json.name.startswith(exclude):
161 json = None
162 break
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000163 if json:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100164 # debug print(f"Converting {json}")
165 json2numpy.json_to_npy(json)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000166
167
168def workerThread(task_queue, runnerList, args, result_queue):
169 """Worker thread that runs the next test from the queue."""
170 while True:
171 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100172 test_path = task_queue.get(block=False)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000173 except queue.Empty:
174 break
175
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100176 if test_path is None:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000177 break
178
179 msg = ""
180 converted = False
181 for runnerModule, runnerArgs in runnerList:
182 try:
183 start_time = datetime.now()
184 # Set up system under test runner
185 runnerName = runnerModule.__name__
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100186 runner = runnerModule.TosaSUTRunner(args, runnerArgs, test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000187
Jeremy Johnson88588622022-07-12 16:42:29 +0100188 skip, reason = runner.skipTest()
189 if skip:
190 msg = "Skipping {} test".format(reason)
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100191 print("{} {}".format(msg, test_path))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000192 rc = TosaTestRunner.Result.SKIPPED
193 else:
194 # Convert JSON data files into numpy format on first pass
195 if not converted:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100196 convert2Numpy(test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000197 converted = True
198
199 if args.verbose:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100200 print(
201 "Running runner {} with test {}".format(
202 runnerName, test_path
203 )
204 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000205 try:
206 grc, gmsg = runner.runTestGraph()
207 rc, msg = runner.testResult(grc, gmsg)
208 except Exception as e:
209 msg = "System Under Test error: {}".format(e)
210 print(msg)
211 print(
212 "".join(
213 traceback.format_exception(
214 etype=type(e), value=e, tb=e.__traceback__
215 )
216 )
217 )
218 rc = TosaTestRunner.Result.INTERNAL_ERROR
219 except Exception as e:
220 msg = "Internal error: {}".format(e)
221 print(msg)
222 if not isinstance(e, TosaTestInvalid):
223 # Show stack trace on unexpected exceptions
224 print(
225 "".join(
226 traceback.format_exception(
227 etype=type(e), value=e, tb=e.__traceback__
228 )
229 )
230 )
231 rc = TosaTestRunner.Result.INTERNAL_ERROR
232 finally:
233 end_time = datetime.now()
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100234 result_queue.put(
235 (runnerName, test_path, rc, msg, end_time - start_time)
236 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000237
238 task_queue.task_done()
239
240 return True
241
242
243def loadSUTRunnerModules(args):
244 """Load in the system under test modules.
245
246 Returns a list of tuples of (runner_module, [argument list])
247 """
248 runnerList = []
249 # Remove any duplicates from the list
250 sut_module_list = list(set(args.sut_module))
251 for r in sut_module_list:
252 if args.verbose:
253 print("Loading module {}".format(r))
254
255 runner = importlib.import_module(r)
256
257 # Look for arguments associated with this runner
258 runnerArgPrefix = "{}:".format(r)
259 runnerArgList = []
260 for a in args.sut_module_args:
261 if a.startswith(runnerArgPrefix):
262 runnerArgList.append(a[len(runnerArgPrefix) :])
263 runnerList.append((runner, runnerArgList))
264
265 return runnerList
266
267
268def createXUnitResults(xunitFile, runnerList, resultLists, verbose):
269 """Create the xunit results file."""
270 xunit_result = xunit.xunit_results()
271
272 for runnerModule, _ in runnerList:
273 # Create test suite per system under test (runner)
274 runner = runnerModule.__name__
275 xunit_suite = xunit_result.create_suite(runner)
276
277 # Sort by test name
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100278 for test_path, rc, msg, time_delta in sorted(
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000279 resultLists[runner], key=lambda tup: tup[0]
280 ):
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100281 test_name = str(test_path)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000282 xt = xunit.xunit_test(test_name, runner)
283
284 xt.time = str(
285 float(time_delta.seconds) + (float(time_delta.microseconds) * 1e-6)
286 )
287
288 testMsg = rc.name if not msg else "{}: {}".format(rc.name, msg)
289
290 if (
291 rc == TosaTestRunner.Result.EXPECTED_PASS
292 or rc == TosaTestRunner.Result.EXPECTED_FAILURE
293 ):
294 if verbose:
295 print("{} {} ({})".format(rc.name, test_name, runner))
296 elif rc == TosaTestRunner.Result.SKIPPED:
297 xt.skipped()
298 if verbose:
299 print("{} {} ({})".format(rc.name, test_name, runner))
300 else:
301 xt.failed(testMsg)
302 print("{} {} ({})".format(rc.name, test_name, runner))
303
304 xunit_suite.tests.append(xt)
305
306 xunit_result.write_results(xunitFile)
307
308
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100309def getTestsInPath(path):
310 # Recursively find any tests in this directory
311 desc_path = path / "desc.json"
312 if desc_path.is_file():
313 return [path]
314 elif path.is_dir():
315 path_list = []
316 for p in path.glob("*"):
317 path_list.extend(getTestsInPath(p))
318 return path_list
319 else:
320 return []
321
322
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000323def main(argv=None):
324 """Start worker threads to do the testing and outputs the results."""
325 args = parseArgs(argv)
326
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100327 if (
328 TOSA_REFMODEL_RUNNER in args.sut_module
329 and not Path(args.ref_model_path).is_file()
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000330 ):
331 print(
332 "Argument error: Reference Model not found ({})".format(args.ref_model_path)
333 )
334 exit(2)
335
336 if args.test_list_file:
337 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100338 with args.test_list_file.open("r") as f:
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000339 args.test = f.read().splitlines()
340 except Exception as e:
341 print(
342 "Argument error: Cannot read list of tests in {}\n{}".format(
343 args.test_list_file, e
344 )
345 )
346 exit(2)
347
348 runnerList = loadSUTRunnerModules(args)
349
350 threads = []
351 taskQueue = queue.Queue()
352 resultQueue = queue.Queue()
353
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100354 for tdir in args.test:
355 tpath = Path(tdir)
356 if tpath.is_file():
357 if tpath.name != "README":
358 print(
359 "Warning: Skipping test {} as not a valid directory".format(tpath)
360 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000361 else:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100362 if args.recursive_tests:
363 tpath_list = getTestsInPath(tpath)
364 else:
365 tpath_list = [tpath]
366
367 for t in tpath_list:
368 taskQueue.put((t))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000369
370 print(
371 "Running {} tests on {} system{} under test".format(
372 taskQueue.qsize(), len(runnerList), "s" if len(runnerList) > 1 else ""
373 )
374 )
375
376 for i in range(args.jobs):
377 t = threading.Thread(
378 target=workerThread, args=(taskQueue, runnerList, args, resultQueue)
379 )
380 t.setDaemon(True)
381 t.start()
382 threads.append(t)
383
384 taskQueue.join()
385
386 # Set up results lists for each system under test
387 resultLists = {}
388 results = {}
389 for runnerModule, _ in runnerList:
390 runner = runnerModule.__name__
391 resultLists[runner] = []
392 results[runner] = [0] * len(TosaTestRunner.Result)
393
394 while True:
395 try:
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100396 runner, test_path, rc, msg, time_delta = resultQueue.get(block=False)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000397 resultQueue.task_done()
398 except queue.Empty:
399 break
400
401 # Limit error messages to make results easier to digest
402 if msg and len(msg) > MAX_XUNIT_TEST_MESSAGE:
403 half = int(MAX_XUNIT_TEST_MESSAGE / 2)
404 trimmed = len(msg) - MAX_XUNIT_TEST_MESSAGE
405 msg = "{} ...\nskipped {} bytes\n... {}".format(
406 msg[:half], trimmed, msg[-half:]
407 )
Jeremy Johnsone4b08ff2022-09-15 10:38:17 +0100408 resultLists[runner].append((test_path, rc, msg, time_delta))
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000409 results[runner][rc] += 1
410
411 createXUnitResults(args.xunit_file, runnerList, resultLists, args.verbose)
412
413 # Print out results for each system under test
414 for runnerModule, _ in runnerList:
415 runner = runnerModule.__name__
416 resultSummary = []
417 for result in TosaTestRunner.Result:
418 resultSummary.append(
419 "{} {}".format(results[runner][result], result.name.lower())
420 )
421 print("Totals ({}): {}".format(runner, ", ".join(resultSummary)))
422
423 return 0
424
425
426if __name__ == "__main__":
427 exit(main())