diff options
author | Ramon Fernandez <ramon@mongodb.com> | 2016-08-25 16:34:34 -0400 |
---|---|---|
committer | Ramon Fernandez <ramon@mongodb.com> | 2016-08-25 16:54:18 -0400 |
commit | c330c9991ab45e7d0685d53e699ef26dba065660 (patch) | |
tree | 3dc5cd06b5f6c7eaaa4cb20cbe763504c14a772b /src/mongo/gotools/test/qa-tests/buildscripts/resmoke.py | |
parent | eb62b862d5ebf179a1bcd9f394070e69c30188ab (diff) | |
download | mongo-c330c9991ab45e7d0685d53e699ef26dba065660.tar.gz |
Import tools: 5b883d86fdb4df55036d5dba2ca6f9dfa0750b44 from branch v3.3
ref: 1ac1389bda..5b883d86fd
for: 3.3.12
SERVER-25814 Initial vendor import: tools
Diffstat (limited to 'src/mongo/gotools/test/qa-tests/buildscripts/resmoke.py')
-rwxr-xr-x | src/mongo/gotools/test/qa-tests/buildscripts/resmoke.py | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/src/mongo/gotools/test/qa-tests/buildscripts/resmoke.py b/src/mongo/gotools/test/qa-tests/buildscripts/resmoke.py new file mode 100755 index 00000000000..a6cb03cb620 --- /dev/null +++ b/src/mongo/gotools/test/qa-tests/buildscripts/resmoke.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python + +""" +Command line utility for executing MongoDB tests of all kinds. +""" + +from __future__ import absolute_import + +import json +import os.path +import random +import signal +import sys +import time +import traceback + +# Get relative imports to work when the package is not installed on the PYTHONPATH. +if __name__ == "__main__" and __package__ is None: + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + from buildscripts import resmokelib + + +def _execute_suite(suite, logging_config): + """ + Executes each test group of 'suite', failing fast if requested. + + Returns true if the execution of the suite was interrupted by the + user, and false otherwise. + """ + + logger = resmokelib.logging.loggers.EXECUTOR + + for group in suite.test_groups: + if resmokelib.config.SHUFFLE: + logger.info("Shuffling order of tests for %ss in suite %s. The seed is %d.", + group.test_kind, suite.get_name(), resmokelib.config.RANDOM_SEED) + random.seed(resmokelib.config.RANDOM_SEED) + random.shuffle(group.tests) + + if resmokelib.config.DRY_RUN == "tests": + sb = [] + sb.append("Tests that would be run for %ss in suite %s:" + % (group.test_kind, suite.get_name())) + if len(group.tests) > 0: + for test in group.tests: + sb.append(test) + else: + sb.append("(no tests)") + logger.info("\n".join(sb)) + + # Set a successful return code on the test group because we want to output the tests + # that would get run by any other suites the user specified. + group.return_code = 0 + continue + + if len(group.tests) == 0: + logger.info("Skipping %ss, no tests to run", group.test_kind) + continue + + group_config = suite.get_executor_config().get(group.test_kind, {}) + executor = resmokelib.testing.executor.TestGroupExecutor(logger, + group, + logging_config, + **group_config) + + try: + executor.run() + if resmokelib.config.FAIL_FAST and group.return_code != 0: + suite.return_code = group.return_code + return False + except resmokelib.errors.UserInterrupt: + suite.return_code = 130 # Simulate SIGINT as exit code. + return True + except: + logger.exception("Encountered an error when running %ss of suite %s.", + group.test_kind, suite.get_name()) + suite.return_code = 2 + return False + + +def _log_summary(logger, suites, time_taken): + if len(suites) > 1: + sb = [] + sb.append("Summary of all suites: %d suites ran in %0.2f seconds" + % (len(suites), time_taken)) + for suite in suites: + suite_sb = [] + suite.summarize(suite_sb) + sb.append(" %s: %s" % (suite.get_name(), "\n ".join(suite_sb))) + + logger.info("=" * 80) + logger.info("\n".join(sb)) + + +def _summarize_suite(suite): + sb = [] + suite.summarize(sb) + return "\n".join(sb) + + +def _dump_suite_config(suite, logging_config): + """ + Returns a string that represents the YAML configuration of a suite. + + TODO: include the "options" key in the result + """ + + sb = [] + sb.append("YAML configuration of suite %s" % (suite.get_name())) + sb.append(resmokelib.utils.dump_yaml({"selector": suite.get_selector_config()})) + sb.append("") + sb.append(resmokelib.utils.dump_yaml({"executor": suite.get_executor_config()})) + sb.append("") + sb.append(resmokelib.utils.dump_yaml({"logging": logging_config})) + return "\n".join(sb) + + +def _write_report_file(suites, pathname): + """ + Writes the report.json file if requested. + """ + + reports = [] + for suite in suites: + for group in suite.test_groups: + reports.extend(group.get_reports()) + + combined_report_dict = resmokelib.testing.report.TestReport.combine(*reports).as_dict() + with open(pathname, "w") as fp: + json.dump(combined_report_dict, fp) + + +def main(): + start_time = time.time() + + values, args = resmokelib.parser.parse_command_line() + + logging_config = resmokelib.parser.get_logging_config(values) + resmokelib.logging.config.apply_config(logging_config) + resmokelib.logging.flush.start_thread() + + resmokelib.parser.update_config_vars(values) + + exec_logger = resmokelib.logging.loggers.EXECUTOR + resmoke_logger = resmokelib.logging.loggers.new_logger("resmoke", parent=exec_logger) + + if values.list_suites: + suite_names = resmokelib.parser.get_named_suites() + resmoke_logger.info("Suites available to execute:\n%s", "\n".join(suite_names)) + sys.exit(0) + + interrupted = False + suites = resmokelib.parser.get_suites(values, args) + try: + for suite in suites: + resmoke_logger.info(_dump_suite_config(suite, logging_config)) + + suite.record_start() + interrupted = _execute_suite(suite, logging_config) + suite.record_end() + + resmoke_logger.info("=" * 80) + resmoke_logger.info("Summary of %s suite: %s", + suite.get_name(), _summarize_suite(suite)) + + if interrupted or (resmokelib.config.FAIL_FAST and suite.return_code != 0): + time_taken = time.time() - start_time + _log_summary(resmoke_logger, suites, time_taken) + sys.exit(suite.return_code) + + time_taken = time.time() - start_time + _log_summary(resmoke_logger, suites, time_taken) + + # Exit with a nonzero code if any of the suites failed. + exit_code = max(suite.return_code for suite in suites) + sys.exit(exit_code) + finally: + if not interrupted: + resmokelib.logging.flush.stop_thread() + + if resmokelib.config.REPORT_FILE is not None: + _write_report_file(suites, resmokelib.config.REPORT_FILE) + + +if __name__ == "__main__": + + def _dump_stacks(signum, frame): + """ + Signal handler that will dump the stacks of all threads. + """ + + header_msg = "Dumping stacks due to SIGUSR1 signal" + + sb = [] + sb.append("=" * len(header_msg)) + sb.append(header_msg) + sb.append("=" * len(header_msg)) + + frames = sys._current_frames() + sb.append("Total threads: %d" % (len(frames))) + sb.append("") + + for thread_id in frames: + stack = frames[thread_id] + sb.append("Thread %d:" % (thread_id)) + sb.append("".join(traceback.format_stack(stack))) + + sb.append("=" * len(header_msg)) + print "\n".join(sb) + + try: + signal.signal(signal.SIGUSR1, _dump_stacks) + except AttributeError: + print "Cannot catch signals on Windows" + + main() |