119ea8026Sopenharmony_ci#!/usr/bin/env python3 219ea8026Sopenharmony_ci# 319ea8026Sopenharmony_ci# Script to compile and runs tests. 419ea8026Sopenharmony_ci# 519ea8026Sopenharmony_ci# Example: 619ea8026Sopenharmony_ci# ./scripts/test.py runners/test_runner -b 719ea8026Sopenharmony_ci# 819ea8026Sopenharmony_ci# Copyright (c) 2022, The littlefs authors. 919ea8026Sopenharmony_ci# SPDX-License-Identifier: BSD-3-Clause 1019ea8026Sopenharmony_ci# 1119ea8026Sopenharmony_ci 1219ea8026Sopenharmony_ciimport collections as co 1319ea8026Sopenharmony_ciimport csv 1419ea8026Sopenharmony_ciimport errno 1519ea8026Sopenharmony_ciimport glob 1619ea8026Sopenharmony_ciimport itertools as it 1719ea8026Sopenharmony_ciimport math as m 1819ea8026Sopenharmony_ciimport os 1919ea8026Sopenharmony_ciimport pty 2019ea8026Sopenharmony_ciimport re 2119ea8026Sopenharmony_ciimport shlex 2219ea8026Sopenharmony_ciimport shutil 2319ea8026Sopenharmony_ciimport signal 2419ea8026Sopenharmony_ciimport subprocess as sp 2519ea8026Sopenharmony_ciimport threading as th 2619ea8026Sopenharmony_ciimport time 2719ea8026Sopenharmony_ciimport toml 2819ea8026Sopenharmony_ci 2919ea8026Sopenharmony_ci 3019ea8026Sopenharmony_ciRUNNER_PATH = './runners/test_runner' 3119ea8026Sopenharmony_ciHEADER_PATH = 'runners/test_runner.h' 3219ea8026Sopenharmony_ci 3319ea8026Sopenharmony_ciGDB_PATH = ['gdb'] 3419ea8026Sopenharmony_ciVALGRIND_PATH = ['valgrind'] 3519ea8026Sopenharmony_ciPERF_SCRIPT = ['./scripts/perf.py'] 3619ea8026Sopenharmony_ci 3719ea8026Sopenharmony_ci 3819ea8026Sopenharmony_cidef openio(path, mode='r', buffering=-1): 3919ea8026Sopenharmony_ci # allow '-' for stdin/stdout 4019ea8026Sopenharmony_ci if path == '-': 4119ea8026Sopenharmony_ci if mode == 'r': 4219ea8026Sopenharmony_ci return os.fdopen(os.dup(sys.stdin.fileno()), mode, buffering) 4319ea8026Sopenharmony_ci else: 4419ea8026Sopenharmony_ci return os.fdopen(os.dup(sys.stdout.fileno()), mode, buffering) 4519ea8026Sopenharmony_ci else: 4619ea8026Sopenharmony_ci return open(path, mode, buffering) 4719ea8026Sopenharmony_ci 4819ea8026Sopenharmony_ciclass TestCase: 4919ea8026Sopenharmony_ci # create a TestCase object from a config 5019ea8026Sopenharmony_ci def __init__(self, config, args={}): 5119ea8026Sopenharmony_ci self.name = config.pop('name') 5219ea8026Sopenharmony_ci self.path = config.pop('path') 5319ea8026Sopenharmony_ci self.suite = config.pop('suite') 5419ea8026Sopenharmony_ci self.lineno = config.pop('lineno', None) 5519ea8026Sopenharmony_ci self.if_ = config.pop('if', None) 5619ea8026Sopenharmony_ci if isinstance(self.if_, bool): 5719ea8026Sopenharmony_ci self.if_ = 'true' if self.if_ else 'false' 5819ea8026Sopenharmony_ci self.code = config.pop('code') 5919ea8026Sopenharmony_ci self.code_lineno = config.pop('code_lineno', None) 6019ea8026Sopenharmony_ci self.in_ = config.pop('in', 6119ea8026Sopenharmony_ci config.pop('suite_in', None)) 6219ea8026Sopenharmony_ci 6319ea8026Sopenharmony_ci self.reentrant = config.pop('reentrant', 6419ea8026Sopenharmony_ci config.pop('suite_reentrant', False)) 6519ea8026Sopenharmony_ci 6619ea8026Sopenharmony_ci # figure out defines and build possible permutations 6719ea8026Sopenharmony_ci self.defines = set() 6819ea8026Sopenharmony_ci self.permutations = [] 6919ea8026Sopenharmony_ci 7019ea8026Sopenharmony_ci # defines can be a dict or a list or dicts 7119ea8026Sopenharmony_ci suite_defines = config.pop('suite_defines', {}) 7219ea8026Sopenharmony_ci if not isinstance(suite_defines, list): 7319ea8026Sopenharmony_ci suite_defines = [suite_defines] 7419ea8026Sopenharmony_ci defines = config.pop('defines', {}) 7519ea8026Sopenharmony_ci if not isinstance(defines, list): 7619ea8026Sopenharmony_ci defines = [defines] 7719ea8026Sopenharmony_ci 7819ea8026Sopenharmony_ci def csplit(v): 7919ea8026Sopenharmony_ci # split commas but only outside of parens 8019ea8026Sopenharmony_ci parens = 0 8119ea8026Sopenharmony_ci i_ = 0 8219ea8026Sopenharmony_ci for i in range(len(v)): 8319ea8026Sopenharmony_ci if v[i] == ',' and parens == 0: 8419ea8026Sopenharmony_ci yield v[i_:i] 8519ea8026Sopenharmony_ci i_ = i+1 8619ea8026Sopenharmony_ci elif v[i] in '([{': 8719ea8026Sopenharmony_ci parens += 1 8819ea8026Sopenharmony_ci elif v[i] in '}])': 8919ea8026Sopenharmony_ci parens -= 1 9019ea8026Sopenharmony_ci if v[i_:].strip(): 9119ea8026Sopenharmony_ci yield v[i_:] 9219ea8026Sopenharmony_ci 9319ea8026Sopenharmony_ci def parse_define(v): 9419ea8026Sopenharmony_ci # a define entry can be a list 9519ea8026Sopenharmony_ci if isinstance(v, list): 9619ea8026Sopenharmony_ci for v_ in v: 9719ea8026Sopenharmony_ci yield from parse_define(v_) 9819ea8026Sopenharmony_ci # or a string 9919ea8026Sopenharmony_ci elif isinstance(v, str): 10019ea8026Sopenharmony_ci # which can be comma-separated values, with optional 10119ea8026Sopenharmony_ci # range statements. This matches the runtime define parser in 10219ea8026Sopenharmony_ci # the runner itself. 10319ea8026Sopenharmony_ci for v_ in csplit(v): 10419ea8026Sopenharmony_ci m = re.search(r'\brange\b\s*\(' 10519ea8026Sopenharmony_ci '(?P<start>[^,\s]*)' 10619ea8026Sopenharmony_ci '\s*(?:,\s*(?P<stop>[^,\s]*)' 10719ea8026Sopenharmony_ci '\s*(?:,\s*(?P<step>[^,\s]*)\s*)?)?\)', 10819ea8026Sopenharmony_ci v_) 10919ea8026Sopenharmony_ci if m: 11019ea8026Sopenharmony_ci start = (int(m.group('start'), 0) 11119ea8026Sopenharmony_ci if m.group('start') else 0) 11219ea8026Sopenharmony_ci stop = (int(m.group('stop'), 0) 11319ea8026Sopenharmony_ci if m.group('stop') else None) 11419ea8026Sopenharmony_ci step = (int(m.group('step'), 0) 11519ea8026Sopenharmony_ci if m.group('step') else 1) 11619ea8026Sopenharmony_ci if m.lastindex <= 1: 11719ea8026Sopenharmony_ci start, stop = 0, start 11819ea8026Sopenharmony_ci for x in range(start, stop, step): 11919ea8026Sopenharmony_ci yield from parse_define('%s(%d)%s' % ( 12019ea8026Sopenharmony_ci v_[:m.start()], x, v_[m.end():])) 12119ea8026Sopenharmony_ci else: 12219ea8026Sopenharmony_ci yield v_ 12319ea8026Sopenharmony_ci # or a literal value 12419ea8026Sopenharmony_ci elif isinstance(v, bool): 12519ea8026Sopenharmony_ci yield 'true' if v else 'false' 12619ea8026Sopenharmony_ci else: 12719ea8026Sopenharmony_ci yield v 12819ea8026Sopenharmony_ci 12919ea8026Sopenharmony_ci # build possible permutations 13019ea8026Sopenharmony_ci for suite_defines_ in suite_defines: 13119ea8026Sopenharmony_ci self.defines |= suite_defines_.keys() 13219ea8026Sopenharmony_ci for defines_ in defines: 13319ea8026Sopenharmony_ci self.defines |= defines_.keys() 13419ea8026Sopenharmony_ci self.permutations.extend(dict(perm) for perm in it.product(*( 13519ea8026Sopenharmony_ci [(k, v) for v in parse_define(vs)] 13619ea8026Sopenharmony_ci for k, vs in sorted((suite_defines_ | defines_).items())))) 13719ea8026Sopenharmony_ci 13819ea8026Sopenharmony_ci for k in config.keys(): 13919ea8026Sopenharmony_ci print('%swarning:%s in %s, found unused key %r' % ( 14019ea8026Sopenharmony_ci '\x1b[01;33m' if args['color'] else '', 14119ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 14219ea8026Sopenharmony_ci self.name, 14319ea8026Sopenharmony_ci k), 14419ea8026Sopenharmony_ci file=sys.stderr) 14519ea8026Sopenharmony_ci 14619ea8026Sopenharmony_ci 14719ea8026Sopenharmony_ciclass TestSuite: 14819ea8026Sopenharmony_ci # create a TestSuite object from a toml file 14919ea8026Sopenharmony_ci def __init__(self, path, args={}): 15019ea8026Sopenharmony_ci self.path = path 15119ea8026Sopenharmony_ci self.name = os.path.basename(path) 15219ea8026Sopenharmony_ci if self.name.endswith('.toml'): 15319ea8026Sopenharmony_ci self.name = self.name[:-len('.toml')] 15419ea8026Sopenharmony_ci 15519ea8026Sopenharmony_ci # load toml file and parse test cases 15619ea8026Sopenharmony_ci with open(self.path) as f: 15719ea8026Sopenharmony_ci # load tests 15819ea8026Sopenharmony_ci config = toml.load(f) 15919ea8026Sopenharmony_ci 16019ea8026Sopenharmony_ci # find line numbers 16119ea8026Sopenharmony_ci f.seek(0) 16219ea8026Sopenharmony_ci case_linenos = [] 16319ea8026Sopenharmony_ci code_linenos = [] 16419ea8026Sopenharmony_ci for i, line in enumerate(f): 16519ea8026Sopenharmony_ci match = re.match( 16619ea8026Sopenharmony_ci '(?P<case>\[\s*cases\s*\.\s*(?P<name>\w+)\s*\])' 16719ea8026Sopenharmony_ci '|' '(?P<code>code\s*=)', 16819ea8026Sopenharmony_ci line) 16919ea8026Sopenharmony_ci if match and match.group('case'): 17019ea8026Sopenharmony_ci case_linenos.append((i+1, match.group('name'))) 17119ea8026Sopenharmony_ci elif match and match.group('code'): 17219ea8026Sopenharmony_ci code_linenos.append(i+2) 17319ea8026Sopenharmony_ci 17419ea8026Sopenharmony_ci # sort in case toml parsing did not retain order 17519ea8026Sopenharmony_ci case_linenos.sort() 17619ea8026Sopenharmony_ci 17719ea8026Sopenharmony_ci cases = config.pop('cases') 17819ea8026Sopenharmony_ci for (lineno, name), (nlineno, _) in it.zip_longest( 17919ea8026Sopenharmony_ci case_linenos, case_linenos[1:], 18019ea8026Sopenharmony_ci fillvalue=(float('inf'), None)): 18119ea8026Sopenharmony_ci code_lineno = min( 18219ea8026Sopenharmony_ci (l for l in code_linenos if l >= lineno and l < nlineno), 18319ea8026Sopenharmony_ci default=None) 18419ea8026Sopenharmony_ci cases[name]['lineno'] = lineno 18519ea8026Sopenharmony_ci cases[name]['code_lineno'] = code_lineno 18619ea8026Sopenharmony_ci 18719ea8026Sopenharmony_ci self.if_ = config.pop('if', None) 18819ea8026Sopenharmony_ci if isinstance(self.if_, bool): 18919ea8026Sopenharmony_ci self.if_ = 'true' if self.if_ else 'false' 19019ea8026Sopenharmony_ci 19119ea8026Sopenharmony_ci self.code = config.pop('code', None) 19219ea8026Sopenharmony_ci self.code_lineno = min( 19319ea8026Sopenharmony_ci (l for l in code_linenos 19419ea8026Sopenharmony_ci if not case_linenos or l < case_linenos[0][0]), 19519ea8026Sopenharmony_ci default=None) 19619ea8026Sopenharmony_ci 19719ea8026Sopenharmony_ci # a couple of these we just forward to all cases 19819ea8026Sopenharmony_ci defines = config.pop('defines', {}) 19919ea8026Sopenharmony_ci in_ = config.pop('in', None) 20019ea8026Sopenharmony_ci reentrant = config.pop('reentrant', False) 20119ea8026Sopenharmony_ci 20219ea8026Sopenharmony_ci self.cases = [] 20319ea8026Sopenharmony_ci for name, case in sorted(cases.items(), 20419ea8026Sopenharmony_ci key=lambda c: c[1].get('lineno')): 20519ea8026Sopenharmony_ci self.cases.append(TestCase(config={ 20619ea8026Sopenharmony_ci 'name': name, 20719ea8026Sopenharmony_ci 'path': path + (':%d' % case['lineno'] 20819ea8026Sopenharmony_ci if 'lineno' in case else ''), 20919ea8026Sopenharmony_ci 'suite': self.name, 21019ea8026Sopenharmony_ci 'suite_defines': defines, 21119ea8026Sopenharmony_ci 'suite_in': in_, 21219ea8026Sopenharmony_ci 'suite_reentrant': reentrant, 21319ea8026Sopenharmony_ci **case}, 21419ea8026Sopenharmony_ci args=args)) 21519ea8026Sopenharmony_ci 21619ea8026Sopenharmony_ci # combine per-case defines 21719ea8026Sopenharmony_ci self.defines = set.union(*( 21819ea8026Sopenharmony_ci set(case.defines) for case in self.cases)) 21919ea8026Sopenharmony_ci 22019ea8026Sopenharmony_ci # combine other per-case things 22119ea8026Sopenharmony_ci self.reentrant = any(case.reentrant for case in self.cases) 22219ea8026Sopenharmony_ci 22319ea8026Sopenharmony_ci for k in config.keys(): 22419ea8026Sopenharmony_ci print('%swarning:%s in %s, found unused key %r' % ( 22519ea8026Sopenharmony_ci '\x1b[01;33m' if args['color'] else '', 22619ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 22719ea8026Sopenharmony_ci self.name, 22819ea8026Sopenharmony_ci k), 22919ea8026Sopenharmony_ci file=sys.stderr) 23019ea8026Sopenharmony_ci 23119ea8026Sopenharmony_ci 23219ea8026Sopenharmony_ci 23319ea8026Sopenharmony_cidef compile(test_paths, **args): 23419ea8026Sopenharmony_ci # find .toml files 23519ea8026Sopenharmony_ci paths = [] 23619ea8026Sopenharmony_ci for path in test_paths: 23719ea8026Sopenharmony_ci if os.path.isdir(path): 23819ea8026Sopenharmony_ci path = path + '/*.toml' 23919ea8026Sopenharmony_ci 24019ea8026Sopenharmony_ci for path in glob.glob(path): 24119ea8026Sopenharmony_ci paths.append(path) 24219ea8026Sopenharmony_ci 24319ea8026Sopenharmony_ci if not paths: 24419ea8026Sopenharmony_ci print('no test suites found in %r?' % test_paths) 24519ea8026Sopenharmony_ci sys.exit(-1) 24619ea8026Sopenharmony_ci 24719ea8026Sopenharmony_ci # load the suites 24819ea8026Sopenharmony_ci suites = [TestSuite(path, args) for path in paths] 24919ea8026Sopenharmony_ci suites.sort(key=lambda s: s.name) 25019ea8026Sopenharmony_ci 25119ea8026Sopenharmony_ci # check for name conflicts, these will cause ambiguity problems later 25219ea8026Sopenharmony_ci # when running tests 25319ea8026Sopenharmony_ci seen = {} 25419ea8026Sopenharmony_ci for suite in suites: 25519ea8026Sopenharmony_ci if suite.name in seen: 25619ea8026Sopenharmony_ci print('%swarning:%s conflicting suite %r, %s and %s' % ( 25719ea8026Sopenharmony_ci '\x1b[01;33m' if args['color'] else '', 25819ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 25919ea8026Sopenharmony_ci suite.name, 26019ea8026Sopenharmony_ci suite.path, 26119ea8026Sopenharmony_ci seen[suite.name].path), 26219ea8026Sopenharmony_ci file=sys.stderr) 26319ea8026Sopenharmony_ci seen[suite.name] = suite 26419ea8026Sopenharmony_ci 26519ea8026Sopenharmony_ci for case in suite.cases: 26619ea8026Sopenharmony_ci # only allow conflicts if a case and its suite share a name 26719ea8026Sopenharmony_ci if case.name in seen and not ( 26819ea8026Sopenharmony_ci isinstance(seen[case.name], TestSuite) 26919ea8026Sopenharmony_ci and seen[case.name].cases == [case]): 27019ea8026Sopenharmony_ci print('%swarning:%s conflicting case %r, %s and %s' % ( 27119ea8026Sopenharmony_ci '\x1b[01;33m' if args['color'] else '', 27219ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 27319ea8026Sopenharmony_ci case.name, 27419ea8026Sopenharmony_ci case.path, 27519ea8026Sopenharmony_ci seen[case.name].path), 27619ea8026Sopenharmony_ci file=sys.stderr) 27719ea8026Sopenharmony_ci seen[case.name] = case 27819ea8026Sopenharmony_ci 27919ea8026Sopenharmony_ci # we can only compile one test suite at a time 28019ea8026Sopenharmony_ci if not args.get('source'): 28119ea8026Sopenharmony_ci if len(suites) > 1: 28219ea8026Sopenharmony_ci print('more than one test suite for compilation? (%r)' % test_paths) 28319ea8026Sopenharmony_ci sys.exit(-1) 28419ea8026Sopenharmony_ci 28519ea8026Sopenharmony_ci suite = suites[0] 28619ea8026Sopenharmony_ci 28719ea8026Sopenharmony_ci # write generated test source 28819ea8026Sopenharmony_ci if 'output' in args: 28919ea8026Sopenharmony_ci with openio(args['output'], 'w') as f: 29019ea8026Sopenharmony_ci _write = f.write 29119ea8026Sopenharmony_ci def write(s): 29219ea8026Sopenharmony_ci f.lineno += s.count('\n') 29319ea8026Sopenharmony_ci _write(s) 29419ea8026Sopenharmony_ci def writeln(s=''): 29519ea8026Sopenharmony_ci f.lineno += s.count('\n') + 1 29619ea8026Sopenharmony_ci _write(s) 29719ea8026Sopenharmony_ci _write('\n') 29819ea8026Sopenharmony_ci f.lineno = 1 29919ea8026Sopenharmony_ci f.write = write 30019ea8026Sopenharmony_ci f.writeln = writeln 30119ea8026Sopenharmony_ci 30219ea8026Sopenharmony_ci f.writeln("// Generated by %s:" % sys.argv[0]) 30319ea8026Sopenharmony_ci f.writeln("//") 30419ea8026Sopenharmony_ci f.writeln("// %s" % ' '.join(sys.argv)) 30519ea8026Sopenharmony_ci f.writeln("//") 30619ea8026Sopenharmony_ci f.writeln() 30719ea8026Sopenharmony_ci 30819ea8026Sopenharmony_ci # include test_runner.h in every generated file 30919ea8026Sopenharmony_ci f.writeln("#include \"%s\"" % args['include']) 31019ea8026Sopenharmony_ci f.writeln() 31119ea8026Sopenharmony_ci 31219ea8026Sopenharmony_ci # write out generated functions, this can end up in different 31319ea8026Sopenharmony_ci # files depending on the "in" attribute 31419ea8026Sopenharmony_ci # 31519ea8026Sopenharmony_ci # note it's up to the specific generated file to declare 31619ea8026Sopenharmony_ci # the test defines 31719ea8026Sopenharmony_ci def write_case_functions(f, suite, case): 31819ea8026Sopenharmony_ci # create case define functions 31919ea8026Sopenharmony_ci if case.defines: 32019ea8026Sopenharmony_ci # deduplicate defines by value to try to reduce the 32119ea8026Sopenharmony_ci # number of functions we generate 32219ea8026Sopenharmony_ci define_cbs = {} 32319ea8026Sopenharmony_ci for i, defines in enumerate(case.permutations): 32419ea8026Sopenharmony_ci for k, v in sorted(defines.items()): 32519ea8026Sopenharmony_ci if v not in define_cbs: 32619ea8026Sopenharmony_ci name = ('__test__%s__%s__%d' 32719ea8026Sopenharmony_ci % (case.name, k, i)) 32819ea8026Sopenharmony_ci define_cbs[v] = name 32919ea8026Sopenharmony_ci f.writeln('intmax_t %s(' 33019ea8026Sopenharmony_ci '__attribute__((unused)) ' 33119ea8026Sopenharmony_ci 'void *data) {' % name) 33219ea8026Sopenharmony_ci f.writeln(4*' '+'return %s;' % v) 33319ea8026Sopenharmony_ci f.writeln('}') 33419ea8026Sopenharmony_ci f.writeln() 33519ea8026Sopenharmony_ci f.writeln('const test_define_t ' 33619ea8026Sopenharmony_ci '__test__%s__defines[][' 33719ea8026Sopenharmony_ci 'TEST_IMPLICIT_DEFINE_COUNT+%d] = {' 33819ea8026Sopenharmony_ci % (case.name, len(suite.defines))) 33919ea8026Sopenharmony_ci for defines in case.permutations: 34019ea8026Sopenharmony_ci f.writeln(4*' '+'{') 34119ea8026Sopenharmony_ci for k, v in sorted(defines.items()): 34219ea8026Sopenharmony_ci f.writeln(8*' '+'[%-24s] = {%s, NULL},' % ( 34319ea8026Sopenharmony_ci k+'_i', define_cbs[v])) 34419ea8026Sopenharmony_ci f.writeln(4*' '+'},') 34519ea8026Sopenharmony_ci f.writeln('};') 34619ea8026Sopenharmony_ci f.writeln() 34719ea8026Sopenharmony_ci 34819ea8026Sopenharmony_ci # create case filter function 34919ea8026Sopenharmony_ci if suite.if_ is not None or case.if_ is not None: 35019ea8026Sopenharmony_ci f.writeln('bool __test__%s__filter(void) {' 35119ea8026Sopenharmony_ci % (case.name)) 35219ea8026Sopenharmony_ci f.writeln(4*' '+'return %s;' 35319ea8026Sopenharmony_ci % ' && '.join('(%s)' % if_ 35419ea8026Sopenharmony_ci for if_ in [suite.if_, case.if_] 35519ea8026Sopenharmony_ci if if_ is not None)) 35619ea8026Sopenharmony_ci f.writeln('}') 35719ea8026Sopenharmony_ci f.writeln() 35819ea8026Sopenharmony_ci 35919ea8026Sopenharmony_ci # create case run function 36019ea8026Sopenharmony_ci f.writeln('void __test__%s__run(' 36119ea8026Sopenharmony_ci '__attribute__((unused)) struct lfs_config *cfg) {' 36219ea8026Sopenharmony_ci % (case.name)) 36319ea8026Sopenharmony_ci f.writeln(4*' '+'// test case %s' % case.name) 36419ea8026Sopenharmony_ci if case.code_lineno is not None: 36519ea8026Sopenharmony_ci f.writeln(4*' '+'#line %d "%s"' 36619ea8026Sopenharmony_ci % (case.code_lineno, suite.path)) 36719ea8026Sopenharmony_ci f.write(case.code) 36819ea8026Sopenharmony_ci if case.code_lineno is not None: 36919ea8026Sopenharmony_ci f.writeln(4*' '+'#line %d "%s"' 37019ea8026Sopenharmony_ci % (f.lineno+1, args['output'])) 37119ea8026Sopenharmony_ci f.writeln('}') 37219ea8026Sopenharmony_ci f.writeln() 37319ea8026Sopenharmony_ci 37419ea8026Sopenharmony_ci if not args.get('source'): 37519ea8026Sopenharmony_ci if suite.code is not None: 37619ea8026Sopenharmony_ci if suite.code_lineno is not None: 37719ea8026Sopenharmony_ci f.writeln('#line %d "%s"' 37819ea8026Sopenharmony_ci % (suite.code_lineno, suite.path)) 37919ea8026Sopenharmony_ci f.write(suite.code) 38019ea8026Sopenharmony_ci if suite.code_lineno is not None: 38119ea8026Sopenharmony_ci f.writeln('#line %d "%s"' 38219ea8026Sopenharmony_ci % (f.lineno+1, args['output'])) 38319ea8026Sopenharmony_ci f.writeln() 38419ea8026Sopenharmony_ci 38519ea8026Sopenharmony_ci if suite.defines: 38619ea8026Sopenharmony_ci for i, define in enumerate(sorted(suite.defines)): 38719ea8026Sopenharmony_ci f.writeln('#ifndef %s' % define) 38819ea8026Sopenharmony_ci f.writeln('#define %-24s ' 38919ea8026Sopenharmony_ci 'TEST_IMPLICIT_DEFINE_COUNT+%d' % (define+'_i', i)) 39019ea8026Sopenharmony_ci f.writeln('#define %-24s ' 39119ea8026Sopenharmony_ci 'TEST_DEFINE(%s)' % (define, define+'_i')) 39219ea8026Sopenharmony_ci f.writeln('#endif') 39319ea8026Sopenharmony_ci f.writeln() 39419ea8026Sopenharmony_ci 39519ea8026Sopenharmony_ci # create case functions 39619ea8026Sopenharmony_ci for case in suite.cases: 39719ea8026Sopenharmony_ci if case.in_ is None: 39819ea8026Sopenharmony_ci write_case_functions(f, suite, case) 39919ea8026Sopenharmony_ci else: 40019ea8026Sopenharmony_ci if case.defines: 40119ea8026Sopenharmony_ci f.writeln('extern const test_define_t ' 40219ea8026Sopenharmony_ci '__test__%s__defines[][' 40319ea8026Sopenharmony_ci 'TEST_IMPLICIT_DEFINE_COUNT+%d];' 40419ea8026Sopenharmony_ci % (case.name, len(suite.defines))) 40519ea8026Sopenharmony_ci if suite.if_ is not None or case.if_ is not None: 40619ea8026Sopenharmony_ci f.writeln('extern bool __test__%s__filter(' 40719ea8026Sopenharmony_ci 'void);' 40819ea8026Sopenharmony_ci % (case.name)) 40919ea8026Sopenharmony_ci f.writeln('extern void __test__%s__run(' 41019ea8026Sopenharmony_ci 'struct lfs_config *cfg);' 41119ea8026Sopenharmony_ci % (case.name)) 41219ea8026Sopenharmony_ci f.writeln() 41319ea8026Sopenharmony_ci 41419ea8026Sopenharmony_ci # create suite struct 41519ea8026Sopenharmony_ci # 41619ea8026Sopenharmony_ci # note we place this in the custom test_suites section with 41719ea8026Sopenharmony_ci # minimum alignment, otherwise GCC ups the alignment to 41819ea8026Sopenharmony_ci # 32-bytes for some reason 41919ea8026Sopenharmony_ci f.writeln('__attribute__((section("_test_suites"), ' 42019ea8026Sopenharmony_ci 'aligned(1)))') 42119ea8026Sopenharmony_ci f.writeln('const struct test_suite __test__%s__suite = {' 42219ea8026Sopenharmony_ci % suite.name) 42319ea8026Sopenharmony_ci f.writeln(4*' '+'.name = "%s",' % suite.name) 42419ea8026Sopenharmony_ci f.writeln(4*' '+'.path = "%s",' % suite.path) 42519ea8026Sopenharmony_ci f.writeln(4*' '+'.flags = %s,' 42619ea8026Sopenharmony_ci % (' | '.join(filter(None, [ 42719ea8026Sopenharmony_ci 'TEST_REENTRANT' if suite.reentrant else None])) 42819ea8026Sopenharmony_ci or 0)) 42919ea8026Sopenharmony_ci if suite.defines: 43019ea8026Sopenharmony_ci # create suite define names 43119ea8026Sopenharmony_ci f.writeln(4*' '+'.define_names = (const char *const[' 43219ea8026Sopenharmony_ci 'TEST_IMPLICIT_DEFINE_COUNT+%d]){' % ( 43319ea8026Sopenharmony_ci len(suite.defines))) 43419ea8026Sopenharmony_ci for k in sorted(suite.defines): 43519ea8026Sopenharmony_ci f.writeln(8*' '+'[%-24s] = "%s",' % (k+'_i', k)) 43619ea8026Sopenharmony_ci f.writeln(4*' '+'},') 43719ea8026Sopenharmony_ci f.writeln(4*' '+'.define_count = ' 43819ea8026Sopenharmony_ci 'TEST_IMPLICIT_DEFINE_COUNT+%d,' % len(suite.defines)) 43919ea8026Sopenharmony_ci f.writeln(4*' '+'.cases = (const struct test_case[]){') 44019ea8026Sopenharmony_ci for case in suite.cases: 44119ea8026Sopenharmony_ci # create case structs 44219ea8026Sopenharmony_ci f.writeln(8*' '+'{') 44319ea8026Sopenharmony_ci f.writeln(12*' '+'.name = "%s",' % case.name) 44419ea8026Sopenharmony_ci f.writeln(12*' '+'.path = "%s",' % case.path) 44519ea8026Sopenharmony_ci f.writeln(12*' '+'.flags = %s,' 44619ea8026Sopenharmony_ci % (' | '.join(filter(None, [ 44719ea8026Sopenharmony_ci 'TEST_REENTRANT' if case.reentrant else None])) 44819ea8026Sopenharmony_ci or 0)) 44919ea8026Sopenharmony_ci f.writeln(12*' '+'.permutations = %d,' 45019ea8026Sopenharmony_ci % len(case.permutations)) 45119ea8026Sopenharmony_ci if case.defines: 45219ea8026Sopenharmony_ci f.writeln(12*' '+'.defines ' 45319ea8026Sopenharmony_ci '= (const test_define_t*)__test__%s__defines,' 45419ea8026Sopenharmony_ci % (case.name)) 45519ea8026Sopenharmony_ci if suite.if_ is not None or case.if_ is not None: 45619ea8026Sopenharmony_ci f.writeln(12*' '+'.filter = __test__%s__filter,' 45719ea8026Sopenharmony_ci % (case.name)) 45819ea8026Sopenharmony_ci f.writeln(12*' '+'.run = __test__%s__run,' 45919ea8026Sopenharmony_ci % (case.name)) 46019ea8026Sopenharmony_ci f.writeln(8*' '+'},') 46119ea8026Sopenharmony_ci f.writeln(4*' '+'},') 46219ea8026Sopenharmony_ci f.writeln(4*' '+'.case_count = %d,' % len(suite.cases)) 46319ea8026Sopenharmony_ci f.writeln('};') 46419ea8026Sopenharmony_ci f.writeln() 46519ea8026Sopenharmony_ci 46619ea8026Sopenharmony_ci else: 46719ea8026Sopenharmony_ci # copy source 46819ea8026Sopenharmony_ci f.writeln('#line 1 "%s"' % args['source']) 46919ea8026Sopenharmony_ci with open(args['source']) as sf: 47019ea8026Sopenharmony_ci shutil.copyfileobj(sf, f) 47119ea8026Sopenharmony_ci f.writeln() 47219ea8026Sopenharmony_ci 47319ea8026Sopenharmony_ci # write any internal tests 47419ea8026Sopenharmony_ci for suite in suites: 47519ea8026Sopenharmony_ci for case in suite.cases: 47619ea8026Sopenharmony_ci if (case.in_ is not None 47719ea8026Sopenharmony_ci and os.path.normpath(case.in_) 47819ea8026Sopenharmony_ci == os.path.normpath(args['source'])): 47919ea8026Sopenharmony_ci # write defines, but note we need to undef any 48019ea8026Sopenharmony_ci # new defines since we're in someone else's file 48119ea8026Sopenharmony_ci if suite.defines: 48219ea8026Sopenharmony_ci for i, define in enumerate( 48319ea8026Sopenharmony_ci sorted(suite.defines)): 48419ea8026Sopenharmony_ci f.writeln('#ifndef %s' % define) 48519ea8026Sopenharmony_ci f.writeln('#define %-24s ' 48619ea8026Sopenharmony_ci 'TEST_IMPLICIT_DEFINE_COUNT+%d' % ( 48719ea8026Sopenharmony_ci define+'_i', i)) 48819ea8026Sopenharmony_ci f.writeln('#define %-24s ' 48919ea8026Sopenharmony_ci 'TEST_DEFINE(%s)' % ( 49019ea8026Sopenharmony_ci define, define+'_i')) 49119ea8026Sopenharmony_ci f.writeln('#define ' 49219ea8026Sopenharmony_ci '__TEST__%s__NEEDS_UNDEF' % ( 49319ea8026Sopenharmony_ci define)) 49419ea8026Sopenharmony_ci f.writeln('#endif') 49519ea8026Sopenharmony_ci f.writeln() 49619ea8026Sopenharmony_ci 49719ea8026Sopenharmony_ci write_case_functions(f, suite, case) 49819ea8026Sopenharmony_ci 49919ea8026Sopenharmony_ci if suite.defines: 50019ea8026Sopenharmony_ci for define in sorted(suite.defines): 50119ea8026Sopenharmony_ci f.writeln('#ifdef __TEST__%s__NEEDS_UNDEF' 50219ea8026Sopenharmony_ci % define) 50319ea8026Sopenharmony_ci f.writeln('#undef __TEST__%s__NEEDS_UNDEF' 50419ea8026Sopenharmony_ci % define) 50519ea8026Sopenharmony_ci f.writeln('#undef %s' % define) 50619ea8026Sopenharmony_ci f.writeln('#undef %s' % (define+'_i')) 50719ea8026Sopenharmony_ci f.writeln('#endif') 50819ea8026Sopenharmony_ci f.writeln() 50919ea8026Sopenharmony_ci 51019ea8026Sopenharmony_cidef find_runner(runner, **args): 51119ea8026Sopenharmony_ci cmd = runner.copy() 51219ea8026Sopenharmony_ci 51319ea8026Sopenharmony_ci # run under some external command? 51419ea8026Sopenharmony_ci if args.get('exec'): 51519ea8026Sopenharmony_ci cmd[:0] = args['exec'] 51619ea8026Sopenharmony_ci 51719ea8026Sopenharmony_ci # run under valgrind? 51819ea8026Sopenharmony_ci if args.get('valgrind'): 51919ea8026Sopenharmony_ci cmd[:0] = args['valgrind_path'] + [ 52019ea8026Sopenharmony_ci '--leak-check=full', 52119ea8026Sopenharmony_ci '--track-origins=yes', 52219ea8026Sopenharmony_ci '--error-exitcode=4', 52319ea8026Sopenharmony_ci '-q'] 52419ea8026Sopenharmony_ci 52519ea8026Sopenharmony_ci # run under perf? 52619ea8026Sopenharmony_ci if args.get('perf'): 52719ea8026Sopenharmony_ci cmd[:0] = args['perf_script'] + list(filter(None, [ 52819ea8026Sopenharmony_ci '-R', 52919ea8026Sopenharmony_ci '--perf-freq=%s' % args['perf_freq'] 53019ea8026Sopenharmony_ci if args.get('perf_freq') else None, 53119ea8026Sopenharmony_ci '--perf-period=%s' % args['perf_period'] 53219ea8026Sopenharmony_ci if args.get('perf_period') else None, 53319ea8026Sopenharmony_ci '--perf-events=%s' % args['perf_events'] 53419ea8026Sopenharmony_ci if args.get('perf_events') else None, 53519ea8026Sopenharmony_ci '--perf-path=%s' % args['perf_path'] 53619ea8026Sopenharmony_ci if args.get('perf_path') else None, 53719ea8026Sopenharmony_ci '-o%s' % args['perf']])) 53819ea8026Sopenharmony_ci 53919ea8026Sopenharmony_ci # other context 54019ea8026Sopenharmony_ci if args.get('geometry'): 54119ea8026Sopenharmony_ci cmd.append('-G%s' % args['geometry']) 54219ea8026Sopenharmony_ci if args.get('powerloss'): 54319ea8026Sopenharmony_ci cmd.append('-P%s' % args['powerloss']) 54419ea8026Sopenharmony_ci if args.get('disk'): 54519ea8026Sopenharmony_ci cmd.append('-d%s' % args['disk']) 54619ea8026Sopenharmony_ci if args.get('trace'): 54719ea8026Sopenharmony_ci cmd.append('-t%s' % args['trace']) 54819ea8026Sopenharmony_ci if args.get('trace_backtrace'): 54919ea8026Sopenharmony_ci cmd.append('--trace-backtrace') 55019ea8026Sopenharmony_ci if args.get('trace_period'): 55119ea8026Sopenharmony_ci cmd.append('--trace-period=%s' % args['trace_period']) 55219ea8026Sopenharmony_ci if args.get('trace_freq'): 55319ea8026Sopenharmony_ci cmd.append('--trace-freq=%s' % args['trace_freq']) 55419ea8026Sopenharmony_ci if args.get('read_sleep'): 55519ea8026Sopenharmony_ci cmd.append('--read-sleep=%s' % args['read_sleep']) 55619ea8026Sopenharmony_ci if args.get('prog_sleep'): 55719ea8026Sopenharmony_ci cmd.append('--prog-sleep=%s' % args['prog_sleep']) 55819ea8026Sopenharmony_ci if args.get('erase_sleep'): 55919ea8026Sopenharmony_ci cmd.append('--erase-sleep=%s' % args['erase_sleep']) 56019ea8026Sopenharmony_ci 56119ea8026Sopenharmony_ci # defines? 56219ea8026Sopenharmony_ci if args.get('define'): 56319ea8026Sopenharmony_ci for define in args.get('define'): 56419ea8026Sopenharmony_ci cmd.append('-D%s' % define) 56519ea8026Sopenharmony_ci 56619ea8026Sopenharmony_ci return cmd 56719ea8026Sopenharmony_ci 56819ea8026Sopenharmony_cidef list_(runner, test_ids=[], **args): 56919ea8026Sopenharmony_ci cmd = find_runner(runner, **args) + test_ids 57019ea8026Sopenharmony_ci if args.get('summary'): cmd.append('--summary') 57119ea8026Sopenharmony_ci if args.get('list_suites'): cmd.append('--list-suites') 57219ea8026Sopenharmony_ci if args.get('list_cases'): cmd.append('--list-cases') 57319ea8026Sopenharmony_ci if args.get('list_suite_paths'): cmd.append('--list-suite-paths') 57419ea8026Sopenharmony_ci if args.get('list_case_paths'): cmd.append('--list-case-paths') 57519ea8026Sopenharmony_ci if args.get('list_defines'): cmd.append('--list-defines') 57619ea8026Sopenharmony_ci if args.get('list_permutation_defines'): 57719ea8026Sopenharmony_ci cmd.append('--list-permutation-defines') 57819ea8026Sopenharmony_ci if args.get('list_implicit_defines'): 57919ea8026Sopenharmony_ci cmd.append('--list-implicit-defines') 58019ea8026Sopenharmony_ci if args.get('list_geometries'): cmd.append('--list-geometries') 58119ea8026Sopenharmony_ci if args.get('list_powerlosses'): cmd.append('--list-powerlosses') 58219ea8026Sopenharmony_ci 58319ea8026Sopenharmony_ci if args.get('verbose'): 58419ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 58519ea8026Sopenharmony_ci return sp.call(cmd) 58619ea8026Sopenharmony_ci 58719ea8026Sopenharmony_ci 58819ea8026Sopenharmony_cidef find_perms(runner_, ids=[], **args): 58919ea8026Sopenharmony_ci case_suites = {} 59019ea8026Sopenharmony_ci expected_case_perms = co.defaultdict(lambda: 0) 59119ea8026Sopenharmony_ci expected_perms = 0 59219ea8026Sopenharmony_ci total_perms = 0 59319ea8026Sopenharmony_ci 59419ea8026Sopenharmony_ci # query cases from the runner 59519ea8026Sopenharmony_ci cmd = runner_ + ['--list-cases'] + ids 59619ea8026Sopenharmony_ci if args.get('verbose'): 59719ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 59819ea8026Sopenharmony_ci proc = sp.Popen(cmd, 59919ea8026Sopenharmony_ci stdout=sp.PIPE, 60019ea8026Sopenharmony_ci stderr=sp.PIPE if not args.get('verbose') else None, 60119ea8026Sopenharmony_ci universal_newlines=True, 60219ea8026Sopenharmony_ci errors='replace', 60319ea8026Sopenharmony_ci close_fds=False) 60419ea8026Sopenharmony_ci pattern = re.compile( 60519ea8026Sopenharmony_ci '^(?P<case>[^\s]+)' 60619ea8026Sopenharmony_ci '\s+(?P<flags>[^\s]+)' 60719ea8026Sopenharmony_ci '\s+(?P<filtered>\d+)/(?P<perms>\d+)') 60819ea8026Sopenharmony_ci # skip the first line 60919ea8026Sopenharmony_ci for line in it.islice(proc.stdout, 1, None): 61019ea8026Sopenharmony_ci m = pattern.match(line) 61119ea8026Sopenharmony_ci if m: 61219ea8026Sopenharmony_ci filtered = int(m.group('filtered')) 61319ea8026Sopenharmony_ci perms = int(m.group('perms')) 61419ea8026Sopenharmony_ci expected_case_perms[m.group('case')] += filtered 61519ea8026Sopenharmony_ci expected_perms += filtered 61619ea8026Sopenharmony_ci total_perms += perms 61719ea8026Sopenharmony_ci proc.wait() 61819ea8026Sopenharmony_ci if proc.returncode != 0: 61919ea8026Sopenharmony_ci if not args.get('verbose'): 62019ea8026Sopenharmony_ci for line in proc.stderr: 62119ea8026Sopenharmony_ci sys.stdout.write(line) 62219ea8026Sopenharmony_ci sys.exit(-1) 62319ea8026Sopenharmony_ci 62419ea8026Sopenharmony_ci # get which suite each case belongs to via paths 62519ea8026Sopenharmony_ci cmd = runner_ + ['--list-case-paths'] + ids 62619ea8026Sopenharmony_ci if args.get('verbose'): 62719ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 62819ea8026Sopenharmony_ci proc = sp.Popen(cmd, 62919ea8026Sopenharmony_ci stdout=sp.PIPE, 63019ea8026Sopenharmony_ci stderr=sp.PIPE if not args.get('verbose') else None, 63119ea8026Sopenharmony_ci universal_newlines=True, 63219ea8026Sopenharmony_ci errors='replace', 63319ea8026Sopenharmony_ci close_fds=False) 63419ea8026Sopenharmony_ci pattern = re.compile( 63519ea8026Sopenharmony_ci '^(?P<case>[^\s]+)' 63619ea8026Sopenharmony_ci '\s+(?P<path>[^:]+):(?P<lineno>\d+)') 63719ea8026Sopenharmony_ci # skip the first line 63819ea8026Sopenharmony_ci for line in it.islice(proc.stdout, 1, None): 63919ea8026Sopenharmony_ci m = pattern.match(line) 64019ea8026Sopenharmony_ci if m: 64119ea8026Sopenharmony_ci path = m.group('path') 64219ea8026Sopenharmony_ci # strip path/suffix here 64319ea8026Sopenharmony_ci suite = os.path.basename(path) 64419ea8026Sopenharmony_ci if suite.endswith('.toml'): 64519ea8026Sopenharmony_ci suite = suite[:-len('.toml')] 64619ea8026Sopenharmony_ci case_suites[m.group('case')] = suite 64719ea8026Sopenharmony_ci proc.wait() 64819ea8026Sopenharmony_ci if proc.returncode != 0: 64919ea8026Sopenharmony_ci if not args.get('verbose'): 65019ea8026Sopenharmony_ci for line in proc.stderr: 65119ea8026Sopenharmony_ci sys.stdout.write(line) 65219ea8026Sopenharmony_ci sys.exit(-1) 65319ea8026Sopenharmony_ci 65419ea8026Sopenharmony_ci # figure out expected suite perms 65519ea8026Sopenharmony_ci expected_suite_perms = co.defaultdict(lambda: 0) 65619ea8026Sopenharmony_ci for case, suite in case_suites.items(): 65719ea8026Sopenharmony_ci expected_suite_perms[suite] += expected_case_perms[case] 65819ea8026Sopenharmony_ci 65919ea8026Sopenharmony_ci return ( 66019ea8026Sopenharmony_ci case_suites, 66119ea8026Sopenharmony_ci expected_suite_perms, 66219ea8026Sopenharmony_ci expected_case_perms, 66319ea8026Sopenharmony_ci expected_perms, 66419ea8026Sopenharmony_ci total_perms) 66519ea8026Sopenharmony_ci 66619ea8026Sopenharmony_cidef find_path(runner_, id, **args): 66719ea8026Sopenharmony_ci path = None 66819ea8026Sopenharmony_ci # query from runner 66919ea8026Sopenharmony_ci cmd = runner_ + ['--list-case-paths', id] 67019ea8026Sopenharmony_ci if args.get('verbose'): 67119ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 67219ea8026Sopenharmony_ci proc = sp.Popen(cmd, 67319ea8026Sopenharmony_ci stdout=sp.PIPE, 67419ea8026Sopenharmony_ci stderr=sp.PIPE if not args.get('verbose') else None, 67519ea8026Sopenharmony_ci universal_newlines=True, 67619ea8026Sopenharmony_ci errors='replace', 67719ea8026Sopenharmony_ci close_fds=False) 67819ea8026Sopenharmony_ci pattern = re.compile( 67919ea8026Sopenharmony_ci '^(?P<case>[^\s]+)' 68019ea8026Sopenharmony_ci '\s+(?P<path>[^:]+):(?P<lineno>\d+)') 68119ea8026Sopenharmony_ci # skip the first line 68219ea8026Sopenharmony_ci for line in it.islice(proc.stdout, 1, None): 68319ea8026Sopenharmony_ci m = pattern.match(line) 68419ea8026Sopenharmony_ci if m and path is None: 68519ea8026Sopenharmony_ci path_ = m.group('path') 68619ea8026Sopenharmony_ci lineno = int(m.group('lineno')) 68719ea8026Sopenharmony_ci path = (path_, lineno) 68819ea8026Sopenharmony_ci proc.wait() 68919ea8026Sopenharmony_ci if proc.returncode != 0: 69019ea8026Sopenharmony_ci if not args.get('verbose'): 69119ea8026Sopenharmony_ci for line in proc.stderr: 69219ea8026Sopenharmony_ci sys.stdout.write(line) 69319ea8026Sopenharmony_ci sys.exit(-1) 69419ea8026Sopenharmony_ci 69519ea8026Sopenharmony_ci return path 69619ea8026Sopenharmony_ci 69719ea8026Sopenharmony_cidef find_defines(runner_, id, **args): 69819ea8026Sopenharmony_ci # query permutation defines from runner 69919ea8026Sopenharmony_ci cmd = runner_ + ['--list-permutation-defines', id] 70019ea8026Sopenharmony_ci if args.get('verbose'): 70119ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 70219ea8026Sopenharmony_ci proc = sp.Popen(cmd, 70319ea8026Sopenharmony_ci stdout=sp.PIPE, 70419ea8026Sopenharmony_ci stderr=sp.PIPE if not args.get('verbose') else None, 70519ea8026Sopenharmony_ci universal_newlines=True, 70619ea8026Sopenharmony_ci errors='replace', 70719ea8026Sopenharmony_ci close_fds=False) 70819ea8026Sopenharmony_ci defines = co.OrderedDict() 70919ea8026Sopenharmony_ci pattern = re.compile('^(?P<define>\w+)=(?P<value>.+)') 71019ea8026Sopenharmony_ci for line in proc.stdout: 71119ea8026Sopenharmony_ci m = pattern.match(line) 71219ea8026Sopenharmony_ci if m: 71319ea8026Sopenharmony_ci define = m.group('define') 71419ea8026Sopenharmony_ci value = m.group('value') 71519ea8026Sopenharmony_ci defines[define] = value 71619ea8026Sopenharmony_ci proc.wait() 71719ea8026Sopenharmony_ci if proc.returncode != 0: 71819ea8026Sopenharmony_ci if not args.get('verbose'): 71919ea8026Sopenharmony_ci for line in proc.stderr: 72019ea8026Sopenharmony_ci sys.stdout.write(line) 72119ea8026Sopenharmony_ci sys.exit(-1) 72219ea8026Sopenharmony_ci 72319ea8026Sopenharmony_ci return defines 72419ea8026Sopenharmony_ci 72519ea8026Sopenharmony_ci 72619ea8026Sopenharmony_ci# Thread-safe CSV writer 72719ea8026Sopenharmony_ciclass TestOutput: 72819ea8026Sopenharmony_ci def __init__(self, path, head=None, tail=None): 72919ea8026Sopenharmony_ci self.f = openio(path, 'w+', 1) 73019ea8026Sopenharmony_ci self.lock = th.Lock() 73119ea8026Sopenharmony_ci self.head = head or [] 73219ea8026Sopenharmony_ci self.tail = tail or [] 73319ea8026Sopenharmony_ci self.writer = csv.DictWriter(self.f, self.head + self.tail) 73419ea8026Sopenharmony_ci self.rows = [] 73519ea8026Sopenharmony_ci 73619ea8026Sopenharmony_ci def close(self): 73719ea8026Sopenharmony_ci self.f.close() 73819ea8026Sopenharmony_ci 73919ea8026Sopenharmony_ci def __enter__(self): 74019ea8026Sopenharmony_ci return self 74119ea8026Sopenharmony_ci 74219ea8026Sopenharmony_ci def __exit__(self, *_): 74319ea8026Sopenharmony_ci self.f.close() 74419ea8026Sopenharmony_ci 74519ea8026Sopenharmony_ci def writerow(self, row): 74619ea8026Sopenharmony_ci with self.lock: 74719ea8026Sopenharmony_ci self.rows.append(row) 74819ea8026Sopenharmony_ci if all(k in self.head or k in self.tail for k in row.keys()): 74919ea8026Sopenharmony_ci # can simply append 75019ea8026Sopenharmony_ci self.writer.writerow(row) 75119ea8026Sopenharmony_ci else: 75219ea8026Sopenharmony_ci # need to rewrite the file 75319ea8026Sopenharmony_ci self.head.extend(row.keys() - (self.head + self.tail)) 75419ea8026Sopenharmony_ci self.f.seek(0) 75519ea8026Sopenharmony_ci self.f.truncate() 75619ea8026Sopenharmony_ci self.writer = csv.DictWriter(self.f, self.head + self.tail) 75719ea8026Sopenharmony_ci self.writer.writeheader() 75819ea8026Sopenharmony_ci for row in self.rows: 75919ea8026Sopenharmony_ci self.writer.writerow(row) 76019ea8026Sopenharmony_ci 76119ea8026Sopenharmony_ci# A test failure 76219ea8026Sopenharmony_ciclass TestFailure(Exception): 76319ea8026Sopenharmony_ci def __init__(self, id, returncode, stdout, assert_=None): 76419ea8026Sopenharmony_ci self.id = id 76519ea8026Sopenharmony_ci self.returncode = returncode 76619ea8026Sopenharmony_ci self.stdout = stdout 76719ea8026Sopenharmony_ci self.assert_ = assert_ 76819ea8026Sopenharmony_ci 76919ea8026Sopenharmony_cidef run_stage(name, runner_, ids, stdout_, trace_, output_, **args): 77019ea8026Sopenharmony_ci # get expected suite/case/perm counts 77119ea8026Sopenharmony_ci (case_suites, 77219ea8026Sopenharmony_ci expected_suite_perms, 77319ea8026Sopenharmony_ci expected_case_perms, 77419ea8026Sopenharmony_ci expected_perms, 77519ea8026Sopenharmony_ci total_perms) = find_perms(runner_, ids, **args) 77619ea8026Sopenharmony_ci 77719ea8026Sopenharmony_ci passed_suite_perms = co.defaultdict(lambda: 0) 77819ea8026Sopenharmony_ci passed_case_perms = co.defaultdict(lambda: 0) 77919ea8026Sopenharmony_ci passed_perms = 0 78019ea8026Sopenharmony_ci powerlosses = 0 78119ea8026Sopenharmony_ci failures = [] 78219ea8026Sopenharmony_ci killed = False 78319ea8026Sopenharmony_ci 78419ea8026Sopenharmony_ci pattern = re.compile('^(?:' 78519ea8026Sopenharmony_ci '(?P<op>running|finished|skipped|powerloss) ' 78619ea8026Sopenharmony_ci '(?P<id>(?P<case>[^:]+)[^\s]*)' 78719ea8026Sopenharmony_ci '|' '(?P<path>[^:]+):(?P<lineno>\d+):(?P<op_>assert):' 78819ea8026Sopenharmony_ci ' *(?P<message>.*)' 78919ea8026Sopenharmony_ci ')$') 79019ea8026Sopenharmony_ci locals = th.local() 79119ea8026Sopenharmony_ci children = set() 79219ea8026Sopenharmony_ci 79319ea8026Sopenharmony_ci def run_runner(runner_, ids=[]): 79419ea8026Sopenharmony_ci nonlocal passed_suite_perms 79519ea8026Sopenharmony_ci nonlocal passed_case_perms 79619ea8026Sopenharmony_ci nonlocal passed_perms 79719ea8026Sopenharmony_ci nonlocal powerlosses 79819ea8026Sopenharmony_ci nonlocal locals 79919ea8026Sopenharmony_ci 80019ea8026Sopenharmony_ci # run the tests! 80119ea8026Sopenharmony_ci cmd = runner_ + ids 80219ea8026Sopenharmony_ci if args.get('verbose'): 80319ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 80419ea8026Sopenharmony_ci 80519ea8026Sopenharmony_ci mpty, spty = pty.openpty() 80619ea8026Sopenharmony_ci proc = sp.Popen(cmd, stdout=spty, stderr=spty, close_fds=False) 80719ea8026Sopenharmony_ci os.close(spty) 80819ea8026Sopenharmony_ci children.add(proc) 80919ea8026Sopenharmony_ci mpty = os.fdopen(mpty, 'r', 1) 81019ea8026Sopenharmony_ci 81119ea8026Sopenharmony_ci last_id = None 81219ea8026Sopenharmony_ci last_stdout = co.deque(maxlen=args.get('context', 5) + 1) 81319ea8026Sopenharmony_ci last_assert = None 81419ea8026Sopenharmony_ci try: 81519ea8026Sopenharmony_ci while True: 81619ea8026Sopenharmony_ci # parse a line for state changes 81719ea8026Sopenharmony_ci try: 81819ea8026Sopenharmony_ci line = mpty.readline() 81919ea8026Sopenharmony_ci except OSError as e: 82019ea8026Sopenharmony_ci if e.errno != errno.EIO: 82119ea8026Sopenharmony_ci raise 82219ea8026Sopenharmony_ci break 82319ea8026Sopenharmony_ci if not line: 82419ea8026Sopenharmony_ci break 82519ea8026Sopenharmony_ci last_stdout.append(line) 82619ea8026Sopenharmony_ci if stdout_: 82719ea8026Sopenharmony_ci try: 82819ea8026Sopenharmony_ci stdout_.write(line) 82919ea8026Sopenharmony_ci stdout_.flush() 83019ea8026Sopenharmony_ci except BrokenPipeError: 83119ea8026Sopenharmony_ci pass 83219ea8026Sopenharmony_ci 83319ea8026Sopenharmony_ci m = pattern.match(line) 83419ea8026Sopenharmony_ci if m: 83519ea8026Sopenharmony_ci op = m.group('op') or m.group('op_') 83619ea8026Sopenharmony_ci if op == 'running': 83719ea8026Sopenharmony_ci locals.seen_perms += 1 83819ea8026Sopenharmony_ci last_id = m.group('id') 83919ea8026Sopenharmony_ci last_stdout.clear() 84019ea8026Sopenharmony_ci last_assert = None 84119ea8026Sopenharmony_ci elif op == 'powerloss': 84219ea8026Sopenharmony_ci last_id = m.group('id') 84319ea8026Sopenharmony_ci powerlosses += 1 84419ea8026Sopenharmony_ci elif op == 'finished': 84519ea8026Sopenharmony_ci case = m.group('case') 84619ea8026Sopenharmony_ci suite = case_suites[case] 84719ea8026Sopenharmony_ci passed_suite_perms[suite] += 1 84819ea8026Sopenharmony_ci passed_case_perms[case] += 1 84919ea8026Sopenharmony_ci passed_perms += 1 85019ea8026Sopenharmony_ci if output_: 85119ea8026Sopenharmony_ci # get defines and write to csv 85219ea8026Sopenharmony_ci defines = find_defines( 85319ea8026Sopenharmony_ci runner_, m.group('id'), **args) 85419ea8026Sopenharmony_ci output_.writerow({ 85519ea8026Sopenharmony_ci 'suite': suite, 85619ea8026Sopenharmony_ci 'case': case, 85719ea8026Sopenharmony_ci 'test_passed': '1/1', 85819ea8026Sopenharmony_ci **defines}) 85919ea8026Sopenharmony_ci elif op == 'skipped': 86019ea8026Sopenharmony_ci locals.seen_perms += 1 86119ea8026Sopenharmony_ci elif op == 'assert': 86219ea8026Sopenharmony_ci last_assert = ( 86319ea8026Sopenharmony_ci m.group('path'), 86419ea8026Sopenharmony_ci int(m.group('lineno')), 86519ea8026Sopenharmony_ci m.group('message')) 86619ea8026Sopenharmony_ci # go ahead and kill the process, aborting takes a while 86719ea8026Sopenharmony_ci if args.get('keep_going'): 86819ea8026Sopenharmony_ci proc.kill() 86919ea8026Sopenharmony_ci except KeyboardInterrupt: 87019ea8026Sopenharmony_ci raise TestFailure(last_id, 1, list(last_stdout)) 87119ea8026Sopenharmony_ci finally: 87219ea8026Sopenharmony_ci children.remove(proc) 87319ea8026Sopenharmony_ci mpty.close() 87419ea8026Sopenharmony_ci 87519ea8026Sopenharmony_ci proc.wait() 87619ea8026Sopenharmony_ci if proc.returncode != 0: 87719ea8026Sopenharmony_ci raise TestFailure( 87819ea8026Sopenharmony_ci last_id, 87919ea8026Sopenharmony_ci proc.returncode, 88019ea8026Sopenharmony_ci list(last_stdout), 88119ea8026Sopenharmony_ci last_assert) 88219ea8026Sopenharmony_ci 88319ea8026Sopenharmony_ci def run_job(runner_, ids=[], start=None, step=None): 88419ea8026Sopenharmony_ci nonlocal failures 88519ea8026Sopenharmony_ci nonlocal killed 88619ea8026Sopenharmony_ci nonlocal locals 88719ea8026Sopenharmony_ci 88819ea8026Sopenharmony_ci start = start or 0 88919ea8026Sopenharmony_ci step = step or 1 89019ea8026Sopenharmony_ci while start < total_perms: 89119ea8026Sopenharmony_ci job_runner = runner_.copy() 89219ea8026Sopenharmony_ci if args.get('isolate') or args.get('valgrind'): 89319ea8026Sopenharmony_ci job_runner.append('-s%s,%s,%s' % (start, start+step, step)) 89419ea8026Sopenharmony_ci else: 89519ea8026Sopenharmony_ci job_runner.append('-s%s,,%s' % (start, step)) 89619ea8026Sopenharmony_ci 89719ea8026Sopenharmony_ci try: 89819ea8026Sopenharmony_ci # run the tests 89919ea8026Sopenharmony_ci locals.seen_perms = 0 90019ea8026Sopenharmony_ci run_runner(job_runner, ids) 90119ea8026Sopenharmony_ci assert locals.seen_perms > 0 90219ea8026Sopenharmony_ci start += locals.seen_perms*step 90319ea8026Sopenharmony_ci 90419ea8026Sopenharmony_ci except TestFailure as failure: 90519ea8026Sopenharmony_ci # keep track of failures 90619ea8026Sopenharmony_ci if output_: 90719ea8026Sopenharmony_ci case, _ = failure.id.split(':', 1) 90819ea8026Sopenharmony_ci suite = case_suites[case] 90919ea8026Sopenharmony_ci # get defines and write to csv 91019ea8026Sopenharmony_ci defines = find_defines(runner_, failure.id, **args) 91119ea8026Sopenharmony_ci output_.writerow({ 91219ea8026Sopenharmony_ci 'suite': suite, 91319ea8026Sopenharmony_ci 'case': case, 91419ea8026Sopenharmony_ci 'test_passed': '0/1', 91519ea8026Sopenharmony_ci **defines}) 91619ea8026Sopenharmony_ci 91719ea8026Sopenharmony_ci # race condition for multiple failures? 91819ea8026Sopenharmony_ci if failures and not args.get('keep_going'): 91919ea8026Sopenharmony_ci break 92019ea8026Sopenharmony_ci 92119ea8026Sopenharmony_ci failures.append(failure) 92219ea8026Sopenharmony_ci 92319ea8026Sopenharmony_ci if args.get('keep_going') and not killed: 92419ea8026Sopenharmony_ci # resume after failed test 92519ea8026Sopenharmony_ci assert locals.seen_perms > 0 92619ea8026Sopenharmony_ci start += locals.seen_perms*step 92719ea8026Sopenharmony_ci continue 92819ea8026Sopenharmony_ci else: 92919ea8026Sopenharmony_ci # stop other tests 93019ea8026Sopenharmony_ci killed = True 93119ea8026Sopenharmony_ci for child in children.copy(): 93219ea8026Sopenharmony_ci child.kill() 93319ea8026Sopenharmony_ci break 93419ea8026Sopenharmony_ci 93519ea8026Sopenharmony_ci 93619ea8026Sopenharmony_ci # parallel jobs? 93719ea8026Sopenharmony_ci runners = [] 93819ea8026Sopenharmony_ci if 'jobs' in args: 93919ea8026Sopenharmony_ci for job in range(args['jobs']): 94019ea8026Sopenharmony_ci runners.append(th.Thread( 94119ea8026Sopenharmony_ci target=run_job, args=(runner_, ids, job, args['jobs']), 94219ea8026Sopenharmony_ci daemon=True)) 94319ea8026Sopenharmony_ci else: 94419ea8026Sopenharmony_ci runners.append(th.Thread( 94519ea8026Sopenharmony_ci target=run_job, args=(runner_, ids, None, None), 94619ea8026Sopenharmony_ci daemon=True)) 94719ea8026Sopenharmony_ci 94819ea8026Sopenharmony_ci def print_update(done): 94919ea8026Sopenharmony_ci if not args.get('verbose') and (args['color'] or done): 95019ea8026Sopenharmony_ci sys.stdout.write('%s%srunning %s%s:%s %s%s' % ( 95119ea8026Sopenharmony_ci '\r\x1b[K' if args['color'] else '', 95219ea8026Sopenharmony_ci '\x1b[?7l' if not done else '', 95319ea8026Sopenharmony_ci ('\x1b[32m' if not failures else '\x1b[31m') 95419ea8026Sopenharmony_ci if args['color'] else '', 95519ea8026Sopenharmony_ci name, 95619ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 95719ea8026Sopenharmony_ci ', '.join(filter(None, [ 95819ea8026Sopenharmony_ci '%d/%d suites' % ( 95919ea8026Sopenharmony_ci sum(passed_suite_perms[k] == v 96019ea8026Sopenharmony_ci for k, v in expected_suite_perms.items()), 96119ea8026Sopenharmony_ci len(expected_suite_perms)) 96219ea8026Sopenharmony_ci if (not args.get('by_suites') 96319ea8026Sopenharmony_ci and not args.get('by_cases')) else None, 96419ea8026Sopenharmony_ci '%d/%d cases' % ( 96519ea8026Sopenharmony_ci sum(passed_case_perms[k] == v 96619ea8026Sopenharmony_ci for k, v in expected_case_perms.items()), 96719ea8026Sopenharmony_ci len(expected_case_perms)) 96819ea8026Sopenharmony_ci if not args.get('by_cases') else None, 96919ea8026Sopenharmony_ci '%d/%d perms' % (passed_perms, expected_perms), 97019ea8026Sopenharmony_ci '%dpls!' % powerlosses 97119ea8026Sopenharmony_ci if powerlosses else None, 97219ea8026Sopenharmony_ci '%s%d/%d failures%s' % ( 97319ea8026Sopenharmony_ci '\x1b[31m' if args['color'] else '', 97419ea8026Sopenharmony_ci len(failures), 97519ea8026Sopenharmony_ci expected_perms, 97619ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '') 97719ea8026Sopenharmony_ci if failures else None])), 97819ea8026Sopenharmony_ci '\x1b[?7h' if not done else '\n')) 97919ea8026Sopenharmony_ci sys.stdout.flush() 98019ea8026Sopenharmony_ci 98119ea8026Sopenharmony_ci for r in runners: 98219ea8026Sopenharmony_ci r.start() 98319ea8026Sopenharmony_ci 98419ea8026Sopenharmony_ci try: 98519ea8026Sopenharmony_ci while any(r.is_alive() for r in runners): 98619ea8026Sopenharmony_ci time.sleep(0.01) 98719ea8026Sopenharmony_ci print_update(False) 98819ea8026Sopenharmony_ci except KeyboardInterrupt: 98919ea8026Sopenharmony_ci # this is handled by the runner threads, we just 99019ea8026Sopenharmony_ci # need to not abort here 99119ea8026Sopenharmony_ci killed = True 99219ea8026Sopenharmony_ci finally: 99319ea8026Sopenharmony_ci print_update(True) 99419ea8026Sopenharmony_ci 99519ea8026Sopenharmony_ci for r in runners: 99619ea8026Sopenharmony_ci r.join() 99719ea8026Sopenharmony_ci 99819ea8026Sopenharmony_ci return ( 99919ea8026Sopenharmony_ci expected_perms, 100019ea8026Sopenharmony_ci passed_perms, 100119ea8026Sopenharmony_ci powerlosses, 100219ea8026Sopenharmony_ci failures, 100319ea8026Sopenharmony_ci killed) 100419ea8026Sopenharmony_ci 100519ea8026Sopenharmony_ci 100619ea8026Sopenharmony_cidef run(runner, test_ids=[], **args): 100719ea8026Sopenharmony_ci # query runner for tests 100819ea8026Sopenharmony_ci runner_ = find_runner(runner, **args) 100919ea8026Sopenharmony_ci print('using runner: %s' % ' '.join(shlex.quote(c) for c in runner_)) 101019ea8026Sopenharmony_ci (_, 101119ea8026Sopenharmony_ci expected_suite_perms, 101219ea8026Sopenharmony_ci expected_case_perms, 101319ea8026Sopenharmony_ci expected_perms, 101419ea8026Sopenharmony_ci total_perms) = find_perms(runner_, test_ids, **args) 101519ea8026Sopenharmony_ci print('found %d suites, %d cases, %d/%d permutations' % ( 101619ea8026Sopenharmony_ci len(expected_suite_perms), 101719ea8026Sopenharmony_ci len(expected_case_perms), 101819ea8026Sopenharmony_ci expected_perms, 101919ea8026Sopenharmony_ci total_perms)) 102019ea8026Sopenharmony_ci print() 102119ea8026Sopenharmony_ci 102219ea8026Sopenharmony_ci # automatic job detection? 102319ea8026Sopenharmony_ci if args.get('jobs') == 0: 102419ea8026Sopenharmony_ci args['jobs'] = len(os.sched_getaffinity(0)) 102519ea8026Sopenharmony_ci 102619ea8026Sopenharmony_ci # truncate and open logs here so they aren't disconnected between tests 102719ea8026Sopenharmony_ci stdout = None 102819ea8026Sopenharmony_ci if args.get('stdout'): 102919ea8026Sopenharmony_ci stdout = openio(args['stdout'], 'w', 1) 103019ea8026Sopenharmony_ci trace = None 103119ea8026Sopenharmony_ci if args.get('trace'): 103219ea8026Sopenharmony_ci trace = openio(args['trace'], 'w', 1) 103319ea8026Sopenharmony_ci output = None 103419ea8026Sopenharmony_ci if args.get('output'): 103519ea8026Sopenharmony_ci output = TestOutput(args['output'], 103619ea8026Sopenharmony_ci ['suite', 'case'], 103719ea8026Sopenharmony_ci ['test_passed']) 103819ea8026Sopenharmony_ci 103919ea8026Sopenharmony_ci # measure runtime 104019ea8026Sopenharmony_ci start = time.time() 104119ea8026Sopenharmony_ci 104219ea8026Sopenharmony_ci # spawn runners 104319ea8026Sopenharmony_ci expected = 0 104419ea8026Sopenharmony_ci passed = 0 104519ea8026Sopenharmony_ci powerlosses = 0 104619ea8026Sopenharmony_ci failures = [] 104719ea8026Sopenharmony_ci for by in (test_ids if test_ids 104819ea8026Sopenharmony_ci else expected_case_perms.keys() if args.get('by_cases') 104919ea8026Sopenharmony_ci else expected_suite_perms.keys() if args.get('by_suites') 105019ea8026Sopenharmony_ci else [None]): 105119ea8026Sopenharmony_ci # spawn jobs for stage 105219ea8026Sopenharmony_ci (expected_, 105319ea8026Sopenharmony_ci passed_, 105419ea8026Sopenharmony_ci powerlosses_, 105519ea8026Sopenharmony_ci failures_, 105619ea8026Sopenharmony_ci killed) = run_stage( 105719ea8026Sopenharmony_ci by or 'tests', 105819ea8026Sopenharmony_ci runner_, 105919ea8026Sopenharmony_ci [by] if by is not None else [], 106019ea8026Sopenharmony_ci stdout, 106119ea8026Sopenharmony_ci trace, 106219ea8026Sopenharmony_ci output, 106319ea8026Sopenharmony_ci **args) 106419ea8026Sopenharmony_ci # collect passes/failures 106519ea8026Sopenharmony_ci expected += expected_ 106619ea8026Sopenharmony_ci passed += passed_ 106719ea8026Sopenharmony_ci powerlosses += powerlosses_ 106819ea8026Sopenharmony_ci failures.extend(failures_) 106919ea8026Sopenharmony_ci if (failures and not args.get('keep_going')) or killed: 107019ea8026Sopenharmony_ci break 107119ea8026Sopenharmony_ci 107219ea8026Sopenharmony_ci stop = time.time() 107319ea8026Sopenharmony_ci 107419ea8026Sopenharmony_ci if stdout: 107519ea8026Sopenharmony_ci try: 107619ea8026Sopenharmony_ci stdout.close() 107719ea8026Sopenharmony_ci except BrokenPipeError: 107819ea8026Sopenharmony_ci pass 107919ea8026Sopenharmony_ci if trace: 108019ea8026Sopenharmony_ci try: 108119ea8026Sopenharmony_ci trace.close() 108219ea8026Sopenharmony_ci except BrokenPipeError: 108319ea8026Sopenharmony_ci pass 108419ea8026Sopenharmony_ci if output: 108519ea8026Sopenharmony_ci output.close() 108619ea8026Sopenharmony_ci 108719ea8026Sopenharmony_ci # show summary 108819ea8026Sopenharmony_ci print() 108919ea8026Sopenharmony_ci print('%sdone:%s %s' % ( 109019ea8026Sopenharmony_ci ('\x1b[32m' if not failures else '\x1b[31m') 109119ea8026Sopenharmony_ci if args['color'] else '', 109219ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 109319ea8026Sopenharmony_ci ', '.join(filter(None, [ 109419ea8026Sopenharmony_ci '%d/%d passed' % (passed, expected), 109519ea8026Sopenharmony_ci '%d/%d failed' % (len(failures), expected), 109619ea8026Sopenharmony_ci '%dpls!' % powerlosses if powerlosses else None, 109719ea8026Sopenharmony_ci 'in %.2fs' % (stop-start)])))) 109819ea8026Sopenharmony_ci print() 109919ea8026Sopenharmony_ci 110019ea8026Sopenharmony_ci # print each failure 110119ea8026Sopenharmony_ci for failure in failures: 110219ea8026Sopenharmony_ci assert failure.id is not None, '%s broken? %r' % ( 110319ea8026Sopenharmony_ci ' '.join(shlex.quote(c) for c in runner_), 110419ea8026Sopenharmony_ci failure) 110519ea8026Sopenharmony_ci 110619ea8026Sopenharmony_ci # get some extra info from runner 110719ea8026Sopenharmony_ci path, lineno = find_path(runner_, failure.id, **args) 110819ea8026Sopenharmony_ci defines = find_defines(runner_, failure.id, **args) 110919ea8026Sopenharmony_ci 111019ea8026Sopenharmony_ci # show summary of failure 111119ea8026Sopenharmony_ci print('%s%s:%d:%sfailure:%s %s%s failed' % ( 111219ea8026Sopenharmony_ci '\x1b[01m' if args['color'] else '', 111319ea8026Sopenharmony_ci path, lineno, 111419ea8026Sopenharmony_ci '\x1b[01;31m' if args['color'] else '', 111519ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 111619ea8026Sopenharmony_ci failure.id, 111719ea8026Sopenharmony_ci ' (%s)' % ', '.join('%s=%s' % (k,v) for k,v in defines.items()) 111819ea8026Sopenharmony_ci if defines else '')) 111919ea8026Sopenharmony_ci 112019ea8026Sopenharmony_ci if failure.stdout: 112119ea8026Sopenharmony_ci stdout = failure.stdout 112219ea8026Sopenharmony_ci if failure.assert_ is not None: 112319ea8026Sopenharmony_ci stdout = stdout[:-1] 112419ea8026Sopenharmony_ci for line in stdout[-args.get('context', 5):]: 112519ea8026Sopenharmony_ci sys.stdout.write(line) 112619ea8026Sopenharmony_ci 112719ea8026Sopenharmony_ci if failure.assert_ is not None: 112819ea8026Sopenharmony_ci path, lineno, message = failure.assert_ 112919ea8026Sopenharmony_ci print('%s%s:%d:%sassert:%s %s' % ( 113019ea8026Sopenharmony_ci '\x1b[01m' if args['color'] else '', 113119ea8026Sopenharmony_ci path, lineno, 113219ea8026Sopenharmony_ci '\x1b[01;31m' if args['color'] else '', 113319ea8026Sopenharmony_ci '\x1b[m' if args['color'] else '', 113419ea8026Sopenharmony_ci message)) 113519ea8026Sopenharmony_ci with open(path) as f: 113619ea8026Sopenharmony_ci line = next(it.islice(f, lineno-1, None)).strip('\n') 113719ea8026Sopenharmony_ci print(line) 113819ea8026Sopenharmony_ci print() 113919ea8026Sopenharmony_ci 114019ea8026Sopenharmony_ci # drop into gdb? 114119ea8026Sopenharmony_ci if failures and (args.get('gdb') 114219ea8026Sopenharmony_ci or args.get('gdb_case') 114319ea8026Sopenharmony_ci or args.get('gdb_main') 114419ea8026Sopenharmony_ci or args.get('gdb_pl') is not None 114519ea8026Sopenharmony_ci or args.get('gdb_pl_before') 114619ea8026Sopenharmony_ci or args.get('gdb_pl_after')): 114719ea8026Sopenharmony_ci failure = failures[0] 114819ea8026Sopenharmony_ci cmd = runner_ + [failure.id] 114919ea8026Sopenharmony_ci 115019ea8026Sopenharmony_ci if args.get('gdb_main'): 115119ea8026Sopenharmony_ci # we don't really need the case breakpoint here, but it 115219ea8026Sopenharmony_ci # can be helpful 115319ea8026Sopenharmony_ci path, lineno = find_path(runner_, failure.id, **args) 115419ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 115519ea8026Sopenharmony_ci '-ex', 'break main', 115619ea8026Sopenharmony_ci '-ex', 'break %s:%d' % (path, lineno), 115719ea8026Sopenharmony_ci '-ex', 'run', 115819ea8026Sopenharmony_ci '--args'] 115919ea8026Sopenharmony_ci elif args.get('gdb_case'): 116019ea8026Sopenharmony_ci path, lineno = find_path(runner_, failure.id, **args) 116119ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 116219ea8026Sopenharmony_ci '-ex', 'break %s:%d' % (path, lineno), 116319ea8026Sopenharmony_ci '-ex', 'run', 116419ea8026Sopenharmony_ci '--args'] 116519ea8026Sopenharmony_ci elif args.get('gdb_pl') is not None: 116619ea8026Sopenharmony_ci path, lineno = find_path(runner_, failure.id, **args) 116719ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 116819ea8026Sopenharmony_ci '-ex', 'break %s:%d' % (path, lineno), 116919ea8026Sopenharmony_ci '-ex', 'ignore 1 %d' % args['gdb_pl'], 117019ea8026Sopenharmony_ci '-ex', 'run', 117119ea8026Sopenharmony_ci '--args'] 117219ea8026Sopenharmony_ci elif args.get('gdb_pl_before'): 117319ea8026Sopenharmony_ci # figure out how many powerlosses there were 117419ea8026Sopenharmony_ci powerlosses = ( 117519ea8026Sopenharmony_ci sum(1 for _ in re.finditer('[0-9a-f]', 117619ea8026Sopenharmony_ci failure.id.split(':', 2)[-1])) 117719ea8026Sopenharmony_ci if failure.id.count(':') >= 2 else 0) 117819ea8026Sopenharmony_ci path, lineno = find_path(runner_, failure.id, **args) 117919ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 118019ea8026Sopenharmony_ci '-ex', 'break %s:%d' % (path, lineno), 118119ea8026Sopenharmony_ci '-ex', 'ignore 1 %d' % max(powerlosses-1, 0), 118219ea8026Sopenharmony_ci '-ex', 'run', 118319ea8026Sopenharmony_ci '--args'] 118419ea8026Sopenharmony_ci elif args.get('gdb_pl_after'): 118519ea8026Sopenharmony_ci # figure out how many powerlosses there were 118619ea8026Sopenharmony_ci powerlosses = ( 118719ea8026Sopenharmony_ci sum(1 for _ in re.finditer('[0-9a-f]', 118819ea8026Sopenharmony_ci failure.id.split(':', 2)[-1])) 118919ea8026Sopenharmony_ci if failure.id.count(':') >= 2 else 0) 119019ea8026Sopenharmony_ci path, lineno = find_path(runner_, failure.id, **args) 119119ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 119219ea8026Sopenharmony_ci '-ex', 'break %s:%d' % (path, lineno), 119319ea8026Sopenharmony_ci '-ex', 'ignore 1 %d' % powerlosses, 119419ea8026Sopenharmony_ci '-ex', 'run', 119519ea8026Sopenharmony_ci '--args'] 119619ea8026Sopenharmony_ci elif failure.assert_ is not None: 119719ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 119819ea8026Sopenharmony_ci '-ex', 'run', 119919ea8026Sopenharmony_ci '-ex', 'frame function raise', 120019ea8026Sopenharmony_ci '-ex', 'up 2', 120119ea8026Sopenharmony_ci '--args'] 120219ea8026Sopenharmony_ci else: 120319ea8026Sopenharmony_ci cmd[:0] = args['gdb_path'] + [ 120419ea8026Sopenharmony_ci '-ex', 'run', 120519ea8026Sopenharmony_ci '--args'] 120619ea8026Sopenharmony_ci 120719ea8026Sopenharmony_ci # exec gdb interactively 120819ea8026Sopenharmony_ci if args.get('verbose'): 120919ea8026Sopenharmony_ci print(' '.join(shlex.quote(c) for c in cmd)) 121019ea8026Sopenharmony_ci os.execvp(cmd[0], cmd) 121119ea8026Sopenharmony_ci 121219ea8026Sopenharmony_ci return 1 if failures else 0 121319ea8026Sopenharmony_ci 121419ea8026Sopenharmony_ci 121519ea8026Sopenharmony_cidef main(**args): 121619ea8026Sopenharmony_ci # figure out what color should be 121719ea8026Sopenharmony_ci if args.get('color') == 'auto': 121819ea8026Sopenharmony_ci args['color'] = sys.stdout.isatty() 121919ea8026Sopenharmony_ci elif args.get('color') == 'always': 122019ea8026Sopenharmony_ci args['color'] = True 122119ea8026Sopenharmony_ci else: 122219ea8026Sopenharmony_ci args['color'] = False 122319ea8026Sopenharmony_ci 122419ea8026Sopenharmony_ci if args.get('compile'): 122519ea8026Sopenharmony_ci return compile(**args) 122619ea8026Sopenharmony_ci elif (args.get('summary') 122719ea8026Sopenharmony_ci or args.get('list_suites') 122819ea8026Sopenharmony_ci or args.get('list_cases') 122919ea8026Sopenharmony_ci or args.get('list_suite_paths') 123019ea8026Sopenharmony_ci or args.get('list_case_paths') 123119ea8026Sopenharmony_ci or args.get('list_defines') 123219ea8026Sopenharmony_ci or args.get('list_permutation_defines') 123319ea8026Sopenharmony_ci or args.get('list_implicit_defines') 123419ea8026Sopenharmony_ci or args.get('list_geometries') 123519ea8026Sopenharmony_ci or args.get('list_powerlosses')): 123619ea8026Sopenharmony_ci return list_(**args) 123719ea8026Sopenharmony_ci else: 123819ea8026Sopenharmony_ci return run(**args) 123919ea8026Sopenharmony_ci 124019ea8026Sopenharmony_ci 124119ea8026Sopenharmony_ciif __name__ == "__main__": 124219ea8026Sopenharmony_ci import argparse 124319ea8026Sopenharmony_ci import sys 124419ea8026Sopenharmony_ci argparse.ArgumentParser._handle_conflict_ignore = lambda *_: None 124519ea8026Sopenharmony_ci argparse._ArgumentGroup._handle_conflict_ignore = lambda *_: None 124619ea8026Sopenharmony_ci parser = argparse.ArgumentParser( 124719ea8026Sopenharmony_ci description="Build and run tests.", 124819ea8026Sopenharmony_ci allow_abbrev=False, 124919ea8026Sopenharmony_ci conflict_handler='ignore') 125019ea8026Sopenharmony_ci parser.add_argument( 125119ea8026Sopenharmony_ci '-v', '--verbose', 125219ea8026Sopenharmony_ci action='store_true', 125319ea8026Sopenharmony_ci help="Output commands that run behind the scenes.") 125419ea8026Sopenharmony_ci parser.add_argument( 125519ea8026Sopenharmony_ci '--color', 125619ea8026Sopenharmony_ci choices=['never', 'always', 'auto'], 125719ea8026Sopenharmony_ci default='auto', 125819ea8026Sopenharmony_ci help="When to use terminal colors. Defaults to 'auto'.") 125919ea8026Sopenharmony_ci 126019ea8026Sopenharmony_ci # test flags 126119ea8026Sopenharmony_ci test_parser = parser.add_argument_group('test options') 126219ea8026Sopenharmony_ci test_parser.add_argument( 126319ea8026Sopenharmony_ci 'runner', 126419ea8026Sopenharmony_ci nargs='?', 126519ea8026Sopenharmony_ci type=lambda x: x.split(), 126619ea8026Sopenharmony_ci help="Test runner to use for testing. Defaults to %r." % RUNNER_PATH) 126719ea8026Sopenharmony_ci test_parser.add_argument( 126819ea8026Sopenharmony_ci 'test_ids', 126919ea8026Sopenharmony_ci nargs='*', 127019ea8026Sopenharmony_ci help="Description of tests to run.") 127119ea8026Sopenharmony_ci test_parser.add_argument( 127219ea8026Sopenharmony_ci '-Y', '--summary', 127319ea8026Sopenharmony_ci action='store_true', 127419ea8026Sopenharmony_ci help="Show quick summary.") 127519ea8026Sopenharmony_ci test_parser.add_argument( 127619ea8026Sopenharmony_ci '-l', '--list-suites', 127719ea8026Sopenharmony_ci action='store_true', 127819ea8026Sopenharmony_ci help="List test suites.") 127919ea8026Sopenharmony_ci test_parser.add_argument( 128019ea8026Sopenharmony_ci '-L', '--list-cases', 128119ea8026Sopenharmony_ci action='store_true', 128219ea8026Sopenharmony_ci help="List test cases.") 128319ea8026Sopenharmony_ci test_parser.add_argument( 128419ea8026Sopenharmony_ci '--list-suite-paths', 128519ea8026Sopenharmony_ci action='store_true', 128619ea8026Sopenharmony_ci help="List the path for each test suite.") 128719ea8026Sopenharmony_ci test_parser.add_argument( 128819ea8026Sopenharmony_ci '--list-case-paths', 128919ea8026Sopenharmony_ci action='store_true', 129019ea8026Sopenharmony_ci help="List the path and line number for each test case.") 129119ea8026Sopenharmony_ci test_parser.add_argument( 129219ea8026Sopenharmony_ci '--list-defines', 129319ea8026Sopenharmony_ci action='store_true', 129419ea8026Sopenharmony_ci help="List all defines in this test-runner.") 129519ea8026Sopenharmony_ci test_parser.add_argument( 129619ea8026Sopenharmony_ci '--list-permutation-defines', 129719ea8026Sopenharmony_ci action='store_true', 129819ea8026Sopenharmony_ci help="List explicit defines in this test-runner.") 129919ea8026Sopenharmony_ci test_parser.add_argument( 130019ea8026Sopenharmony_ci '--list-implicit-defines', 130119ea8026Sopenharmony_ci action='store_true', 130219ea8026Sopenharmony_ci help="List implicit defines in this test-runner.") 130319ea8026Sopenharmony_ci test_parser.add_argument( 130419ea8026Sopenharmony_ci '--list-geometries', 130519ea8026Sopenharmony_ci action='store_true', 130619ea8026Sopenharmony_ci help="List the available disk geometries.") 130719ea8026Sopenharmony_ci test_parser.add_argument( 130819ea8026Sopenharmony_ci '--list-powerlosses', 130919ea8026Sopenharmony_ci action='store_true', 131019ea8026Sopenharmony_ci help="List the available power-loss scenarios.") 131119ea8026Sopenharmony_ci test_parser.add_argument( 131219ea8026Sopenharmony_ci '-D', '--define', 131319ea8026Sopenharmony_ci action='append', 131419ea8026Sopenharmony_ci help="Override a test define.") 131519ea8026Sopenharmony_ci test_parser.add_argument( 131619ea8026Sopenharmony_ci '-G', '--geometry', 131719ea8026Sopenharmony_ci help="Comma-separated list of disk geometries to test.") 131819ea8026Sopenharmony_ci test_parser.add_argument( 131919ea8026Sopenharmony_ci '-P', '--powerloss', 132019ea8026Sopenharmony_ci help="Comma-separated list of power-loss scenarios to test.") 132119ea8026Sopenharmony_ci test_parser.add_argument( 132219ea8026Sopenharmony_ci '-d', '--disk', 132319ea8026Sopenharmony_ci help="Direct block device operations to this file.") 132419ea8026Sopenharmony_ci test_parser.add_argument( 132519ea8026Sopenharmony_ci '-t', '--trace', 132619ea8026Sopenharmony_ci help="Direct trace output to this file.") 132719ea8026Sopenharmony_ci test_parser.add_argument( 132819ea8026Sopenharmony_ci '--trace-backtrace', 132919ea8026Sopenharmony_ci action='store_true', 133019ea8026Sopenharmony_ci help="Include a backtrace with every trace statement.") 133119ea8026Sopenharmony_ci test_parser.add_argument( 133219ea8026Sopenharmony_ci '--trace-period', 133319ea8026Sopenharmony_ci help="Sample trace output at this period in cycles.") 133419ea8026Sopenharmony_ci test_parser.add_argument( 133519ea8026Sopenharmony_ci '--trace-freq', 133619ea8026Sopenharmony_ci help="Sample trace output at this frequency in hz.") 133719ea8026Sopenharmony_ci test_parser.add_argument( 133819ea8026Sopenharmony_ci '-O', '--stdout', 133919ea8026Sopenharmony_ci help="Direct stdout to this file. Note stderr is already merged here.") 134019ea8026Sopenharmony_ci test_parser.add_argument( 134119ea8026Sopenharmony_ci '-o', '--output', 134219ea8026Sopenharmony_ci help="CSV file to store results.") 134319ea8026Sopenharmony_ci test_parser.add_argument( 134419ea8026Sopenharmony_ci '--read-sleep', 134519ea8026Sopenharmony_ci help="Artificial read delay in seconds.") 134619ea8026Sopenharmony_ci test_parser.add_argument( 134719ea8026Sopenharmony_ci '--prog-sleep', 134819ea8026Sopenharmony_ci help="Artificial prog delay in seconds.") 134919ea8026Sopenharmony_ci test_parser.add_argument( 135019ea8026Sopenharmony_ci '--erase-sleep', 135119ea8026Sopenharmony_ci help="Artificial erase delay in seconds.") 135219ea8026Sopenharmony_ci test_parser.add_argument( 135319ea8026Sopenharmony_ci '-j', '--jobs', 135419ea8026Sopenharmony_ci nargs='?', 135519ea8026Sopenharmony_ci type=lambda x: int(x, 0), 135619ea8026Sopenharmony_ci const=0, 135719ea8026Sopenharmony_ci help="Number of parallel runners to run. 0 runs one runner per core.") 135819ea8026Sopenharmony_ci test_parser.add_argument( 135919ea8026Sopenharmony_ci '-k', '--keep-going', 136019ea8026Sopenharmony_ci action='store_true', 136119ea8026Sopenharmony_ci help="Don't stop on first error.") 136219ea8026Sopenharmony_ci test_parser.add_argument( 136319ea8026Sopenharmony_ci '-i', '--isolate', 136419ea8026Sopenharmony_ci action='store_true', 136519ea8026Sopenharmony_ci help="Run each test permutation in a separate process.") 136619ea8026Sopenharmony_ci test_parser.add_argument( 136719ea8026Sopenharmony_ci '-b', '--by-suites', 136819ea8026Sopenharmony_ci action='store_true', 136919ea8026Sopenharmony_ci help="Step through tests by suite.") 137019ea8026Sopenharmony_ci test_parser.add_argument( 137119ea8026Sopenharmony_ci '-B', '--by-cases', 137219ea8026Sopenharmony_ci action='store_true', 137319ea8026Sopenharmony_ci help="Step through tests by case.") 137419ea8026Sopenharmony_ci test_parser.add_argument( 137519ea8026Sopenharmony_ci '--context', 137619ea8026Sopenharmony_ci type=lambda x: int(x, 0), 137719ea8026Sopenharmony_ci default=5, 137819ea8026Sopenharmony_ci help="Show this many lines of stdout on test failure. " 137919ea8026Sopenharmony_ci "Defaults to 5.") 138019ea8026Sopenharmony_ci test_parser.add_argument( 138119ea8026Sopenharmony_ci '--gdb', 138219ea8026Sopenharmony_ci action='store_true', 138319ea8026Sopenharmony_ci help="Drop into gdb on test failure.") 138419ea8026Sopenharmony_ci test_parser.add_argument( 138519ea8026Sopenharmony_ci '--gdb-case', 138619ea8026Sopenharmony_ci action='store_true', 138719ea8026Sopenharmony_ci help="Drop into gdb on test failure but stop at the beginning " 138819ea8026Sopenharmony_ci "of the failing test case.") 138919ea8026Sopenharmony_ci test_parser.add_argument( 139019ea8026Sopenharmony_ci '--gdb-main', 139119ea8026Sopenharmony_ci action='store_true', 139219ea8026Sopenharmony_ci help="Drop into gdb on test failure but stop at the beginning " 139319ea8026Sopenharmony_ci "of main.") 139419ea8026Sopenharmony_ci test_parser.add_argument( 139519ea8026Sopenharmony_ci '--gdb-pl', 139619ea8026Sopenharmony_ci type=lambda x: int(x, 0), 139719ea8026Sopenharmony_ci help="Drop into gdb on this specific powerloss.") 139819ea8026Sopenharmony_ci test_parser.add_argument( 139919ea8026Sopenharmony_ci '--gdb-pl-before', 140019ea8026Sopenharmony_ci action='store_true', 140119ea8026Sopenharmony_ci help="Drop into gdb before the powerloss that caused the failure.") 140219ea8026Sopenharmony_ci test_parser.add_argument( 140319ea8026Sopenharmony_ci '--gdb-pl-after', 140419ea8026Sopenharmony_ci action='store_true', 140519ea8026Sopenharmony_ci help="Drop into gdb after the powerloss that caused the failure.") 140619ea8026Sopenharmony_ci test_parser.add_argument( 140719ea8026Sopenharmony_ci '--gdb-path', 140819ea8026Sopenharmony_ci type=lambda x: x.split(), 140919ea8026Sopenharmony_ci default=GDB_PATH, 141019ea8026Sopenharmony_ci help="Path to the gdb executable, may include flags. " 141119ea8026Sopenharmony_ci "Defaults to %r." % GDB_PATH) 141219ea8026Sopenharmony_ci test_parser.add_argument( 141319ea8026Sopenharmony_ci '--exec', 141419ea8026Sopenharmony_ci type=lambda e: e.split(), 141519ea8026Sopenharmony_ci help="Run under another executable.") 141619ea8026Sopenharmony_ci test_parser.add_argument( 141719ea8026Sopenharmony_ci '--valgrind', 141819ea8026Sopenharmony_ci action='store_true', 141919ea8026Sopenharmony_ci help="Run under Valgrind to find memory errors. Implicitly sets " 142019ea8026Sopenharmony_ci "--isolate.") 142119ea8026Sopenharmony_ci test_parser.add_argument( 142219ea8026Sopenharmony_ci '--valgrind-path', 142319ea8026Sopenharmony_ci type=lambda x: x.split(), 142419ea8026Sopenharmony_ci default=VALGRIND_PATH, 142519ea8026Sopenharmony_ci help="Path to the Valgrind executable, may include flags. " 142619ea8026Sopenharmony_ci "Defaults to %r." % VALGRIND_PATH) 142719ea8026Sopenharmony_ci test_parser.add_argument( 142819ea8026Sopenharmony_ci '-p', '--perf', 142919ea8026Sopenharmony_ci help="Run under Linux's perf to sample performance counters, writing " 143019ea8026Sopenharmony_ci "samples to this file.") 143119ea8026Sopenharmony_ci test_parser.add_argument( 143219ea8026Sopenharmony_ci '--perf-freq', 143319ea8026Sopenharmony_ci help="perf sampling frequency. This is passed directly to the perf " 143419ea8026Sopenharmony_ci "script.") 143519ea8026Sopenharmony_ci test_parser.add_argument( 143619ea8026Sopenharmony_ci '--perf-period', 143719ea8026Sopenharmony_ci help="perf sampling period. This is passed directly to the perf " 143819ea8026Sopenharmony_ci "script.") 143919ea8026Sopenharmony_ci test_parser.add_argument( 144019ea8026Sopenharmony_ci '--perf-events', 144119ea8026Sopenharmony_ci help="perf events to record. This is passed directly to the perf " 144219ea8026Sopenharmony_ci "script.") 144319ea8026Sopenharmony_ci test_parser.add_argument( 144419ea8026Sopenharmony_ci '--perf-script', 144519ea8026Sopenharmony_ci type=lambda x: x.split(), 144619ea8026Sopenharmony_ci default=PERF_SCRIPT, 144719ea8026Sopenharmony_ci help="Path to the perf script to use. Defaults to %r." % PERF_SCRIPT) 144819ea8026Sopenharmony_ci test_parser.add_argument( 144919ea8026Sopenharmony_ci '--perf-path', 145019ea8026Sopenharmony_ci type=lambda x: x.split(), 145119ea8026Sopenharmony_ci help="Path to the perf executable, may include flags. This is passed " 145219ea8026Sopenharmony_ci "directly to the perf script") 145319ea8026Sopenharmony_ci 145419ea8026Sopenharmony_ci # compilation flags 145519ea8026Sopenharmony_ci comp_parser = parser.add_argument_group('compilation options') 145619ea8026Sopenharmony_ci comp_parser.add_argument( 145719ea8026Sopenharmony_ci 'test_paths', 145819ea8026Sopenharmony_ci nargs='*', 145919ea8026Sopenharmony_ci help="Description of *.toml files to compile. May be a directory " 146019ea8026Sopenharmony_ci "or a list of paths.") 146119ea8026Sopenharmony_ci comp_parser.add_argument( 146219ea8026Sopenharmony_ci '-c', '--compile', 146319ea8026Sopenharmony_ci action='store_true', 146419ea8026Sopenharmony_ci help="Compile a test suite or source file.") 146519ea8026Sopenharmony_ci comp_parser.add_argument( 146619ea8026Sopenharmony_ci '-s', '--source', 146719ea8026Sopenharmony_ci help="Source file to compile, possibly injecting internal tests.") 146819ea8026Sopenharmony_ci comp_parser.add_argument( 146919ea8026Sopenharmony_ci '--include', 147019ea8026Sopenharmony_ci default=HEADER_PATH, 147119ea8026Sopenharmony_ci help="Inject this header file into every compiled test file. " 147219ea8026Sopenharmony_ci "Defaults to %r." % HEADER_PATH) 147319ea8026Sopenharmony_ci comp_parser.add_argument( 147419ea8026Sopenharmony_ci '-o', '--output', 147519ea8026Sopenharmony_ci help="Output file.") 147619ea8026Sopenharmony_ci 147719ea8026Sopenharmony_ci # runner/test_paths overlap, so need to do some munging here 147819ea8026Sopenharmony_ci args = parser.parse_intermixed_args() 147919ea8026Sopenharmony_ci args.test_paths = [' '.join(args.runner or [])] + args.test_ids 148019ea8026Sopenharmony_ci args.runner = args.runner or [RUNNER_PATH] 148119ea8026Sopenharmony_ci 148219ea8026Sopenharmony_ci sys.exit(main(**{k: v 148319ea8026Sopenharmony_ci for k, v in vars(args).items() 148419ea8026Sopenharmony_ci if v is not None})) 1485