diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -421,6 +421,9 @@ def main(tests=None, testdir=None, verbo if use_mp and findleaks: usage("-l and -j don't go together!") + # set the global timeout of a single test + support.TIMEOUT = timeout + good = [] bad = [] skipped = [] @@ -647,7 +650,7 @@ def main(tests=None, testdir=None, verbo else: try: result = runtest(test, verbose, quiet, huntrleaks, debug, - rerun_failed=verbose3, timeout=timeout) + rerun_failed=verbose3) accumulate_result(test, result) except KeyboardInterrupt: interrupted = True @@ -794,7 +797,7 @@ def replace_stdout(): def runtest(test, verbose, quiet, huntrleaks=False, debug=False, use_resources=None, - rerun_failed=False, timeout=None): + rerun_failed=False): """Run a single test. test -- the name of the test @@ -804,8 +807,6 @@ def runtest(test, verbose, quiet, huntrleaks -- run multiple times to test for leaks; requires a debug build; a triple corresponding to -R's three arguments rerun_failed -- if true, re-run in verbose mode when failed - timeout -- dump the traceback and exit if a test takes more than - timeout seconds Returns one of the test result constants: INTERRUPTED KeyboardInterrupt when run under -j @@ -819,9 +820,6 @@ def runtest(test, verbose, quiet, support.verbose = verbose # Tell tests to be moderately quiet if use_resources is not None: support.use_resources = use_resources - use_timeout = (timeout is not None and timeout > 0) - if use_timeout: - faulthandler.dump_tracebacks_later(timeout, exit=True) try: result = runtest_inner(test, verbose, quiet, huntrleaks, debug) if result[0] == FAILED and rerun_failed: @@ -829,11 +827,9 @@ def runtest(test, verbose, quiet, sys.stdout.flush() sys.stderr.flush() print("Re-running test {} in verbose mode".format(test)) - runtest(test, True, quiet, huntrleaks, debug, timeout=timeout) + runtest(test, True, quiet, huntrleaks, debug) return result finally: - if use_timeout: - faulthandler.cancel_dump_tracebacks_later() cleanup_test_droppings(test, verbose) # Unit tests are supposed to leave the execution environment unchanged diff --git a/Lib/test/support.py b/Lib/test/support.py --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -5,6 +5,7 @@ if __name__ != 'test.support': import contextlib import errno +import faulthandler import functools import gc import socket @@ -45,6 +46,9 @@ __all__ = [ "swap_item", "swap_attr", "requires_IEEE_754", "TestHandler", "Matcher", "can_symlink", "skip_unless_symlink"] +# Timeout in seconds of a single test used by run_unittest() +TIMEOUT = None + class Error(Exception): """Base class for regression test exceptions.""" @@ -1073,8 +1077,14 @@ def bigaddrspacetest(f): # unittest integration. class BasicTestRunner: + resultclass = unittest.TestResult + + def __init__(self, resultclass=None): + if resultclass is not None: + self.resultclass = resultclass + def run(self, test): - result = unittest.TestResult() + result = self.resultclass() test(result) return result @@ -1159,9 +1169,31 @@ def refcount_test(test): def _run_suite(suite): """Run tests from a unittest.TestSuite-derived class.""" if verbose: - runner = unittest.TextTestRunner(sys.stdout, verbosity=2) + result_base_class = unittest.TextTestRunner.resultclass else: - runner = BasicTestRunner() + result_base_class = BasicTestRunner.resultclass + + class Result(result_base_class): + def __init__(self, *args, **kw): + result_base_class.__init__(self, *args, **kw) + self.timeout = TIMEOUT + self.use_timeout = (self.timeout is not None and self.timeout > 0) + + def startTest(self, test): + result_base_class.startTest(self, test) + if self.use_timeout: + faulthandler.dump_tracebacks_later(self.timeout, exit=True) + + def stopTest(self, test): + if self.use_timeout: + faulthandler.cancel_dump_tracebacks_later() + result_base_class.stopTest(self, test) + + if verbose: + runner = unittest.TextTestRunner(sys.stdout, verbosity=2, + resultclass=Result) + else: + runner = BasicTestRunner(resultclass=Result) result = runner.run(suite) if not result.wasSuccessful():