blob: 77394ccbe6e1e8ada2414a596e6987c7e1525339 [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
5import glob
6import importlib
7import os
8import queue
9import threading
10import traceback
11from datetime import datetime
12from pathlib import Path
13
14from json2numpy import json2numpy
15from runner.tosa_test_runner import TosaTestInvalid
16from runner.tosa_test_runner import TosaTestRunner
17from xunit import xunit
18
19TOSA_REFMODEL_RUNNER = "runner.tosa_refmodel_sut_run"
20MAX_XUNIT_TEST_MESSAGE = 1000
21
22
23def parseArgs(argv):
24 """Parse the arguments and return the settings."""
25 parser = argparse.ArgumentParser()
26 group = parser.add_mutually_exclusive_group(required=True)
27 group.add_argument(
28 "-t",
29 "--test",
30 dest="test",
31 type=str,
32 nargs="+",
33 help="Test(s) to run",
34 )
35 group.add_argument(
36 "-T",
37 "--test-list",
38 dest="test_list_file",
39 type=Path,
40 help="File containing list of tests to run (one per line)",
41 )
42 parser.add_argument(
43 "--operator-fbs",
44 dest="operator_fbs",
45 default="conformance_tests/third_party/serialization_lib/schema/tosa.fbs",
46 type=str,
47 help="flat buffer syntax file",
48 )
49 parser.add_argument(
50 "--ref-model-path",
51 dest="ref_model_path",
52 default="reference_model/build/reference_model/tosa_reference_model",
53 type=str,
54 help="Path to reference model executable",
55 )
56 parser.add_argument(
57 "--flatc-path",
58 dest="flatc_path",
59 default="reference_model/build/thirdparty/serialization_lib/third_party/flatbuffers/flatc",
60 type=str,
61 help="Path to flatc compiler executable",
62 )
63 parser.add_argument(
64 "--ref-debug",
65 dest="ref_debug",
66 default="",
67 type=str,
68 help="Reference debug flag (low, med, high)",
69 )
70 parser.add_argument(
71 "--ref-intermediates",
72 dest="ref_intermediates",
73 default=0,
74 type=int,
75 help="Reference model dumps intermediate tensors",
76 )
77 parser.add_argument(
78 "-b",
79 "--binary",
80 dest="binary",
81 action="store_true",
82 help="Convert to using binary flatbuffers instead of JSON",
83 )
84 parser.add_argument(
85 "-v", "--verbose", dest="verbose", action="count", help="Verbose operation"
86 )
87 parser.add_argument(
88 "-j", "--jobs", dest="jobs", type=int, default=1, help="Number of parallel jobs"
89 )
90 parser.add_argument(
91 "--sut-module",
92 "-s",
93 dest="sut_module",
94 type=str,
95 nargs="+",
96 default=[TOSA_REFMODEL_RUNNER],
97 help="System under test module to load (derives from TosaTestRunner). May be repeated",
98 )
99 parser.add_argument(
100 "--sut-module-args",
101 dest="sut_module_args",
102 type=str,
103 nargs="+",
104 default=[],
105 help="System under test module arguments. Use sutmodulename:argvalue to pass an argument. May be repeated.",
106 )
107 parser.add_argument(
108 "--xunit-file",
109 dest="xunit_file",
110 type=str,
111 default="result.xml",
112 help="XUnit output file",
113 )
114 parser.add_argument(
115 "--test-type",
116 dest="test_type",
117 type=str,
118 default="both",
119 choices=["positive", "negative", "both"],
Jeremy Johnson88588622022-07-12 16:42:29 +0100120 help="Filter tests based on expected failure status",
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000121 )
Jeremy Johnson015c3552022-02-23 12:15:03 +0000122 parser.add_argument(
123 "--no-color",
124 "--no-colour",
125 dest="no_color",
126 action="store_true",
127 help="Disable color output",
128 )
Jeremy Johnson88588622022-07-12 16:42:29 +0100129 parser.add_argument(
130 "--profile",
131 dest="profile",
132 type=str,
133 choices=["tosa-bi", "tosa-mi"],
134 help="Filter tests based on profile",
135 )
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000136
137 args = parser.parse_args(argv)
138
139 # Autodetect CPU count
140 if args.jobs <= 0:
141 args.jobs = os.cpu_count()
142
143 return args
144
145
146EXCLUSION_PREFIX = ["test", "model", "desc"]
147
148
149def convert2Numpy(testDir):
150 """Convert all the JSON numpy files back into binary numpy."""
151 jsons = glob.glob(os.path.join(testDir, "*.json"))
152 for json in jsons:
153 for exclude in EXCLUSION_PREFIX:
154 if os.path.basename(json).startswith(exclude):
155 json = ""
156 if json:
157 # debug print("Converting " + json)
158 json2numpy.json_to_npy(Path(json))
159
160
161def workerThread(task_queue, runnerList, args, result_queue):
162 """Worker thread that runs the next test from the queue."""
163 while True:
164 try:
165 test = task_queue.get(block=False)
166 except queue.Empty:
167 break
168
169 if test is None:
170 break
171
172 msg = ""
173 converted = False
174 for runnerModule, runnerArgs in runnerList:
175 try:
176 start_time = datetime.now()
177 # Set up system under test runner
178 runnerName = runnerModule.__name__
179 runner = runnerModule.TosaSUTRunner(args, runnerArgs, test)
180
Jeremy Johnson88588622022-07-12 16:42:29 +0100181 skip, reason = runner.skipTest()
182 if skip:
183 msg = "Skipping {} test".format(reason)
Jeremy Johnsonbe1a9402021-12-15 17:14:56 +0000184 print("{} {}".format(msg, test))
185 rc = TosaTestRunner.Result.SKIPPED
186 else:
187 # Convert JSON data files into numpy format on first pass
188 if not converted:
189 convert2Numpy(test)
190 converted = True
191
192 if args.verbose:
193 print("Running runner {} with test {}".format(runnerName, test))
194 try:
195 grc, gmsg = runner.runTestGraph()
196 rc, msg = runner.testResult(grc, gmsg)
197 except Exception as e:
198 msg = "System Under Test error: {}".format(e)
199 print(msg)
200 print(
201 "".join(
202 traceback.format_exception(
203 etype=type(e), value=e, tb=e.__traceback__
204 )
205 )
206 )
207 rc = TosaTestRunner.Result.INTERNAL_ERROR
208 except Exception as e:
209 msg = "Internal error: {}".format(e)
210 print(msg)
211 if not isinstance(e, TosaTestInvalid):
212 # Show stack trace on unexpected exceptions
213 print(
214 "".join(
215 traceback.format_exception(
216 etype=type(e), value=e, tb=e.__traceback__
217 )
218 )
219 )
220 rc = TosaTestRunner.Result.INTERNAL_ERROR
221 finally:
222 end_time = datetime.now()
223 result_queue.put((runnerName, test, rc, msg, end_time - start_time))
224
225 task_queue.task_done()
226
227 return True
228
229
230def loadSUTRunnerModules(args):
231 """Load in the system under test modules.
232
233 Returns a list of tuples of (runner_module, [argument list])
234 """
235 runnerList = []
236 # Remove any duplicates from the list
237 sut_module_list = list(set(args.sut_module))
238 for r in sut_module_list:
239 if args.verbose:
240 print("Loading module {}".format(r))
241
242 runner = importlib.import_module(r)
243
244 # Look for arguments associated with this runner
245 runnerArgPrefix = "{}:".format(r)
246 runnerArgList = []
247 for a in args.sut_module_args:
248 if a.startswith(runnerArgPrefix):
249 runnerArgList.append(a[len(runnerArgPrefix) :])
250 runnerList.append((runner, runnerArgList))
251
252 return runnerList
253
254
255def createXUnitResults(xunitFile, runnerList, resultLists, verbose):
256 """Create the xunit results file."""
257 xunit_result = xunit.xunit_results()
258
259 for runnerModule, _ in runnerList:
260 # Create test suite per system under test (runner)
261 runner = runnerModule.__name__
262 xunit_suite = xunit_result.create_suite(runner)
263
264 # Sort by test name
265 for test, rc, msg, time_delta in sorted(
266 resultLists[runner], key=lambda tup: tup[0]
267 ):
268 test_name = test
269 xt = xunit.xunit_test(test_name, runner)
270
271 xt.time = str(
272 float(time_delta.seconds) + (float(time_delta.microseconds) * 1e-6)
273 )
274
275 testMsg = rc.name if not msg else "{}: {}".format(rc.name, msg)
276
277 if (
278 rc == TosaTestRunner.Result.EXPECTED_PASS
279 or rc == TosaTestRunner.Result.EXPECTED_FAILURE
280 ):
281 if verbose:
282 print("{} {} ({})".format(rc.name, test_name, runner))
283 elif rc == TosaTestRunner.Result.SKIPPED:
284 xt.skipped()
285 if verbose:
286 print("{} {} ({})".format(rc.name, test_name, runner))
287 else:
288 xt.failed(testMsg)
289 print("{} {} ({})".format(rc.name, test_name, runner))
290
291 xunit_suite.tests.append(xt)
292
293 xunit_result.write_results(xunitFile)
294
295
296def main(argv=None):
297 """Start worker threads to do the testing and outputs the results."""
298 args = parseArgs(argv)
299
300 if TOSA_REFMODEL_RUNNER in args.sut_module and not os.path.isfile(
301 args.ref_model_path
302 ):
303 print(
304 "Argument error: Reference Model not found ({})".format(args.ref_model_path)
305 )
306 exit(2)
307
308 if args.test_list_file:
309 try:
310 with open(args.test_list_file) as f:
311 args.test = f.read().splitlines()
312 except Exception as e:
313 print(
314 "Argument error: Cannot read list of tests in {}\n{}".format(
315 args.test_list_file, e
316 )
317 )
318 exit(2)
319
320 runnerList = loadSUTRunnerModules(args)
321
322 threads = []
323 taskQueue = queue.Queue()
324 resultQueue = queue.Queue()
325
326 for t in args.test:
327 if os.path.isfile(t):
328 if not os.path.basename(t) == "README":
329 print("Warning: Skipping test {} as not a valid directory".format(t))
330 else:
331 taskQueue.put((t))
332
333 print(
334 "Running {} tests on {} system{} under test".format(
335 taskQueue.qsize(), len(runnerList), "s" if len(runnerList) > 1 else ""
336 )
337 )
338
339 for i in range(args.jobs):
340 t = threading.Thread(
341 target=workerThread, args=(taskQueue, runnerList, args, resultQueue)
342 )
343 t.setDaemon(True)
344 t.start()
345 threads.append(t)
346
347 taskQueue.join()
348
349 # Set up results lists for each system under test
350 resultLists = {}
351 results = {}
352 for runnerModule, _ in runnerList:
353 runner = runnerModule.__name__
354 resultLists[runner] = []
355 results[runner] = [0] * len(TosaTestRunner.Result)
356
357 while True:
358 try:
359 runner, test, rc, msg, time_delta = resultQueue.get(block=False)
360 resultQueue.task_done()
361 except queue.Empty:
362 break
363
364 # Limit error messages to make results easier to digest
365 if msg and len(msg) > MAX_XUNIT_TEST_MESSAGE:
366 half = int(MAX_XUNIT_TEST_MESSAGE / 2)
367 trimmed = len(msg) - MAX_XUNIT_TEST_MESSAGE
368 msg = "{} ...\nskipped {} bytes\n... {}".format(
369 msg[:half], trimmed, msg[-half:]
370 )
371 resultLists[runner].append((test, rc, msg, time_delta))
372 results[runner][rc] += 1
373
374 createXUnitResults(args.xunit_file, runnerList, resultLists, args.verbose)
375
376 # Print out results for each system under test
377 for runnerModule, _ in runnerList:
378 runner = runnerModule.__name__
379 resultSummary = []
380 for result in TosaTestRunner.Result:
381 resultSummary.append(
382 "{} {}".format(results[runner][result], result.name.lower())
383 )
384 print("Totals ({}): {}".format(runner, ", ".join(resultSummary)))
385
386 return 0
387
388
389if __name__ == "__main__":
390 exit(main())