xref: /openbsd/gnu/llvm/llvm/utils/lit/lit/main.py (revision d415bd75)
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