#!/usr/bin/env python3 # Copyright (c) 2020, ARM Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import sys import re import os import subprocess import shlex import json import glob import math import queue import threading import traceback import importlib from enum import IntEnum, Enum, unique from datetime import datetime # Include the ../shared directory in PYTHONPATH parent_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(parent_dir, '..', 'scripts')) sys.path.append(os.path.join(parent_dir, '..', 'scripts', 'xunit')) import xunit import tosa from tosa_test_gen import TosaTestGen from tosa_test_runner import TosaTestRunner no_color_printing = False #from run_tf_unit_test import LogColors, print_color, run_sh_command def parseArgs(): parser = argparse.ArgumentParser() parser.add_argument('-t', '--test', dest='test', type=str, nargs='+', help='Test(s) to run') parser.add_argument('--seed', dest='random_seed', default=42, type=int, help='Random seed for test generation') parser.add_argument('--ref-model-path', dest='ref_model_path', default='build/reference_model/tosa_reference_model', type=str, help='Path to reference model executable') parser.add_argument('--ref-debug', dest='ref_debug', default='', type=str, help='Reference debug flag (low, med, high)') parser.add_argument('--ref-intermediates', dest='ref_intermediates', default=0, type=int, help='Reference model dumps intermediate tensors') parser.add_argument('-v', '--verbose', dest='verbose', action='count', help='Verbose operation') parser.add_argument('-j', '--jobs', dest='jobs', type=int, default=1, help='Number of parallel jobs') parser.add_argument('--sut-module', '-s', dest='sut_module', type=str, nargs='+', default=['tosa_ref_run'], help='System under test module to load (derives from TosaTestRunner). May be repeated') parser.add_argument('--sut-module-args', dest='sut_module_args', type=str, nargs='+', default=[], help='System under test module arguments. Use sutmodulename:argvalue to pass an argument. May be repeated.') parser.add_argument('--xunit-file', dest='xunit_file', type=str, default='result.xml', help='XUnit output file') args = parser.parse_args() # Autodetect CPU count if args.jobs <= 0: args.jobs = os.cpu_count() return args def workerThread(task_queue, runnerList, args, result_queue): while True: try: test = task_queue.get(block=False) except queue.Empty: break if test is None: break msg = '' start_time = datetime.now() try: for runnerModule, runnerArgs in runnerList: if args.verbose: print('Running runner {} with test {}'.format(runnerModule.__name__, test)) runner = runnerModule.TosaRefRunner(args, runnerArgs, test) try: rc = runner.runModel() except Exception as e: rc = TosaTestRunner.Result.INTERNAL_ERROR except Exception as e: print('Internal regression error: {}'.format(e)) print(''.join(traceback.format_exception(etype=type(e), value=e, tb=e.__traceback__))) rc = TosaTestRunner.Result.INTERNAL_ERROR end_time = datetime.now() result_queue.put((test, rc, msg, end_time - start_time)) task_queue.task_done() return True def loadRefModules(args): # Returns a tuple of (runner_module, [argument list]) runnerList = [] for r in args.sut_module: if args.verbose: print('Loading module {}'.format(r)) runner = importlib.import_module(r) # Look for arguments associated with this runner runnerArgPrefix = '{}:'.format(r) runnerArgList = [] for a in args.sut_module_args: if a.startswith(runnerArgPrefix): runnerArgList.append(a[len(runnerArgPrefix):]) runnerList.append((runner, runnerArgList)) return runnerList def main(): args = parseArgs() runnerList = loadRefModules(args) threads = [] taskQueue = queue.Queue() resultQueue = queue.Queue() for t in args.test: taskQueue.put((t)) print('Running {} tests '.format(taskQueue.qsize())) for i in range(args.jobs): t = threading.Thread(target=workerThread, args=(taskQueue, runnerList, args, resultQueue)) t.setDaemon(True) t.start() threads.append(t) taskQueue.join() resultList = [] results = [0] * len(TosaTestRunner.Result) while True: try: test, rc, msg, time_delta = resultQueue.get(block=False) except queue.Empty: break resultList.append((test, rc, msg, time_delta)) results[rc] = results[rc] + 1 xunit_result = xunit.xunit_results('Regressions') xunit_suite = xunit_result.create_suite('Unit tests') # Sort by test name for test, rc, msg, time_delta in sorted(resultList, key=lambda tup: tup[0]): test_name = test xt = xunit.xunit_test(test_name, 'reference') xt.time = str(float(time_delta.seconds) + (float(time_delta.microseconds) * 1e-6)) if rc == TosaTestRunner.Result.EXPECTED_PASS or rc == TosaTestRunner.Result.EXPECTED_FAILURE: if args.verbose: print('{} {}'.format(rc.name, test_name)) else: xt.failed(msg) print('{} {}'.format(rc.name, test_name)) xunit_suite.tests.append(xt) resultQueue.task_done() xunit_result.write_results(args.xunit_file) print('Totals: ', end='') for result in TosaTestRunner.Result: print('{} {}, '.format(results[result], result.name.lower()), end ='') print() return 0 if __name__ == '__main__': exit(main())