diff --git a/llvm/utils/lit/lit/main.py b/llvm/utils/lit/lit/main.py --- a/llvm/utils/lit/lit/main.py +++ b/llvm/utils/lit/lit/main.py @@ -25,77 +25,6 @@ import lit.Test import lit.util -def write_test_results(run, lit_config, testing_time, output_path): - try: - import json - except ImportError: - lit_config.fatal('test output unsupported with Python 2.5') - - # Construct the data we will write. - data = {} - # Encode the current lit version as a schema version. - data['__version__'] = lit.__versioninfo__ - data['elapsed'] = testing_time - # FIXME: Record some information on the lit configuration used? - # FIXME: Record information from the individual test suites? - - # Encode the tests. - data['tests'] = tests_data = [] - for test in run.tests: - test_data = { - 'name' : test.getFullName(), - 'code' : test.result.code.name, - 'output' : test.result.output, - 'elapsed' : test.result.elapsed } - - # Add test metrics, if present. - if test.result.metrics: - test_data['metrics'] = metrics_data = {} - for key, value in test.result.metrics.items(): - metrics_data[key] = value.todata() - - # Report micro-tests separately, if present - if test.result.microResults: - for key, micro_test in test.result.microResults.items(): - # Expand parent test name with micro test name - parent_name = test.getFullName() - micro_full_name = parent_name + ':' + key - - micro_test_data = { - 'name' : micro_full_name, - 'code' : micro_test.code.name, - 'output' : micro_test.output, - 'elapsed' : micro_test.elapsed } - if micro_test.metrics: - micro_test_data['metrics'] = micro_metrics_data = {} - for key, value in micro_test.metrics.items(): - micro_metrics_data[key] = value.todata() - - tests_data.append(micro_test_data) - - tests_data.append(test_data) - - # Write the output. - f = open(output_path, 'w') - try: - json.dump(data, f, indent=2, sort_keys=True) - f.write('\n') - finally: - f.close() - -def update_incremental_cache(test): - if not test.result.code.isFailure: - return - fname = test.getFilePath() - os.utime(fname, None) - -def by_mtime(test): - fname = test.getFilePath() - try: - return os.path.getmtime(fname) - except: - return 0 - def main(builtinParameters = {}): # Create a temp directory inside the normal temp directory so that we can # try to avoid temporary test file leaks. The user can avoid this behavior @@ -132,14 +61,7 @@ print("lit %s" % (lit.__version__,)) return - # Create the user defined parameters. - userParams = dict(builtinParameters) - for entry in opts.userParameters: - if '=' not in entry: - name,val = entry,'' - else: - name,val = entry.split('=', 1) - userParams[name] = val + userParams = create_user_parameters(builtinParameters, opts) # Decide what the requested maximum indvidual test time should be if opts.maxIndividualTestTime is not None: @@ -186,57 +108,16 @@ litConfig.maxIndividualTestTime = opts.maxIndividualTestTime if opts.showSuites or opts.showTests: - # Aggregate the tests by suite. - suitesAndTests = {} - for result_test in run.tests: - if result_test.suite not in suitesAndTests: - suitesAndTests[result_test.suite] = [] - suitesAndTests[result_test.suite].append(result_test) - suitesAndTests = list(suitesAndTests.items()) - suitesAndTests.sort(key = lambda item: item[0].name) - - # Show the suites, if requested. - if opts.showSuites: - print('-- Test Suites --') - for ts,ts_tests in suitesAndTests: - print(' %s - %d tests' %(ts.name, len(ts_tests))) - print(' Source Root: %s' % ts.source_root) - print(' Exec Root : %s' % ts.exec_root) - if ts.config.available_features: - print(' Available Features : %s' % ' '.join( - sorted(ts.config.available_features))) - - # Show the tests, if requested. - if opts.showTests: - print('-- Available Tests --') - for ts,ts_tests in suitesAndTests: - ts_tests.sort(key = lambda test: test.path_in_suite) - for test in ts_tests: - print(' %s' % (test.getFullName(),)) - - # Exit. - sys.exit(0) + print_suites_or_tests(run, opts) + return # Select and order the tests. numTotalTests = len(run.tests) - # First, select based on the filter expression if given. if opts.filter: - try: - rex = re.compile(opts.filter) - except: - parser.error("invalid regular expression for --filter: %r" % ( - opts.filter)) - run.tests = [result_test for result_test in run.tests - if rex.search(result_test.getFullName())] - - # Then select the order. - if opts.shuffle: - random.shuffle(run.tests) - elif opts.incremental: - run.tests.sort(key=by_mtime, reverse=True) - else: - run.tests.sort(key = lambda t: (not t.isEarlyTest(), t.getFullName())) + filter_tests(run, opts) + + order_tests(run, opts) # Then optionally restrict our attention to a shard of the tests. if (opts.numShards is not None) or (opts.runShard is not None): @@ -262,27 +143,7 @@ # Don't create more workers than tests. opts.numWorkers = min(len(run.tests), opts.numWorkers) - # Because some tests use threads internally, and at least on Linux each - # of these threads counts toward the current process limit, try to - # raise the (soft) process limit so that tests don't fail due to - # resource exhaustion. - try: - cpus = lit.util.detectCPUs() - desired_limit = opts.numWorkers * cpus * 2 # the 2 is a safety factor - - # Import the resource module here inside this try block because it - # will likely fail on Windows. - import resource - - max_procs_soft, max_procs_hard = resource.getrlimit(resource.RLIMIT_NPROC) - desired_limit = min(desired_limit, max_procs_hard) - - if max_procs_soft < desired_limit: - resource.setrlimit(resource.RLIMIT_NPROC, (desired_limit, max_procs_hard)) - litConfig.note('raised the process limit from %d to %d' % \ - (max_procs_soft, desired_limit)) - except: - pass + increase_process_limit(litConfig, opts) display = lit.display.create_display(opts, len(run.tests), numTotalTests, opts.numWorkers) @@ -358,41 +219,7 @@ print(' %s: %d' % (name,N)) if opts.xunit_output_file: - # Collect the tests, indexed by test suite - by_suite = {} - for result_test in run.tests: - suite = result_test.suite.config.name - if suite not in by_suite: - by_suite[suite] = { - 'passes' : 0, - 'failures' : 0, - 'skipped': 0, - 'tests' : [] } - by_suite[suite]['tests'].append(result_test) - if result_test.result.code.isFailure: - by_suite[suite]['failures'] += 1 - elif result_test.result.code == lit.Test.UNSUPPORTED: - by_suite[suite]['skipped'] += 1 - else: - by_suite[suite]['passes'] += 1 - xunit_output_file = open(opts.xunit_output_file, "w") - xunit_output_file.write("\n") - xunit_output_file.write("\n") - for suite_name, suite in by_suite.items(): - safe_suite_name = quoteattr(suite_name.replace(".", "-")) - xunit_output_file.write("\n") - - for result_test in suite['tests']: - result_test.writeJUnitXML(xunit_output_file) - xunit_output_file.write("\n") - xunit_output_file.write("\n") - xunit_output_file.write("") - xunit_output_file.close() + write_test_results_xunit(run, opts) # If we encountered any additional errors, exit abnormally. if litConfig.numErrors: @@ -407,5 +234,196 @@ sys.exit(1) sys.exit(0) + +def create_user_parameters(builtinParameters, opts): + userParams = dict(builtinParameters) + for entry in opts.userParameters: + if '=' not in entry: + name,val = entry,'' + else: + name,val = entry.split('=', 1) + userParams[name] = val + return userParams + +def print_suites_or_tests(run, opts): + # Aggregate the tests by suite. + suitesAndTests = {} + for result_test in run.tests: + if result_test.suite not in suitesAndTests: + suitesAndTests[result_test.suite] = [] + suitesAndTests[result_test.suite].append(result_test) + suitesAndTests = list(suitesAndTests.items()) + suitesAndTests.sort(key = lambda item: item[0].name) + + # Show the suites, if requested. + if opts.showSuites: + print('-- Test Suites --') + for ts,ts_tests in suitesAndTests: + print(' %s - %d tests' %(ts.name, len(ts_tests))) + print(' Source Root: %s' % ts.source_root) + print(' Exec Root : %s' % ts.exec_root) + if ts.config.available_features: + print(' Available Features : %s' % ' '.join( + sorted(ts.config.available_features))) + + # Show the tests, if requested. + if opts.showTests: + print('-- Available Tests --') + for ts,ts_tests in suitesAndTests: + ts_tests.sort(key = lambda test: test.path_in_suite) + for test in ts_tests: + print(' %s' % (test.getFullName(),)) + + # Exit. + sys.exit(0) + +def filter_tests(run, opts): + try: + rex = re.compile(opts.filter) + except: + parser.error("invalid regular expression for --filter: %r" % ( + opts.filter)) + run.tests = [result_test for result_test in run.tests + if rex.search(result_test.getFullName())] + +def order_tests(run, opts): + if opts.shuffle: + random.shuffle(run.tests) + elif opts.incremental: + run.tests.sort(key = by_mtime, reverse = True) + else: + run.tests.sort(key = lambda t: (not t.isEarlyTest(), t.getFullName())) + +def by_mtime(test): + fname = test.getFilePath() + try: + return os.path.getmtime(fname) + except: + return 0 + +def update_incremental_cache(test): + if not test.result.code.isFailure: + return + fname = test.getFilePath() + os.utime(fname, None) + +def increase_process_limit(litConfig, opts): + # Because some tests use threads internally, and at least on Linux each + # of these threads counts toward the current process limit, try to + # raise the (soft) process limit so that tests don't fail due to + # resource exhaustion. + try: + cpus = lit.util.detectCPUs() + desired_limit = opts.numWorkers * cpus * 2 # the 2 is a safety factor + + # Import the resource module here inside this try block because it + # will likely fail on Windows. + import resource + + max_procs_soft, max_procs_hard = resource.getrlimit(resource.RLIMIT_NPROC) + desired_limit = min(desired_limit, max_procs_hard) + + if max_procs_soft < desired_limit: + resource.setrlimit(resource.RLIMIT_NPROC, (desired_limit, max_procs_hard)) + litConfig.note('raised the process limit from %d to %d' % \ + (max_procs_soft, desired_limit)) + except: + pass + +def write_test_results(run, lit_config, testing_time, output_path): + try: + import json + except ImportError: + lit_config.fatal('test output unsupported with Python 2.5') + + # Construct the data we will write. + data = {} + # Encode the current lit version as a schema version. + data['__version__'] = lit.__versioninfo__ + data['elapsed'] = testing_time + # FIXME: Record some information on the lit configuration used? + # FIXME: Record information from the individual test suites? + + # Encode the tests. + data['tests'] = tests_data = [] + for test in run.tests: + test_data = { + 'name' : test.getFullName(), + 'code' : test.result.code.name, + 'output' : test.result.output, + 'elapsed' : test.result.elapsed } + + # Add test metrics, if present. + if test.result.metrics: + test_data['metrics'] = metrics_data = {} + for key, value in test.result.metrics.items(): + metrics_data[key] = value.todata() + + # Report micro-tests separately, if present + if test.result.microResults: + for key, micro_test in test.result.microResults.items(): + # Expand parent test name with micro test name + parent_name = test.getFullName() + micro_full_name = parent_name + ':' + key + + micro_test_data = { + 'name' : micro_full_name, + 'code' : micro_test.code.name, + 'output' : micro_test.output, + 'elapsed' : micro_test.elapsed } + if micro_test.metrics: + micro_test_data['metrics'] = micro_metrics_data = {} + for key, value in micro_test.metrics.items(): + micro_metrics_data[key] = value.todata() + + tests_data.append(micro_test_data) + + tests_data.append(test_data) + + # Write the output. + f = open(output_path, 'w') + try: + json.dump(data, f, indent=2, sort_keys=True) + f.write('\n') + finally: + f.close() + +def write_test_results_xunit(run, opts): + # Collect the tests, indexed by test suite + by_suite = {} + for result_test in run.tests: + suite = result_test.suite.config.name + if suite not in by_suite: + by_suite[suite] = { + 'passes' : 0, + 'failures' : 0, + 'skipped': 0, + 'tests' : [] } + by_suite[suite]['tests'].append(result_test) + if result_test.result.code.isFailure: + by_suite[suite]['failures'] += 1 + elif result_test.result.code == lit.Test.UNSUPPORTED: + by_suite[suite]['skipped'] += 1 + else: + by_suite[suite]['passes'] += 1 + xunit_output_file = open(opts.xunit_output_file, "w") + xunit_output_file.write("\n") + xunit_output_file.write("\n") + for suite_name, suite in by_suite.items(): + safe_suite_name = quoteattr(suite_name.replace(".", "-")) + xunit_output_file.write("\n") + + for result_test in suite['tests']: + result_test.writeJUnitXML(xunit_output_file) + xunit_output_file.write("\n") + xunit_output_file.write("\n") + xunit_output_file.write("") + xunit_output_file.close() + if __name__=='__main__': main()