109467b48Spatrick""" 209467b48Spatricklit - LLVM Integrated Tester. 309467b48Spatrick 409467b48SpatrickSee lit.pod for more information. 509467b48Spatrick""" 609467b48Spatrick 7097a140dSpatrickimport itertools 809467b48Spatrickimport os 909467b48Spatrickimport platform 1009467b48Spatrickimport sys 1109467b48Spatrickimport time 1209467b48Spatrick 1309467b48Spatrickimport lit.cl_arguments 1409467b48Spatrickimport lit.discovery 1509467b48Spatrickimport lit.display 1609467b48Spatrickimport lit.LitConfig 17097a140dSpatrickimport lit.reports 1809467b48Spatrickimport lit.run 1909467b48Spatrickimport lit.Test 2009467b48Spatrickimport lit.util 21*d415bd75Srobertfrom lit.formats.googletest import GoogleTest 2273471bf0Spatrickfrom lit.TestTimes import record_test_times 2309467b48Spatrick 2409467b48Spatrick 2509467b48Spatrickdef main(builtin_params={}): 2609467b48Spatrick opts = lit.cl_arguments.parse_args() 2709467b48Spatrick params = create_params(builtin_params, opts.user_params) 2809467b48Spatrick is_windows = platform.system() == 'Windows' 2909467b48Spatrick 3009467b48Spatrick lit_config = lit.LitConfig.LitConfig( 3109467b48Spatrick progname=os.path.basename(sys.argv[0]), 3209467b48Spatrick path=opts.path, 3309467b48Spatrick quiet=opts.quiet, 3409467b48Spatrick useValgrind=opts.useValgrind, 3509467b48Spatrick valgrindLeakCheck=opts.valgrindLeakCheck, 3609467b48Spatrick valgrindArgs=opts.valgrindArgs, 3709467b48Spatrick noExecute=opts.noExecute, 3809467b48Spatrick debug=opts.debug, 3909467b48Spatrick isWindows=is_windows, 40*d415bd75Srobert order=opts.order, 4109467b48Spatrick params=params, 4209467b48Spatrick config_prefix=opts.configPrefix, 4309467b48Spatrick echo_all_commands=opts.echoAllCommands) 4409467b48Spatrick 4573471bf0Spatrick discovered_tests = lit.discovery.find_tests_for_inputs(lit_config, opts.test_paths, 4673471bf0Spatrick opts.indirectlyRunCheck) 4709467b48Spatrick if not discovered_tests: 48097a140dSpatrick sys.stderr.write('error: did not discover any tests for provided path(s)\n') 4909467b48Spatrick sys.exit(2) 5009467b48Spatrick 51097a140dSpatrick if opts.show_suites or opts.show_tests: 52097a140dSpatrick print_discovered(discovered_tests, opts.show_suites, opts.show_tests) 53097a140dSpatrick sys.exit(0) 54097a140dSpatrick 55097a140dSpatrick if opts.show_used_features: 56*d415bd75Srobert features = set(itertools.chain.from_iterable(t.getUsedFeatures() for t in discovered_tests if t.gtest_json_file is None)) 57097a140dSpatrick print(' '.join(sorted(features))) 58097a140dSpatrick sys.exit(0) 59097a140dSpatrick 6009467b48Spatrick # Command line overrides configuration for maxIndividualTestTime. 6109467b48Spatrick if opts.maxIndividualTestTime is not None: # `not None` is important (default: 0) 6209467b48Spatrick if opts.maxIndividualTestTime != lit_config.maxIndividualTestTime: 6309467b48Spatrick lit_config.note(('The test suite configuration requested an individual' 6409467b48Spatrick ' test timeout of {0} seconds but a timeout of {1} seconds was' 6509467b48Spatrick ' requested on the command line. Forcing timeout to be {1}' 6609467b48Spatrick ' seconds') 6709467b48Spatrick .format(lit_config.maxIndividualTestTime, 6809467b48Spatrick opts.maxIndividualTestTime)) 6909467b48Spatrick lit_config.maxIndividualTestTime = opts.maxIndividualTestTime 7009467b48Spatrick 71097a140dSpatrick determine_order(discovered_tests, opts.order) 7209467b48Spatrick 73097a140dSpatrick selected_tests = [t for t in discovered_tests if 7473471bf0Spatrick opts.filter.search(t.getFullName()) and not 7573471bf0Spatrick opts.filter_out.search(t.getFullName())] 7673471bf0Spatrick 77097a140dSpatrick if not selected_tests: 7809467b48Spatrick sys.stderr.write('error: filter did not match any tests ' 7909467b48Spatrick '(of %d discovered). ' % len(discovered_tests)) 8009467b48Spatrick if opts.allow_empty_runs: 81097a140dSpatrick sys.stderr.write("Suppressing error because '--allow-empty-runs' " 82097a140dSpatrick 'was specified.\n') 8309467b48Spatrick sys.exit(0) 8409467b48Spatrick else: 8509467b48Spatrick sys.stderr.write("Use '--allow-empty-runs' to suppress this " 8609467b48Spatrick 'error.\n') 8709467b48Spatrick sys.exit(2) 8809467b48Spatrick 8973471bf0Spatrick # When running multiple shards, don't include skipped tests in the xunit 9073471bf0Spatrick # output since merging the files will result in duplicates. 9109467b48Spatrick if opts.shard: 9209467b48Spatrick (run, shards) = opts.shard 93097a140dSpatrick selected_tests = filter_by_shard(selected_tests, run, shards, lit_config) 94097a140dSpatrick if not selected_tests: 9509467b48Spatrick sys.stderr.write('warning: shard does not contain any tests. ' 9609467b48Spatrick 'Consider decreasing the number of shards.\n') 9709467b48Spatrick sys.exit(0) 9809467b48Spatrick 99097a140dSpatrick selected_tests = selected_tests[:opts.max_tests] 10009467b48Spatrick 10173471bf0Spatrick mark_xfail(discovered_tests, opts) 10273471bf0Spatrick 103097a140dSpatrick mark_excluded(discovered_tests, selected_tests) 10409467b48Spatrick 10509467b48Spatrick start = time.time() 106097a140dSpatrick run_tests(selected_tests, lit_config, opts, len(discovered_tests)) 10709467b48Spatrick elapsed = time.time() - start 10809467b48Spatrick 10973471bf0Spatrick record_test_times(selected_tests, lit_config) 11073471bf0Spatrick 111*d415bd75Srobert selected_tests, discovered_tests = GoogleTest.post_process_shard_results( 112*d415bd75Srobert selected_tests, discovered_tests) 113*d415bd75Srobert 114097a140dSpatrick if opts.time_tests: 115097a140dSpatrick print_histogram(discovered_tests) 11609467b48Spatrick 117097a140dSpatrick print_results(discovered_tests, elapsed, opts) 11809467b48Spatrick 119*d415bd75Srobert tests_for_report = selected_tests if opts.shard else discovered_tests 120097a140dSpatrick for report in opts.reports: 12173471bf0Spatrick report.write_results(tests_for_report, elapsed) 12209467b48Spatrick 12309467b48Spatrick if lit_config.numErrors: 12409467b48Spatrick sys.stderr.write('\n%d error(s) in tests\n' % lit_config.numErrors) 12509467b48Spatrick sys.exit(2) 12609467b48Spatrick 12709467b48Spatrick if lit_config.numWarnings: 12809467b48Spatrick sys.stderr.write('\n%d warning(s) in tests\n' % lit_config.numWarnings) 12909467b48Spatrick 130097a140dSpatrick has_failure = any(t.isFailure() for t in discovered_tests) 13109467b48Spatrick if has_failure: 13273471bf0Spatrick if opts.ignoreFail: 13373471bf0Spatrick sys.stderr.write("\nExiting with status 0 instead of 1 because " 13473471bf0Spatrick "'--ignore-fail' was specified.\n") 13573471bf0Spatrick else: 13609467b48Spatrick sys.exit(1) 13709467b48Spatrick 13809467b48Spatrickdef create_params(builtin_params, user_params): 13909467b48Spatrick def parse(p): 14009467b48Spatrick return p.split('=', 1) if '=' in p else (p, '') 14109467b48Spatrick 14209467b48Spatrick params = dict(builtin_params) 14309467b48Spatrick params.update([parse(p) for p in user_params]) 14409467b48Spatrick return params 14509467b48Spatrick 14609467b48Spatrick 147097a140dSpatrickdef print_discovered(tests, show_suites, show_tests): 148097a140dSpatrick tests.sort(key=lit.reports.by_suite_and_test_path) 149097a140dSpatrick 150097a140dSpatrick if show_suites: 151097a140dSpatrick tests_by_suite = itertools.groupby(tests, lambda t: t.suite) 15209467b48Spatrick print('-- Test Suites --') 153097a140dSpatrick for suite, test_iter in tests_by_suite: 154097a140dSpatrick test_count = sum(1 for _ in test_iter) 155097a140dSpatrick print(' %s - %d tests' % (suite.name, test_count)) 156097a140dSpatrick print(' Source Root: %s' % suite.source_root) 157097a140dSpatrick print(' Exec Root : %s' % suite.exec_root) 158097a140dSpatrick features = ' '.join(sorted(suite.config.available_features)) 159097a140dSpatrick print(' Available Features: %s' % features) 160097a140dSpatrick substitutions = sorted(suite.config.substitutions) 161097a140dSpatrick substitutions = ('%s => %s' % (x, y) for (x, y) in substitutions) 162097a140dSpatrick substitutions = '\n'.ljust(30).join(substitutions) 163097a140dSpatrick print(' Available Substitutions: %s' % substitutions) 16409467b48Spatrick 165097a140dSpatrick if show_tests: 16609467b48Spatrick print('-- Available Tests --') 167097a140dSpatrick for t in tests: 168097a140dSpatrick print(' %s' % t.getFullName()) 16909467b48Spatrick 17009467b48Spatrick 17109467b48Spatrickdef determine_order(tests, order): 17273471bf0Spatrick from lit.cl_arguments import TestOrder 173*d415bd75Srobert enum_order = TestOrder(order) 174*d415bd75Srobert if enum_order == TestOrder.RANDOM: 17509467b48Spatrick import random 17609467b48Spatrick random.shuffle(tests) 177*d415bd75Srobert elif enum_order == TestOrder.LEXICAL: 178*d415bd75Srobert tests.sort(key=lambda t: t.getFullName()) 17909467b48Spatrick else: 180*d415bd75Srobert assert enum_order == TestOrder.SMART, 'Unknown TestOrder value' 18173471bf0Spatrick tests.sort(key=lambda t: (not t.previous_failure, -t.previous_elapsed, t.getFullName())) 18209467b48Spatrick 18309467b48Spatrick 18409467b48Spatrickdef filter_by_shard(tests, run, shards, lit_config): 18509467b48Spatrick test_ixs = range(run - 1, len(tests), shards) 18609467b48Spatrick selected_tests = [tests[i] for i in test_ixs] 18709467b48Spatrick 18809467b48Spatrick # For clarity, generate a preview of the first few test indices in the shard 18909467b48Spatrick # to accompany the arithmetic expression. 19009467b48Spatrick preview_len = 3 19173471bf0Spatrick preview = ', '.join([str(i + 1) for i in test_ixs[:preview_len]]) 19209467b48Spatrick if len(test_ixs) > preview_len: 19373471bf0Spatrick preview += ', ...' 19473471bf0Spatrick msg = f'Selecting shard {run}/{shards} = ' \ 19573471bf0Spatrick f'size {len(selected_tests)}/{len(tests)} = ' \ 19673471bf0Spatrick f'tests #({shards}*k)+{run} = [{preview}]' 19709467b48Spatrick lit_config.note(msg) 19809467b48Spatrick return selected_tests 19909467b48Spatrick 20009467b48Spatrick 20173471bf0Spatrickdef mark_xfail(selected_tests, opts): 20273471bf0Spatrick for t in selected_tests: 20373471bf0Spatrick test_file = os.sep.join(t.path_in_suite) 20473471bf0Spatrick test_full_name = t.getFullName() 20573471bf0Spatrick if test_file in opts.xfail or test_full_name in opts.xfail: 20673471bf0Spatrick t.xfails += '*' 20773471bf0Spatrick if test_file in opts.xfail_not or test_full_name in opts.xfail_not: 20873471bf0Spatrick t.xfail_not = True 20973471bf0Spatrick 210097a140dSpatrickdef mark_excluded(discovered_tests, selected_tests): 211097a140dSpatrick excluded_tests = set(discovered_tests) - set(selected_tests) 212097a140dSpatrick result = lit.Test.Result(lit.Test.EXCLUDED) 213097a140dSpatrick for t in excluded_tests: 214097a140dSpatrick t.setResult(result) 215097a140dSpatrick 216097a140dSpatrick 217097a140dSpatrickdef run_tests(tests, lit_config, opts, discovered_tests): 218097a140dSpatrick workers = min(len(tests), opts.workers) 21973471bf0Spatrick display = lit.display.create_display(opts, tests, discovered_tests, workers) 220097a140dSpatrick 22173471bf0Spatrick run = lit.run.Run(tests, lit_config, workers, display.update, 22209467b48Spatrick opts.max_failures, opts.timeout) 22309467b48Spatrick 22409467b48Spatrick display.print_header() 225097a140dSpatrick 226097a140dSpatrick interrupted = False 227097a140dSpatrick error = None 22809467b48Spatrick try: 22909467b48Spatrick execute_in_tmp_dir(run, lit_config) 23009467b48Spatrick except KeyboardInterrupt: 231097a140dSpatrick interrupted = True 232097a140dSpatrick error = ' interrupted by user' 233097a140dSpatrick except lit.run.MaxFailuresError: 234097a140dSpatrick error = 'warning: reached maximum number of test failures' 235097a140dSpatrick except lit.run.TimeoutError: 236097a140dSpatrick error = 'warning: reached timeout' 237097a140dSpatrick 238097a140dSpatrick display.clear(interrupted) 239097a140dSpatrick if error: 240097a140dSpatrick sys.stderr.write('%s, skipping remaining tests\n' % error) 24109467b48Spatrick 24209467b48Spatrick 24309467b48Spatrickdef execute_in_tmp_dir(run, lit_config): 24409467b48Spatrick # Create a temp directory inside the normal temp directory so that we can 24509467b48Spatrick # try to avoid temporary test file leaks. The user can avoid this behavior 24609467b48Spatrick # by setting LIT_PRESERVES_TMP in the environment, so they can easily use 24709467b48Spatrick # their own temp directory to monitor temporary file leaks or handle them at 24809467b48Spatrick # the buildbot level. 24909467b48Spatrick tmp_dir = None 25009467b48Spatrick if 'LIT_PRESERVES_TMP' not in os.environ: 25109467b48Spatrick import tempfile 252*d415bd75Srobert # z/OS linker does not support '_' in paths, so use '-'. 253*d415bd75Srobert tmp_dir = tempfile.mkdtemp(prefix='lit-tmp-') 254*d415bd75Srobert tmp_dir_envs = {k: tmp_dir for k in ['TMP', 'TMPDIR', 'TEMP', 'TEMPDIR']} 255*d415bd75Srobert os.environ.update(tmp_dir_envs) 256*d415bd75Srobert for cfg in {t.config for t in run.tests}: 257*d415bd75Srobert cfg.environment.update(tmp_dir_envs) 25809467b48Spatrick try: 25909467b48Spatrick run.execute() 26009467b48Spatrick finally: 26109467b48Spatrick if tmp_dir: 26209467b48Spatrick try: 26309467b48Spatrick import shutil 26409467b48Spatrick shutil.rmtree(tmp_dir) 265097a140dSpatrick except Exception as e: 266097a140dSpatrick lit_config.warning("Failed to delete temp directory '%s', try upgrading your version of Python to fix this" % tmp_dir) 26709467b48Spatrick 26809467b48Spatrick 269097a140dSpatrickdef print_histogram(tests): 270097a140dSpatrick test_times = [(t.getFullName(), t.result.elapsed) 271097a140dSpatrick for t in tests if t.result.elapsed] 272097a140dSpatrick if test_times: 273097a140dSpatrick lit.util.printHistogram(test_times, title='Tests') 274097a140dSpatrick 275097a140dSpatrick 276097a140dSpatrickdef print_results(tests, elapsed, opts): 277097a140dSpatrick tests_by_code = {code: [] for code in lit.Test.ResultCode.all_codes()} 27809467b48Spatrick for test in tests: 279097a140dSpatrick tests_by_code[test.result.code].append(test) 28009467b48Spatrick 281097a140dSpatrick for code in lit.Test.ResultCode.all_codes(): 28273471bf0Spatrick print_group(sorted(tests_by_code[code], key=lambda t: t.getFullName()), code, opts.shown_codes) 283097a140dSpatrick 284097a140dSpatrick print_summary(tests_by_code, opts.quiet, elapsed) 285097a140dSpatrick 286097a140dSpatrick 287097a140dSpatrickdef print_group(tests, code, shown_codes): 288097a140dSpatrick if not tests: 289097a140dSpatrick return 290097a140dSpatrick if not code.isFailure and code not in shown_codes: 291097a140dSpatrick return 29209467b48Spatrick print('*' * 20) 293097a140dSpatrick print('{} Tests ({}):'.format(code.label, len(tests))) 294097a140dSpatrick for test in tests: 29509467b48Spatrick print(' %s' % test.getFullName()) 29609467b48Spatrick sys.stdout.write('\n') 29709467b48Spatrick 29809467b48Spatrick 299097a140dSpatrickdef print_summary(tests_by_code, quiet, elapsed): 300097a140dSpatrick if not quiet: 301097a140dSpatrick print('\nTesting Time: %.2fs' % elapsed) 30209467b48Spatrick 303097a140dSpatrick codes = [c for c in lit.Test.ResultCode.all_codes() 304097a140dSpatrick if not quiet or c.isFailure] 305097a140dSpatrick groups = [(c.label, len(tests_by_code[c])) for c in codes] 306097a140dSpatrick groups = [(label, count) for label, count in groups if count] 307097a140dSpatrick if not groups: 308097a140dSpatrick return 30909467b48Spatrick 310097a140dSpatrick max_label_len = max(len(label) for label, _ in groups) 311097a140dSpatrick max_count_len = max(len(str(count)) for _, count in groups) 31209467b48Spatrick 313097a140dSpatrick for (label, count) in groups: 314097a140dSpatrick label = label.ljust(max_label_len) 315097a140dSpatrick count = str(count).rjust(max_count_len) 316097a140dSpatrick print(' %s: %s' % (label, count)) 317