162306a36Sopenharmony_ci#!/usr/bin/env python3 262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci""" 562306a36Sopenharmony_citdc.py - Linux tc (Traffic Control) unit test driver 662306a36Sopenharmony_ci 762306a36Sopenharmony_ciCopyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> 862306a36Sopenharmony_ci""" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ciimport re 1162306a36Sopenharmony_ciimport os 1262306a36Sopenharmony_ciimport sys 1362306a36Sopenharmony_ciimport argparse 1462306a36Sopenharmony_ciimport importlib 1562306a36Sopenharmony_ciimport json 1662306a36Sopenharmony_ciimport subprocess 1762306a36Sopenharmony_ciimport time 1862306a36Sopenharmony_ciimport traceback 1962306a36Sopenharmony_cifrom collections import OrderedDict 2062306a36Sopenharmony_cifrom string import Template 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cifrom tdc_config import * 2362306a36Sopenharmony_cifrom tdc_helper import * 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciimport TdcPlugin 2662306a36Sopenharmony_cifrom TdcResults import * 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciclass PluginDependencyException(Exception): 2962306a36Sopenharmony_ci def __init__(self, missing_pg): 3062306a36Sopenharmony_ci self.missing_pg = missing_pg 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciclass PluginMgrTestFail(Exception): 3362306a36Sopenharmony_ci def __init__(self, stage, output, message): 3462306a36Sopenharmony_ci self.stage = stage 3562306a36Sopenharmony_ci self.output = output 3662306a36Sopenharmony_ci self.message = message 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciclass PluginMgr: 3962306a36Sopenharmony_ci def __init__(self, argparser): 4062306a36Sopenharmony_ci super().__init__() 4162306a36Sopenharmony_ci self.plugins = {} 4262306a36Sopenharmony_ci self.plugin_instances = [] 4362306a36Sopenharmony_ci self.failed_plugins = {} 4462306a36Sopenharmony_ci self.argparser = argparser 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci # TODO, put plugins in order 4762306a36Sopenharmony_ci plugindir = os.getenv('TDC_PLUGIN_DIR', './plugins') 4862306a36Sopenharmony_ci for dirpath, dirnames, filenames in os.walk(plugindir): 4962306a36Sopenharmony_ci for fn in filenames: 5062306a36Sopenharmony_ci if (fn.endswith('.py') and 5162306a36Sopenharmony_ci not fn == '__init__.py' and 5262306a36Sopenharmony_ci not fn.startswith('#') and 5362306a36Sopenharmony_ci not fn.startswith('.#')): 5462306a36Sopenharmony_ci mn = fn[0:-3] 5562306a36Sopenharmony_ci foo = importlib.import_module('plugins.' + mn) 5662306a36Sopenharmony_ci self.plugins[mn] = foo 5762306a36Sopenharmony_ci self.plugin_instances.append(foo.SubPlugin()) 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci def load_plugin(self, pgdir, pgname): 6062306a36Sopenharmony_ci pgname = pgname[0:-3] 6162306a36Sopenharmony_ci foo = importlib.import_module('{}.{}'.format(pgdir, pgname)) 6262306a36Sopenharmony_ci self.plugins[pgname] = foo 6362306a36Sopenharmony_ci self.plugin_instances.append(foo.SubPlugin()) 6462306a36Sopenharmony_ci self.plugin_instances[-1].check_args(self.args, None) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci def get_required_plugins(self, testlist): 6762306a36Sopenharmony_ci ''' 6862306a36Sopenharmony_ci Get all required plugins from the list of test cases and return 6962306a36Sopenharmony_ci all unique items. 7062306a36Sopenharmony_ci ''' 7162306a36Sopenharmony_ci reqs = [] 7262306a36Sopenharmony_ci for t in testlist: 7362306a36Sopenharmony_ci try: 7462306a36Sopenharmony_ci if 'requires' in t['plugins']: 7562306a36Sopenharmony_ci if isinstance(t['plugins']['requires'], list): 7662306a36Sopenharmony_ci reqs.extend(t['plugins']['requires']) 7762306a36Sopenharmony_ci else: 7862306a36Sopenharmony_ci reqs.append(t['plugins']['requires']) 7962306a36Sopenharmony_ci except KeyError: 8062306a36Sopenharmony_ci continue 8162306a36Sopenharmony_ci reqs = get_unique_item(reqs) 8262306a36Sopenharmony_ci return reqs 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci def load_required_plugins(self, reqs, parser, args, remaining): 8562306a36Sopenharmony_ci ''' 8662306a36Sopenharmony_ci Get all required plugins from the list of test cases and load any plugin 8762306a36Sopenharmony_ci that is not already enabled. 8862306a36Sopenharmony_ci ''' 8962306a36Sopenharmony_ci pgd = ['plugin-lib', 'plugin-lib-custom'] 9062306a36Sopenharmony_ci pnf = [] 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for r in reqs: 9362306a36Sopenharmony_ci if r not in self.plugins: 9462306a36Sopenharmony_ci fname = '{}.py'.format(r) 9562306a36Sopenharmony_ci source_path = [] 9662306a36Sopenharmony_ci for d in pgd: 9762306a36Sopenharmony_ci pgpath = '{}/{}'.format(d, fname) 9862306a36Sopenharmony_ci if os.path.isfile(pgpath): 9962306a36Sopenharmony_ci source_path.append(pgpath) 10062306a36Sopenharmony_ci if len(source_path) == 0: 10162306a36Sopenharmony_ci print('ERROR: unable to find required plugin {}'.format(r)) 10262306a36Sopenharmony_ci pnf.append(fname) 10362306a36Sopenharmony_ci continue 10462306a36Sopenharmony_ci elif len(source_path) > 1: 10562306a36Sopenharmony_ci print('WARNING: multiple copies of plugin {} found, using version found') 10662306a36Sopenharmony_ci print('at {}'.format(source_path[0])) 10762306a36Sopenharmony_ci pgdir = source_path[0] 10862306a36Sopenharmony_ci pgdir = pgdir.split('/')[0] 10962306a36Sopenharmony_ci self.load_plugin(pgdir, fname) 11062306a36Sopenharmony_ci if len(pnf) > 0: 11162306a36Sopenharmony_ci raise PluginDependencyException(pnf) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci parser = self.call_add_args(parser) 11462306a36Sopenharmony_ci (args, remaining) = parser.parse_known_args(args=remaining, namespace=args) 11562306a36Sopenharmony_ci return args 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci def call_pre_suite(self, testcount, testidlist): 11862306a36Sopenharmony_ci for pgn_inst in self.plugin_instances: 11962306a36Sopenharmony_ci pgn_inst.pre_suite(testcount, testidlist) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci def call_post_suite(self, index): 12262306a36Sopenharmony_ci for pgn_inst in reversed(self.plugin_instances): 12362306a36Sopenharmony_ci pgn_inst.post_suite(index) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci def call_pre_case(self, caseinfo, *, test_skip=False): 12662306a36Sopenharmony_ci for pgn_inst in self.plugin_instances: 12762306a36Sopenharmony_ci try: 12862306a36Sopenharmony_ci pgn_inst.pre_case(caseinfo, test_skip) 12962306a36Sopenharmony_ci except Exception as ee: 13062306a36Sopenharmony_ci print('exception {} in call to pre_case for {} plugin'. 13162306a36Sopenharmony_ci format(ee, pgn_inst.__class__)) 13262306a36Sopenharmony_ci print('test_ordinal is {}'.format(test_ordinal)) 13362306a36Sopenharmony_ci print('testid is {}'.format(caseinfo['id'])) 13462306a36Sopenharmony_ci raise 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci def call_post_case(self): 13762306a36Sopenharmony_ci for pgn_inst in reversed(self.plugin_instances): 13862306a36Sopenharmony_ci pgn_inst.post_case() 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci def call_pre_execute(self): 14162306a36Sopenharmony_ci for pgn_inst in self.plugin_instances: 14262306a36Sopenharmony_ci pgn_inst.pre_execute() 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci def call_post_execute(self): 14562306a36Sopenharmony_ci for pgn_inst in reversed(self.plugin_instances): 14662306a36Sopenharmony_ci pgn_inst.post_execute() 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci def call_add_args(self, parser): 14962306a36Sopenharmony_ci for pgn_inst in self.plugin_instances: 15062306a36Sopenharmony_ci parser = pgn_inst.add_args(parser) 15162306a36Sopenharmony_ci return parser 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci def call_check_args(self, args, remaining): 15462306a36Sopenharmony_ci for pgn_inst in self.plugin_instances: 15562306a36Sopenharmony_ci pgn_inst.check_args(args, remaining) 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci def call_adjust_command(self, stage, command): 15862306a36Sopenharmony_ci for pgn_inst in self.plugin_instances: 15962306a36Sopenharmony_ci command = pgn_inst.adjust_command(stage, command) 16062306a36Sopenharmony_ci return command 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci def set_args(self, args): 16362306a36Sopenharmony_ci self.args = args 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci @staticmethod 16662306a36Sopenharmony_ci def _make_argparser(args): 16762306a36Sopenharmony_ci self.argparser = argparse.ArgumentParser( 16862306a36Sopenharmony_ci description='Linux TC unit tests') 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cidef replace_keywords(cmd): 17162306a36Sopenharmony_ci """ 17262306a36Sopenharmony_ci For a given executable command, substitute any known 17362306a36Sopenharmony_ci variables contained within NAMES with the correct values 17462306a36Sopenharmony_ci """ 17562306a36Sopenharmony_ci tcmd = Template(cmd) 17662306a36Sopenharmony_ci subcmd = tcmd.safe_substitute(NAMES) 17762306a36Sopenharmony_ci return subcmd 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cidef exec_cmd(args, pm, stage, command): 18162306a36Sopenharmony_ci """ 18262306a36Sopenharmony_ci Perform any required modifications on an executable command, then run 18362306a36Sopenharmony_ci it in a subprocess and return the results. 18462306a36Sopenharmony_ci """ 18562306a36Sopenharmony_ci if len(command.strip()) == 0: 18662306a36Sopenharmony_ci return None, None 18762306a36Sopenharmony_ci if '$' in command: 18862306a36Sopenharmony_ci command = replace_keywords(command) 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci command = pm.call_adjust_command(stage, command) 19162306a36Sopenharmony_ci if args.verbose > 0: 19262306a36Sopenharmony_ci print('command "{}"'.format(command)) 19362306a36Sopenharmony_ci proc = subprocess.Popen(command, 19462306a36Sopenharmony_ci shell=True, 19562306a36Sopenharmony_ci stdout=subprocess.PIPE, 19662306a36Sopenharmony_ci stderr=subprocess.PIPE, 19762306a36Sopenharmony_ci env=ENVIR) 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci try: 20062306a36Sopenharmony_ci (rawout, serr) = proc.communicate(timeout=NAMES['TIMEOUT']) 20162306a36Sopenharmony_ci if proc.returncode != 0 and len(serr) > 0: 20262306a36Sopenharmony_ci foutput = serr.decode("utf-8", errors="ignore") 20362306a36Sopenharmony_ci else: 20462306a36Sopenharmony_ci foutput = rawout.decode("utf-8", errors="ignore") 20562306a36Sopenharmony_ci except subprocess.TimeoutExpired: 20662306a36Sopenharmony_ci foutput = "Command \"{}\" timed out\n".format(command) 20762306a36Sopenharmony_ci proc.returncode = 255 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci proc.stdout.close() 21062306a36Sopenharmony_ci proc.stderr.close() 21162306a36Sopenharmony_ci return proc, foutput 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cidef prepare_env(args, pm, stage, prefix, cmdlist, output = None): 21562306a36Sopenharmony_ci """ 21662306a36Sopenharmony_ci Execute the setup/teardown commands for a test case. 21762306a36Sopenharmony_ci Optionally terminate test execution if the command fails. 21862306a36Sopenharmony_ci """ 21962306a36Sopenharmony_ci if args.verbose > 0: 22062306a36Sopenharmony_ci print('{}'.format(prefix)) 22162306a36Sopenharmony_ci for cmdinfo in cmdlist: 22262306a36Sopenharmony_ci if isinstance(cmdinfo, list): 22362306a36Sopenharmony_ci exit_codes = cmdinfo[1:] 22462306a36Sopenharmony_ci cmd = cmdinfo[0] 22562306a36Sopenharmony_ci else: 22662306a36Sopenharmony_ci exit_codes = [0] 22762306a36Sopenharmony_ci cmd = cmdinfo 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if not cmd: 23062306a36Sopenharmony_ci continue 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci (proc, foutput) = exec_cmd(args, pm, stage, cmd) 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if proc and (proc.returncode not in exit_codes): 23562306a36Sopenharmony_ci print('', file=sys.stderr) 23662306a36Sopenharmony_ci print("{} *** Could not execute: \"{}\"".format(prefix, cmd), 23762306a36Sopenharmony_ci file=sys.stderr) 23862306a36Sopenharmony_ci print("\n{} *** Error message: \"{}\"".format(prefix, foutput), 23962306a36Sopenharmony_ci file=sys.stderr) 24062306a36Sopenharmony_ci print("returncode {}; expected {}".format(proc.returncode, 24162306a36Sopenharmony_ci exit_codes)) 24262306a36Sopenharmony_ci print("\n{} *** Aborting test run.".format(prefix), file=sys.stderr) 24362306a36Sopenharmony_ci print("\n\n{} *** stdout ***".format(proc.stdout), file=sys.stderr) 24462306a36Sopenharmony_ci print("\n\n{} *** stderr ***".format(proc.stderr), file=sys.stderr) 24562306a36Sopenharmony_ci raise PluginMgrTestFail( 24662306a36Sopenharmony_ci stage, output, 24762306a36Sopenharmony_ci '"{}" did not complete successfully'.format(prefix)) 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cidef verify_by_json(procout, res, tidx, args, pm): 25062306a36Sopenharmony_ci try: 25162306a36Sopenharmony_ci outputJSON = json.loads(procout) 25262306a36Sopenharmony_ci except json.JSONDecodeError: 25362306a36Sopenharmony_ci res.set_result(ResultState.fail) 25462306a36Sopenharmony_ci res.set_failmsg('Cannot decode verify command\'s output. Is it JSON?') 25562306a36Sopenharmony_ci return res 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci matchJSON = json.loads(json.dumps(tidx['matchJSON'])) 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if type(outputJSON) != type(matchJSON): 26062306a36Sopenharmony_ci failmsg = 'Original output and matchJSON value are not the same type: output: {} != matchJSON: {} ' 26162306a36Sopenharmony_ci failmsg = failmsg.format(type(outputJSON).__name__, type(matchJSON).__name__) 26262306a36Sopenharmony_ci res.set_result(ResultState.fail) 26362306a36Sopenharmony_ci res.set_failmsg(failmsg) 26462306a36Sopenharmony_ci return res 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if len(matchJSON) > len(outputJSON): 26762306a36Sopenharmony_ci failmsg = "Your matchJSON value is an array, and it contains more elements than the command under test\'s output:\ncommand output (length: {}):\n{}\nmatchJSON value (length: {}):\n{}" 26862306a36Sopenharmony_ci failmsg = failmsg.format(len(outputJSON), outputJSON, len(matchJSON), matchJSON) 26962306a36Sopenharmony_ci res.set_result(ResultState.fail) 27062306a36Sopenharmony_ci res.set_failmsg(failmsg) 27162306a36Sopenharmony_ci return res 27262306a36Sopenharmony_ci res = find_in_json(res, outputJSON, matchJSON, 0) 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return res 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cidef find_in_json(res, outputJSONVal, matchJSONVal, matchJSONKey=None): 27762306a36Sopenharmony_ci if res.get_result() == ResultState.fail: 27862306a36Sopenharmony_ci return res 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if type(matchJSONVal) == list: 28162306a36Sopenharmony_ci res = find_in_json_list(res, outputJSONVal, matchJSONVal, matchJSONKey) 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci elif type(matchJSONVal) == dict: 28462306a36Sopenharmony_ci res = find_in_json_dict(res, outputJSONVal, matchJSONVal) 28562306a36Sopenharmony_ci else: 28662306a36Sopenharmony_ci res = find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey) 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if res.get_result() != ResultState.fail: 28962306a36Sopenharmony_ci res.set_result(ResultState.success) 29062306a36Sopenharmony_ci return res 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return res 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cidef find_in_json_list(res, outputJSONVal, matchJSONVal, matchJSONKey=None): 29562306a36Sopenharmony_ci if (type(matchJSONVal) != type(outputJSONVal)): 29662306a36Sopenharmony_ci failmsg = 'Original output and matchJSON value are not the same type: output: {} != matchJSON: {}' 29762306a36Sopenharmony_ci failmsg = failmsg.format(outputJSONVal, matchJSONVal) 29862306a36Sopenharmony_ci res.set_result(ResultState.fail) 29962306a36Sopenharmony_ci res.set_failmsg(failmsg) 30062306a36Sopenharmony_ci return res 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if len(matchJSONVal) > len(outputJSONVal): 30362306a36Sopenharmony_ci failmsg = "Your matchJSON value is an array, and it contains more elements than the command under test\'s output:\ncommand output (length: {}):\n{}\nmatchJSON value (length: {}):\n{}" 30462306a36Sopenharmony_ci failmsg = failmsg.format(len(outputJSONVal), outputJSONVal, len(matchJSONVal), matchJSONVal) 30562306a36Sopenharmony_ci res.set_result(ResultState.fail) 30662306a36Sopenharmony_ci res.set_failmsg(failmsg) 30762306a36Sopenharmony_ci return res 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci for matchJSONIdx, matchJSONVal in enumerate(matchJSONVal): 31062306a36Sopenharmony_ci res = find_in_json(res, outputJSONVal[matchJSONIdx], matchJSONVal, 31162306a36Sopenharmony_ci matchJSONKey) 31262306a36Sopenharmony_ci return res 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cidef find_in_json_dict(res, outputJSONVal, matchJSONVal): 31562306a36Sopenharmony_ci for matchJSONKey, matchJSONVal in matchJSONVal.items(): 31662306a36Sopenharmony_ci if type(outputJSONVal) == dict: 31762306a36Sopenharmony_ci if matchJSONKey not in outputJSONVal: 31862306a36Sopenharmony_ci failmsg = 'Key not found in json output: {}: {}\nMatching against output: {}' 31962306a36Sopenharmony_ci failmsg = failmsg.format(matchJSONKey, matchJSONVal, outputJSONVal) 32062306a36Sopenharmony_ci res.set_result(ResultState.fail) 32162306a36Sopenharmony_ci res.set_failmsg(failmsg) 32262306a36Sopenharmony_ci return res 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci else: 32562306a36Sopenharmony_ci failmsg = 'Original output and matchJSON value are not the same type: output: {} != matchJSON: {}' 32662306a36Sopenharmony_ci failmsg = failmsg.format(type(outputJSON).__name__, type(matchJSON).__name__) 32762306a36Sopenharmony_ci res.set_result(ResultState.fail) 32862306a36Sopenharmony_ci res.set_failmsg(failmsg) 32962306a36Sopenharmony_ci return rest 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if type(outputJSONVal) == dict and (type(outputJSONVal[matchJSONKey]) == dict or 33262306a36Sopenharmony_ci type(outputJSONVal[matchJSONKey]) == list): 33362306a36Sopenharmony_ci if len(matchJSONVal) > 0: 33462306a36Sopenharmony_ci res = find_in_json(res, outputJSONVal[matchJSONKey], matchJSONVal, matchJSONKey) 33562306a36Sopenharmony_ci # handling corner case where matchJSONVal == [] or matchJSONVal == {} 33662306a36Sopenharmony_ci else: 33762306a36Sopenharmony_ci res = find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey) 33862306a36Sopenharmony_ci else: 33962306a36Sopenharmony_ci res = find_in_json(res, outputJSONVal, matchJSONVal, matchJSONKey) 34062306a36Sopenharmony_ci return res 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cidef find_in_json_other(res, outputJSONVal, matchJSONVal, matchJSONKey=None): 34362306a36Sopenharmony_ci if matchJSONKey in outputJSONVal: 34462306a36Sopenharmony_ci if matchJSONVal != outputJSONVal[matchJSONKey]: 34562306a36Sopenharmony_ci failmsg = 'Value doesn\'t match: {}: {} != {}\nMatching against output: {}' 34662306a36Sopenharmony_ci failmsg = failmsg.format(matchJSONKey, matchJSONVal, outputJSONVal[matchJSONKey], outputJSONVal) 34762306a36Sopenharmony_ci res.set_result(ResultState.fail) 34862306a36Sopenharmony_ci res.set_failmsg(failmsg) 34962306a36Sopenharmony_ci return res 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return res 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cidef run_one_test(pm, args, index, tidx): 35462306a36Sopenharmony_ci global NAMES 35562306a36Sopenharmony_ci result = True 35662306a36Sopenharmony_ci tresult = "" 35762306a36Sopenharmony_ci tap = "" 35862306a36Sopenharmony_ci res = TestResult(tidx['id'], tidx['name']) 35962306a36Sopenharmony_ci if args.verbose > 0: 36062306a36Sopenharmony_ci print("\t====================\n=====> ", end="") 36162306a36Sopenharmony_ci print("Test " + tidx["id"] + ": " + tidx["name"]) 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if 'skip' in tidx: 36462306a36Sopenharmony_ci if tidx['skip'] == 'yes': 36562306a36Sopenharmony_ci res = TestResult(tidx['id'], tidx['name']) 36662306a36Sopenharmony_ci res.set_result(ResultState.skip) 36762306a36Sopenharmony_ci res.set_errormsg('Test case designated as skipped.') 36862306a36Sopenharmony_ci pm.call_pre_case(tidx, test_skip=True) 36962306a36Sopenharmony_ci pm.call_post_execute() 37062306a36Sopenharmony_ci return res 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if 'dependsOn' in tidx: 37362306a36Sopenharmony_ci if (args.verbose > 0): 37462306a36Sopenharmony_ci print('probe command for test skip') 37562306a36Sopenharmony_ci (p, procout) = exec_cmd(args, pm, 'execute', tidx['dependsOn']) 37662306a36Sopenharmony_ci if p: 37762306a36Sopenharmony_ci if (p.returncode != 0): 37862306a36Sopenharmony_ci res = TestResult(tidx['id'], tidx['name']) 37962306a36Sopenharmony_ci res.set_result(ResultState.skip) 38062306a36Sopenharmony_ci res.set_errormsg('probe command: test skipped.') 38162306a36Sopenharmony_ci pm.call_pre_case(tidx, test_skip=True) 38262306a36Sopenharmony_ci pm.call_post_execute() 38362306a36Sopenharmony_ci return res 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci # populate NAMES with TESTID for this test 38662306a36Sopenharmony_ci NAMES['TESTID'] = tidx['id'] 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci pm.call_pre_case(tidx) 38962306a36Sopenharmony_ci prepare_env(args, pm, 'setup', "-----> prepare stage", tidx["setup"]) 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (args.verbose > 0): 39262306a36Sopenharmony_ci print('-----> execute stage') 39362306a36Sopenharmony_ci pm.call_pre_execute() 39462306a36Sopenharmony_ci (p, procout) = exec_cmd(args, pm, 'execute', tidx["cmdUnderTest"]) 39562306a36Sopenharmony_ci if p: 39662306a36Sopenharmony_ci exit_code = p.returncode 39762306a36Sopenharmony_ci else: 39862306a36Sopenharmony_ci exit_code = None 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci pm.call_post_execute() 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (exit_code is None or exit_code != int(tidx["expExitCode"])): 40362306a36Sopenharmony_ci print("exit: {!r}".format(exit_code)) 40462306a36Sopenharmony_ci print("exit: {}".format(int(tidx["expExitCode"]))) 40562306a36Sopenharmony_ci #print("exit: {!r} {}".format(exit_code, int(tidx["expExitCode"]))) 40662306a36Sopenharmony_ci res.set_result(ResultState.fail) 40762306a36Sopenharmony_ci res.set_failmsg('Command exited with {}, expected {}\n{}'.format(exit_code, tidx["expExitCode"], procout)) 40862306a36Sopenharmony_ci print(procout) 40962306a36Sopenharmony_ci else: 41062306a36Sopenharmony_ci if args.verbose > 0: 41162306a36Sopenharmony_ci print('-----> verify stage') 41262306a36Sopenharmony_ci (p, procout) = exec_cmd(args, pm, 'verify', tidx["verifyCmd"]) 41362306a36Sopenharmony_ci if procout: 41462306a36Sopenharmony_ci if 'matchJSON' in tidx: 41562306a36Sopenharmony_ci verify_by_json(procout, res, tidx, args, pm) 41662306a36Sopenharmony_ci elif 'matchPattern' in tidx: 41762306a36Sopenharmony_ci match_pattern = re.compile( 41862306a36Sopenharmony_ci str(tidx["matchPattern"]), re.DOTALL | re.MULTILINE) 41962306a36Sopenharmony_ci match_index = re.findall(match_pattern, procout) 42062306a36Sopenharmony_ci if len(match_index) != int(tidx["matchCount"]): 42162306a36Sopenharmony_ci res.set_result(ResultState.fail) 42262306a36Sopenharmony_ci res.set_failmsg('Could not match regex pattern. Verify command output:\n{}'.format(procout)) 42362306a36Sopenharmony_ci else: 42462306a36Sopenharmony_ci res.set_result(ResultState.success) 42562306a36Sopenharmony_ci else: 42662306a36Sopenharmony_ci res.set_result(ResultState.fail) 42762306a36Sopenharmony_ci res.set_failmsg('Must specify a match option: matchJSON or matchPattern\n{}'.format(procout)) 42862306a36Sopenharmony_ci elif int(tidx["matchCount"]) != 0: 42962306a36Sopenharmony_ci res.set_result(ResultState.fail) 43062306a36Sopenharmony_ci res.set_failmsg('No output generated by verify command.') 43162306a36Sopenharmony_ci else: 43262306a36Sopenharmony_ci res.set_result(ResultState.success) 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci prepare_env(args, pm, 'teardown', '-----> teardown stage', tidx['teardown'], procout) 43562306a36Sopenharmony_ci pm.call_post_case() 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci index += 1 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci # remove TESTID from NAMES 44062306a36Sopenharmony_ci del(NAMES['TESTID']) 44162306a36Sopenharmony_ci return res 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cidef test_runner(pm, args, filtered_tests): 44462306a36Sopenharmony_ci """ 44562306a36Sopenharmony_ci Driver function for the unit tests. 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci Prints information about the tests being run, executes the setup and 44862306a36Sopenharmony_ci teardown commands and the command under test itself. Also determines 44962306a36Sopenharmony_ci success/failure based on the information in the test case and generates 45062306a36Sopenharmony_ci TAP output accordingly. 45162306a36Sopenharmony_ci """ 45262306a36Sopenharmony_ci testlist = filtered_tests 45362306a36Sopenharmony_ci tcount = len(testlist) 45462306a36Sopenharmony_ci index = 1 45562306a36Sopenharmony_ci tap = '' 45662306a36Sopenharmony_ci badtest = None 45762306a36Sopenharmony_ci stage = None 45862306a36Sopenharmony_ci emergency_exit = False 45962306a36Sopenharmony_ci emergency_exit_message = '' 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci tsr = TestSuiteReport() 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci try: 46462306a36Sopenharmony_ci pm.call_pre_suite(tcount, [tidx['id'] for tidx in testlist]) 46562306a36Sopenharmony_ci except Exception as ee: 46662306a36Sopenharmony_ci ex_type, ex, ex_tb = sys.exc_info() 46762306a36Sopenharmony_ci print('Exception {} {} (caught in pre_suite).'. 46862306a36Sopenharmony_ci format(ex_type, ex)) 46962306a36Sopenharmony_ci traceback.print_tb(ex_tb) 47062306a36Sopenharmony_ci emergency_exit_message = 'EMERGENCY EXIT, call_pre_suite failed with exception {} {}\n'.format(ex_type, ex) 47162306a36Sopenharmony_ci emergency_exit = True 47262306a36Sopenharmony_ci stage = 'pre-SUITE' 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if emergency_exit: 47562306a36Sopenharmony_ci pm.call_post_suite(index) 47662306a36Sopenharmony_ci return emergency_exit_message 47762306a36Sopenharmony_ci if args.verbose > 1: 47862306a36Sopenharmony_ci print('give test rig 2 seconds to stabilize') 47962306a36Sopenharmony_ci time.sleep(2) 48062306a36Sopenharmony_ci for tidx in testlist: 48162306a36Sopenharmony_ci if "flower" in tidx["category"] and args.device == None: 48262306a36Sopenharmony_ci errmsg = "Tests using the DEV2 variable must define the name of a " 48362306a36Sopenharmony_ci errmsg += "physical NIC with the -d option when running tdc.\n" 48462306a36Sopenharmony_ci errmsg += "Test has been skipped." 48562306a36Sopenharmony_ci if args.verbose > 1: 48662306a36Sopenharmony_ci print(errmsg) 48762306a36Sopenharmony_ci res = TestResult(tidx['id'], tidx['name']) 48862306a36Sopenharmony_ci res.set_result(ResultState.skip) 48962306a36Sopenharmony_ci res.set_errormsg(errmsg) 49062306a36Sopenharmony_ci tsr.add_resultdata(res) 49162306a36Sopenharmony_ci index += 1 49262306a36Sopenharmony_ci continue 49362306a36Sopenharmony_ci try: 49462306a36Sopenharmony_ci badtest = tidx # in case it goes bad 49562306a36Sopenharmony_ci res = run_one_test(pm, args, index, tidx) 49662306a36Sopenharmony_ci tsr.add_resultdata(res) 49762306a36Sopenharmony_ci except PluginMgrTestFail as pmtf: 49862306a36Sopenharmony_ci ex_type, ex, ex_tb = sys.exc_info() 49962306a36Sopenharmony_ci stage = pmtf.stage 50062306a36Sopenharmony_ci message = pmtf.message 50162306a36Sopenharmony_ci output = pmtf.output 50262306a36Sopenharmony_ci res = TestResult(tidx['id'], tidx['name']) 50362306a36Sopenharmony_ci res.set_result(ResultState.skip) 50462306a36Sopenharmony_ci res.set_errormsg(pmtf.message) 50562306a36Sopenharmony_ci res.set_failmsg(pmtf.output) 50662306a36Sopenharmony_ci tsr.add_resultdata(res) 50762306a36Sopenharmony_ci index += 1 50862306a36Sopenharmony_ci print(message) 50962306a36Sopenharmony_ci print('Exception {} {} (caught in test_runner, running test {} {} {} stage {})'. 51062306a36Sopenharmony_ci format(ex_type, ex, index, tidx['id'], tidx['name'], stage)) 51162306a36Sopenharmony_ci print('---------------') 51262306a36Sopenharmony_ci print('traceback') 51362306a36Sopenharmony_ci traceback.print_tb(ex_tb) 51462306a36Sopenharmony_ci print('---------------') 51562306a36Sopenharmony_ci if stage == 'teardown': 51662306a36Sopenharmony_ci print('accumulated output for this test:') 51762306a36Sopenharmony_ci if pmtf.output: 51862306a36Sopenharmony_ci print(pmtf.output) 51962306a36Sopenharmony_ci print('---------------') 52062306a36Sopenharmony_ci break 52162306a36Sopenharmony_ci index += 1 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci # if we failed in setup or teardown, 52462306a36Sopenharmony_ci # fill in the remaining tests with ok-skipped 52562306a36Sopenharmony_ci count = index 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if tcount + 1 != count: 52862306a36Sopenharmony_ci for tidx in testlist[count - 1:]: 52962306a36Sopenharmony_ci res = TestResult(tidx['id'], tidx['name']) 53062306a36Sopenharmony_ci res.set_result(ResultState.skip) 53162306a36Sopenharmony_ci msg = 'skipped - previous {} failed {} {}'.format(stage, 53262306a36Sopenharmony_ci index, badtest.get('id', '--Unknown--')) 53362306a36Sopenharmony_ci res.set_errormsg(msg) 53462306a36Sopenharmony_ci tsr.add_resultdata(res) 53562306a36Sopenharmony_ci count += 1 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if args.pause: 53862306a36Sopenharmony_ci print('Want to pause\nPress enter to continue ...') 53962306a36Sopenharmony_ci if input(sys.stdin): 54062306a36Sopenharmony_ci print('got something on stdin') 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci pm.call_post_suite(index) 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return tsr 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cidef has_blank_ids(idlist): 54762306a36Sopenharmony_ci """ 54862306a36Sopenharmony_ci Search the list for empty ID fields and return true/false accordingly. 54962306a36Sopenharmony_ci """ 55062306a36Sopenharmony_ci return not(all(k for k in idlist)) 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cidef load_from_file(filename): 55462306a36Sopenharmony_ci """ 55562306a36Sopenharmony_ci Open the JSON file containing the test cases and return them 55662306a36Sopenharmony_ci as list of ordered dictionary objects. 55762306a36Sopenharmony_ci """ 55862306a36Sopenharmony_ci try: 55962306a36Sopenharmony_ci with open(filename) as test_data: 56062306a36Sopenharmony_ci testlist = json.load(test_data, object_pairs_hook=OrderedDict) 56162306a36Sopenharmony_ci except json.JSONDecodeError as jde: 56262306a36Sopenharmony_ci print('IGNORING test case file {}\n\tBECAUSE: {}'.format(filename, jde)) 56362306a36Sopenharmony_ci testlist = list() 56462306a36Sopenharmony_ci else: 56562306a36Sopenharmony_ci idlist = get_id_list(testlist) 56662306a36Sopenharmony_ci if (has_blank_ids(idlist)): 56762306a36Sopenharmony_ci for k in testlist: 56862306a36Sopenharmony_ci k['filename'] = filename 56962306a36Sopenharmony_ci return testlist 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cidef args_parse(): 57362306a36Sopenharmony_ci """ 57462306a36Sopenharmony_ci Create the argument parser. 57562306a36Sopenharmony_ci """ 57662306a36Sopenharmony_ci parser = argparse.ArgumentParser(description='Linux TC unit tests') 57762306a36Sopenharmony_ci return parser 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cidef set_args(parser): 58162306a36Sopenharmony_ci """ 58262306a36Sopenharmony_ci Set the command line arguments for tdc. 58362306a36Sopenharmony_ci """ 58462306a36Sopenharmony_ci parser.add_argument( 58562306a36Sopenharmony_ci '--outfile', type=str, 58662306a36Sopenharmony_ci help='Path to the file in which results should be saved. ' + 58762306a36Sopenharmony_ci 'Default target is the current directory.') 58862306a36Sopenharmony_ci parser.add_argument( 58962306a36Sopenharmony_ci '-p', '--path', type=str, 59062306a36Sopenharmony_ci help='The full path to the tc executable to use') 59162306a36Sopenharmony_ci sg = parser.add_argument_group( 59262306a36Sopenharmony_ci 'selection', 'select which test cases: ' + 59362306a36Sopenharmony_ci 'files plus directories; filtered by categories plus testids') 59462306a36Sopenharmony_ci ag = parser.add_argument_group( 59562306a36Sopenharmony_ci 'action', 'select action to perform on selected test cases') 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci sg.add_argument( 59862306a36Sopenharmony_ci '-D', '--directory', nargs='+', metavar='DIR', 59962306a36Sopenharmony_ci help='Collect tests from the specified directory(ies) ' + 60062306a36Sopenharmony_ci '(default [tc-tests])') 60162306a36Sopenharmony_ci sg.add_argument( 60262306a36Sopenharmony_ci '-f', '--file', nargs='+', metavar='FILE', 60362306a36Sopenharmony_ci help='Run tests from the specified file(s)') 60462306a36Sopenharmony_ci sg.add_argument( 60562306a36Sopenharmony_ci '-c', '--category', nargs='*', metavar='CATG', default=['+c'], 60662306a36Sopenharmony_ci help='Run tests only from the specified category/ies, ' + 60762306a36Sopenharmony_ci 'or if no category/ies is/are specified, list known categories.') 60862306a36Sopenharmony_ci sg.add_argument( 60962306a36Sopenharmony_ci '-e', '--execute', nargs='+', metavar='ID', 61062306a36Sopenharmony_ci help='Execute the specified test cases with specified IDs') 61162306a36Sopenharmony_ci ag.add_argument( 61262306a36Sopenharmony_ci '-l', '--list', action='store_true', 61362306a36Sopenharmony_ci help='List all test cases, or those only within the specified category') 61462306a36Sopenharmony_ci ag.add_argument( 61562306a36Sopenharmony_ci '-s', '--show', action='store_true', dest='showID', 61662306a36Sopenharmony_ci help='Display the selected test cases') 61762306a36Sopenharmony_ci ag.add_argument( 61862306a36Sopenharmony_ci '-i', '--id', action='store_true', dest='gen_id', 61962306a36Sopenharmony_ci help='Generate ID numbers for new test cases') 62062306a36Sopenharmony_ci parser.add_argument( 62162306a36Sopenharmony_ci '-v', '--verbose', action='count', default=0, 62262306a36Sopenharmony_ci help='Show the commands that are being run') 62362306a36Sopenharmony_ci parser.add_argument( 62462306a36Sopenharmony_ci '--format', default='tap', const='tap', nargs='?', 62562306a36Sopenharmony_ci choices=['none', 'xunit', 'tap'], 62662306a36Sopenharmony_ci help='Specify the format for test results. (Default: TAP)') 62762306a36Sopenharmony_ci parser.add_argument('-d', '--device', 62862306a36Sopenharmony_ci help='Execute test cases that use a physical device, ' + 62962306a36Sopenharmony_ci 'where DEVICE is its name. (If not defined, tests ' + 63062306a36Sopenharmony_ci 'that require a physical device will be skipped)') 63162306a36Sopenharmony_ci parser.add_argument( 63262306a36Sopenharmony_ci '-P', '--pause', action='store_true', 63362306a36Sopenharmony_ci help='Pause execution just before post-suite stage') 63462306a36Sopenharmony_ci return parser 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cidef check_default_settings(args, remaining, pm): 63862306a36Sopenharmony_ci """ 63962306a36Sopenharmony_ci Process any arguments overriding the default settings, 64062306a36Sopenharmony_ci and ensure the settings are correct. 64162306a36Sopenharmony_ci """ 64262306a36Sopenharmony_ci # Allow for overriding specific settings 64362306a36Sopenharmony_ci global NAMES 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if args.path != None: 64662306a36Sopenharmony_ci NAMES['TC'] = args.path 64762306a36Sopenharmony_ci if args.device != None: 64862306a36Sopenharmony_ci NAMES['DEV2'] = args.device 64962306a36Sopenharmony_ci if 'TIMEOUT' not in NAMES: 65062306a36Sopenharmony_ci NAMES['TIMEOUT'] = None 65162306a36Sopenharmony_ci if not os.path.isfile(NAMES['TC']): 65262306a36Sopenharmony_ci print("The specified tc path " + NAMES['TC'] + " does not exist.") 65362306a36Sopenharmony_ci exit(1) 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci pm.call_check_args(args, remaining) 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cidef get_id_list(alltests): 65962306a36Sopenharmony_ci """ 66062306a36Sopenharmony_ci Generate a list of all IDs in the test cases. 66162306a36Sopenharmony_ci """ 66262306a36Sopenharmony_ci return [x["id"] for x in alltests] 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cidef check_case_id(alltests): 66662306a36Sopenharmony_ci """ 66762306a36Sopenharmony_ci Check for duplicate test case IDs. 66862306a36Sopenharmony_ci """ 66962306a36Sopenharmony_ci idl = get_id_list(alltests) 67062306a36Sopenharmony_ci return [x for x in idl if idl.count(x) > 1] 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cidef does_id_exist(alltests, newid): 67462306a36Sopenharmony_ci """ 67562306a36Sopenharmony_ci Check if a given ID already exists in the list of test cases. 67662306a36Sopenharmony_ci """ 67762306a36Sopenharmony_ci idl = get_id_list(alltests) 67862306a36Sopenharmony_ci return (any(newid == x for x in idl)) 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cidef generate_case_ids(alltests): 68262306a36Sopenharmony_ci """ 68362306a36Sopenharmony_ci If a test case has a blank ID field, generate a random hex ID for it 68462306a36Sopenharmony_ci and then write the test cases back to disk. 68562306a36Sopenharmony_ci """ 68662306a36Sopenharmony_ci import random 68762306a36Sopenharmony_ci for c in alltests: 68862306a36Sopenharmony_ci if (c["id"] == ""): 68962306a36Sopenharmony_ci while True: 69062306a36Sopenharmony_ci newid = str('{:04x}'.format(random.randrange(16**4))) 69162306a36Sopenharmony_ci if (does_id_exist(alltests, newid)): 69262306a36Sopenharmony_ci continue 69362306a36Sopenharmony_ci else: 69462306a36Sopenharmony_ci c['id'] = newid 69562306a36Sopenharmony_ci break 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci ufilename = [] 69862306a36Sopenharmony_ci for c in alltests: 69962306a36Sopenharmony_ci if ('filename' in c): 70062306a36Sopenharmony_ci ufilename.append(c['filename']) 70162306a36Sopenharmony_ci ufilename = get_unique_item(ufilename) 70262306a36Sopenharmony_ci for f in ufilename: 70362306a36Sopenharmony_ci testlist = [] 70462306a36Sopenharmony_ci for t in alltests: 70562306a36Sopenharmony_ci if 'filename' in t: 70662306a36Sopenharmony_ci if t['filename'] == f: 70762306a36Sopenharmony_ci del t['filename'] 70862306a36Sopenharmony_ci testlist.append(t) 70962306a36Sopenharmony_ci outfile = open(f, "w") 71062306a36Sopenharmony_ci json.dump(testlist, outfile, indent=4) 71162306a36Sopenharmony_ci outfile.write("\n") 71262306a36Sopenharmony_ci outfile.close() 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cidef filter_tests_by_id(args, testlist): 71562306a36Sopenharmony_ci ''' 71662306a36Sopenharmony_ci Remove tests from testlist that are not in the named id list. 71762306a36Sopenharmony_ci If id list is empty, return empty list. 71862306a36Sopenharmony_ci ''' 71962306a36Sopenharmony_ci newlist = list() 72062306a36Sopenharmony_ci if testlist and args.execute: 72162306a36Sopenharmony_ci target_ids = args.execute 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if isinstance(target_ids, list) and (len(target_ids) > 0): 72462306a36Sopenharmony_ci newlist = list(filter(lambda x: x['id'] in target_ids, testlist)) 72562306a36Sopenharmony_ci return newlist 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cidef filter_tests_by_category(args, testlist): 72862306a36Sopenharmony_ci ''' 72962306a36Sopenharmony_ci Remove tests from testlist that are not in a named category. 73062306a36Sopenharmony_ci ''' 73162306a36Sopenharmony_ci answer = list() 73262306a36Sopenharmony_ci if args.category and testlist: 73362306a36Sopenharmony_ci test_ids = list() 73462306a36Sopenharmony_ci for catg in set(args.category): 73562306a36Sopenharmony_ci if catg == '+c': 73662306a36Sopenharmony_ci continue 73762306a36Sopenharmony_ci print('considering category {}'.format(catg)) 73862306a36Sopenharmony_ci for tc in testlist: 73962306a36Sopenharmony_ci if catg in tc['category'] and tc['id'] not in test_ids: 74062306a36Sopenharmony_ci answer.append(tc) 74162306a36Sopenharmony_ci test_ids.append(tc['id']) 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci return answer 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cidef get_test_cases(args): 74762306a36Sopenharmony_ci """ 74862306a36Sopenharmony_ci If a test case file is specified, retrieve tests from that file. 74962306a36Sopenharmony_ci Otherwise, glob for all json files in subdirectories and load from 75062306a36Sopenharmony_ci each one. 75162306a36Sopenharmony_ci Also, if requested, filter by category, and add tests matching 75262306a36Sopenharmony_ci certain ids. 75362306a36Sopenharmony_ci """ 75462306a36Sopenharmony_ci import fnmatch 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci flist = [] 75762306a36Sopenharmony_ci testdirs = ['tc-tests'] 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if args.file: 76062306a36Sopenharmony_ci # at least one file was specified - remove the default directory 76162306a36Sopenharmony_ci testdirs = [] 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci for ff in args.file: 76462306a36Sopenharmony_ci if not os.path.isfile(ff): 76562306a36Sopenharmony_ci print("IGNORING file " + ff + "\n\tBECAUSE does not exist.") 76662306a36Sopenharmony_ci else: 76762306a36Sopenharmony_ci flist.append(os.path.abspath(ff)) 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if args.directory: 77062306a36Sopenharmony_ci testdirs = args.directory 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci for testdir in testdirs: 77362306a36Sopenharmony_ci for root, dirnames, filenames in os.walk(testdir): 77462306a36Sopenharmony_ci for filename in fnmatch.filter(filenames, '*.json'): 77562306a36Sopenharmony_ci candidate = os.path.abspath(os.path.join(root, filename)) 77662306a36Sopenharmony_ci if candidate not in testdirs: 77762306a36Sopenharmony_ci flist.append(candidate) 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci alltestcases = list() 78062306a36Sopenharmony_ci for casefile in flist: 78162306a36Sopenharmony_ci alltestcases = alltestcases + (load_from_file(casefile)) 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci allcatlist = get_test_categories(alltestcases) 78462306a36Sopenharmony_ci allidlist = get_id_list(alltestcases) 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci testcases_by_cats = get_categorized_testlist(alltestcases, allcatlist) 78762306a36Sopenharmony_ci idtestcases = filter_tests_by_id(args, alltestcases) 78862306a36Sopenharmony_ci cattestcases = filter_tests_by_category(args, alltestcases) 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci cat_ids = [x['id'] for x in cattestcases] 79162306a36Sopenharmony_ci if args.execute: 79262306a36Sopenharmony_ci if args.category: 79362306a36Sopenharmony_ci alltestcases = cattestcases + [x for x in idtestcases if x['id'] not in cat_ids] 79462306a36Sopenharmony_ci else: 79562306a36Sopenharmony_ci alltestcases = idtestcases 79662306a36Sopenharmony_ci else: 79762306a36Sopenharmony_ci if cat_ids: 79862306a36Sopenharmony_ci alltestcases = cattestcases 79962306a36Sopenharmony_ci else: 80062306a36Sopenharmony_ci # just accept the existing value of alltestcases, 80162306a36Sopenharmony_ci # which has been filtered by file/directory 80262306a36Sopenharmony_ci pass 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci return allcatlist, allidlist, testcases_by_cats, alltestcases 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_cidef set_operation_mode(pm, parser, args, remaining): 80862306a36Sopenharmony_ci """ 80962306a36Sopenharmony_ci Load the test case data and process remaining arguments to determine 81062306a36Sopenharmony_ci what the script should do for this run, and call the appropriate 81162306a36Sopenharmony_ci function. 81262306a36Sopenharmony_ci """ 81362306a36Sopenharmony_ci ucat, idlist, testcases, alltests = get_test_cases(args) 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if args.gen_id: 81662306a36Sopenharmony_ci if (has_blank_ids(idlist)): 81762306a36Sopenharmony_ci alltests = generate_case_ids(alltests) 81862306a36Sopenharmony_ci else: 81962306a36Sopenharmony_ci print("No empty ID fields found in test files.") 82062306a36Sopenharmony_ci exit(0) 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci duplicate_ids = check_case_id(alltests) 82362306a36Sopenharmony_ci if (len(duplicate_ids) > 0): 82462306a36Sopenharmony_ci print("The following test case IDs are not unique:") 82562306a36Sopenharmony_ci print(str(set(duplicate_ids))) 82662306a36Sopenharmony_ci print("Please correct them before continuing.") 82762306a36Sopenharmony_ci exit(1) 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if args.showID: 83062306a36Sopenharmony_ci for atest in alltests: 83162306a36Sopenharmony_ci print_test_case(atest) 83262306a36Sopenharmony_ci exit(0) 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if isinstance(args.category, list) and (len(args.category) == 0): 83562306a36Sopenharmony_ci print("Available categories:") 83662306a36Sopenharmony_ci print_sll(ucat) 83762306a36Sopenharmony_ci exit(0) 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if args.list: 84062306a36Sopenharmony_ci list_test_cases(alltests) 84162306a36Sopenharmony_ci exit(0) 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci exit_code = 0 # KSFT_PASS 84462306a36Sopenharmony_ci if len(alltests): 84562306a36Sopenharmony_ci req_plugins = pm.get_required_plugins(alltests) 84662306a36Sopenharmony_ci try: 84762306a36Sopenharmony_ci args = pm.load_required_plugins(req_plugins, parser, args, remaining) 84862306a36Sopenharmony_ci except PluginDependencyException as pde: 84962306a36Sopenharmony_ci print('The following plugins were not found:') 85062306a36Sopenharmony_ci print('{}'.format(pde.missing_pg)) 85162306a36Sopenharmony_ci catresults = test_runner(pm, args, alltests) 85262306a36Sopenharmony_ci if catresults.count_failures() != 0: 85362306a36Sopenharmony_ci exit_code = 1 # KSFT_FAIL 85462306a36Sopenharmony_ci if args.format == 'none': 85562306a36Sopenharmony_ci print('Test results output suppression requested\n') 85662306a36Sopenharmony_ci else: 85762306a36Sopenharmony_ci print('\nAll test results: \n') 85862306a36Sopenharmony_ci if args.format == 'xunit': 85962306a36Sopenharmony_ci suffix = 'xml' 86062306a36Sopenharmony_ci res = catresults.format_xunit() 86162306a36Sopenharmony_ci elif args.format == 'tap': 86262306a36Sopenharmony_ci suffix = 'tap' 86362306a36Sopenharmony_ci res = catresults.format_tap() 86462306a36Sopenharmony_ci print(res) 86562306a36Sopenharmony_ci print('\n\n') 86662306a36Sopenharmony_ci if not args.outfile: 86762306a36Sopenharmony_ci fname = 'test-results.{}'.format(suffix) 86862306a36Sopenharmony_ci else: 86962306a36Sopenharmony_ci fname = args.outfile 87062306a36Sopenharmony_ci with open(fname, 'w') as fh: 87162306a36Sopenharmony_ci fh.write(res) 87262306a36Sopenharmony_ci fh.close() 87362306a36Sopenharmony_ci if os.getenv('SUDO_UID') is not None: 87462306a36Sopenharmony_ci os.chown(fname, uid=int(os.getenv('SUDO_UID')), 87562306a36Sopenharmony_ci gid=int(os.getenv('SUDO_GID'))) 87662306a36Sopenharmony_ci else: 87762306a36Sopenharmony_ci print('No tests found\n') 87862306a36Sopenharmony_ci exit_code = 4 # KSFT_SKIP 87962306a36Sopenharmony_ci exit(exit_code) 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cidef main(): 88262306a36Sopenharmony_ci """ 88362306a36Sopenharmony_ci Start of execution; set up argument parser and get the arguments, 88462306a36Sopenharmony_ci and start operations. 88562306a36Sopenharmony_ci """ 88662306a36Sopenharmony_ci parser = args_parse() 88762306a36Sopenharmony_ci parser = set_args(parser) 88862306a36Sopenharmony_ci pm = PluginMgr(parser) 88962306a36Sopenharmony_ci parser = pm.call_add_args(parser) 89062306a36Sopenharmony_ci (args, remaining) = parser.parse_known_args() 89162306a36Sopenharmony_ci args.NAMES = NAMES 89262306a36Sopenharmony_ci pm.set_args(args) 89362306a36Sopenharmony_ci check_default_settings(args, remaining, pm) 89462306a36Sopenharmony_ci if args.verbose > 2: 89562306a36Sopenharmony_ci print('args is {}'.format(args)) 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci set_operation_mode(pm, parser, args, remaining) 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ciif __name__ == "__main__": 90062306a36Sopenharmony_ci main() 901