def rerun_failed_tests(self): self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.first_result = self.get_tests_result() print() print("Re-running failed tests in verbose mode") self.rerun = self.bad[:] for test in self.rerun: print("Re-running test %r in verbose mode" % test, flush=True) try: self.ns.verbose = True ok = runtest(self.ns, test) except KeyboardInterrupt: self.interrupted = True # print a newline separate from the ^C print() break else: if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: self.bad.remove(test) else: if self.bad: print(count(len(self.bad), 'test'), "failed again:") printlist(self.bad) self.display_result()
def run_tests_sequential(self): if self.ns.trace: import trace self.tracer = trace.Trace(trace=False, count=True) save_modules = sys.modules.keys() print("Run tests sequentially") previous_test = None for test_index, test_name in enumerate(self.tests, 1): start_time = time.monotonic() text = test_name if previous_test: text = '%s -- %s' % (text, previous_test) self.display_progress(test_index, text) if self.tracer: # If we're tracing code coverage, then we don't exit with status # if on a false return value from main. cmd = ('result = runtest(self.ns, test_name); ' 'self.accumulate_result(result)') ns = dict(locals()) self.tracer.runctx(cmd, globals=globals(), locals=ns) result = ns['result'] else: result = runtest(self.ns, test_name) self.accumulate_result(result) if result.result == INTERRUPTED: self.interrupted = True break previous_test = format_test_result(result) test_time = time.monotonic() - start_time if test_time >= PROGRESS_MIN_TIME: previous_test = "%s in %s" % (previous_test, format_duration(test_time)) elif result[0] == PASSED: # be quiet: say nothing if the test passed shortly previous_test = None if self.ns.findleaks: gc.collect() if gc.garbage: print("Warning: test created", len(gc.garbage), end=' ') print("uncollectable object(s).") # move the uncollectable objects somewhere so we don't see # them again self.found_garbage.extend(gc.garbage) del gc.garbage[:] # Unload the newly imported modules (best effort finalization) for module in sys.modules.keys(): if module not in save_modules and module.startswith("test."): support.unload(module) if previous_test: print(previous_test)
def run_tests_sequential(self): if self.ns.trace: import trace self.tracer = trace.Trace(trace=False, count=True) save_modules = sys.modules.keys() self.log("Run tests sequentially") #show used memory if sys.platform == 'OpenVMS': print('Used memory: %s' % format_mem(get_mem())) previous_test = None for test_index, test_name in enumerate(self.tests, 1): start_time = time.monotonic() text = test_name if previous_test: text = '%s -- %s' % (text, previous_test) self.display_progress(test_index, text) if self.tracer: # If we're tracing code coverage, then we don't exit with status # if on a false return value from main. cmd = ('result = runtest(self.ns, test_name); ' 'self.accumulate_result(result)') ns = dict(locals()) self.tracer.runctx(cmd, globals=globals(), locals=ns) result = ns['result'] else: result = runtest(self.ns, test_name) self.accumulate_result(result) if result.result == INTERRUPTED: break previous_test = format_test_result(result) test_time = time.monotonic() - start_time if test_time >= PROGRESS_MIN_TIME: previous_test = "%s in %s" % (previous_test, format_duration(test_time)) elif result.result == PASSED: # be quiet: say nothing if the test passed shortly previous_test = None # Unload the newly imported modules (best effort finalization) for module in sys.modules.keys(): if module not in save_modules and module.startswith("test."): support.unload(module) #show used memory if sys.platform == 'OpenVMS': print('Used memory: %s' % format_mem(get_mem())) if self.ns.failfast and is_failed(result, self.ns): break if previous_test: print(previous_test)
def rerun_failed_tests(self): self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.first_result = self.get_tests_result() print() print("Re-running failed tests in verbose mode") self.rerun = self.bad[:] for test_name in self.rerun: print("Re-running test %r in verbose mode" % test_name, flush=True) self.ns.verbose = True ok = runtest(self.ns, test_name) if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: self.bad.remove(test_name) if ok.result == INTERRUPTED: self.interrupted = True break else: if self.bad: print(count(len(self.bad), 'test'), "failed again:") printlist(self.bad) self.display_result()
def rerun_failed_tests(self): self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.first_result = self.get_tests_result() print() print("Re-running failed tests in verbose mode") self.rerun = self.bad[:] for test_name in self.rerun: print(f"Re-running {test_name} in verbose mode", flush=True) self.ns.verbose = True result = runtest(self.ns, test_name) self.accumulate_result(result, rerun=True) if result.result == INTERRUPTED: break if self.bad: print(count(len(self.bad), 'test'), "failed again:") printlist(self.bad) self.display_result()
def run_tests_sequential(self): if self.ns.trace: import trace self.tracer = trace.Trace(trace=False, count=True) save_modules = sys.modules.keys() print("Run tests sequentially") previous_test = None for test_index, test in enumerate(self.tests, 1): start_time = time.monotonic() text = test if previous_test: text = '%s -- %s' % (text, previous_test) self.display_progress(test_index, text) if self.tracer: # If we're tracing code coverage, then we don't exit with status # if on a false return value from main. cmd = ('result = runtest(self.ns, test); ' 'self.accumulate_result(test, result)') self.tracer.runctx(cmd, globals=globals(), locals=vars()) else: try: result = runtest(self.ns, test) except KeyboardInterrupt: self.accumulate_result(test, (INTERRUPTED, None)) self.interrupted = True break else: self.accumulate_result(test, result) test_time = time.monotonic() - start_time if test_time >= PROGRESS_MIN_TIME: previous_test = '%s took %.0f sec' % (test, test_time) else: previous_test = None if self.ns.findleaks: gc.collect() if gc.garbage: print("Warning: test created", len(gc.garbage), end=' ') print("uncollectable object(s).") # move the uncollectable objects somewhere so we don't see # them again self.found_garbage.extend(gc.garbage) del gc.garbage[:] # Unload the newly imported modules (best effort finalization) for module in sys.modules.keys(): if module not in save_modules and module.startswith("test."): support.unload(module) if previous_test: print(previous_test)
def rerun_failed_tests(self): self.log() if self.ns.python: # Temp patch for https://github.com/python/cpython/issues/94052 self.log("Re-running failed tests is not supported with --python " "host runner option.") return self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.first_result = self.get_tests_result() self.log("Re-running failed tests in verbose mode") rerun_list = list(self.need_rerun) self.need_rerun.clear() for result in rerun_list: test_name = result.name self.rerun.append(test_name) errors = result.errors or [] failures = result.failures or [] error_names = [ test_full_name.split(" ")[0] for (test_full_name, *_) in errors ] failure_names = [ test_full_name.split(" ")[0] for (test_full_name, *_) in failures ] self.ns.verbose = True orig_match_tests = self.ns.match_tests if errors or failures: if self.ns.match_tests is None: self.ns.match_tests = [] self.ns.match_tests.extend(error_names) self.ns.match_tests.extend(failure_names) matching = "matching: " + ", ".join(self.ns.match_tests) self.log( f"Re-running {test_name} in verbose mode ({matching})") else: self.log(f"Re-running {test_name} in verbose mode") result = runtest(self.ns, test_name) self.ns.match_tests = orig_match_tests self.accumulate_result(result, rerun=True) if isinstance(result, Interrupted): break if self.bad: print(count(len(self.bad), 'test'), "failed again:") printlist(self.bad) self.display_result()
def run_tests_worker(ns: Namespace, test_name: str) -> NoReturn: setup_tests(ns) result = runtest(ns, test_name) print() # Force a newline (just in case) # Serialize TestResult as dict in JSON print(json.dumps(result, cls=EncodeTestResult), flush=True) sys.exit(0)
def run_tests_worker(worker_args): ns_dict, testname = json.loads(worker_args) ns = types.SimpleNamespace(**ns_dict) setup_tests(ns) result = runtest(ns, testname) print() # Force a newline (just in case) print(json.dumps(result), flush=True) sys.exit(0)
def run_tests_worker(ns, test_name): setup_tests(ns) result = runtest(ns, test_name) print() # Force a newline (just in case) # Serialize TestResult as list in JSON print(json.dumps(list(result)), flush=True) sys.exit(0)
def run_tests_slave(slaveargs): ns_dict, testname = json.loads(slaveargs) ns = types.SimpleNamespace(**ns_dict) setup_tests(ns) try: result = runtest(ns, testname) except KeyboardInterrupt: result = INTERRUPTED, '' except BaseException as e: traceback.print_exc() result = CHILD_ERROR, str(e) print() print(json.dumps(result), flush=True) sys.exit(0)
def run_tests_sequential(self): if self.ns.trace: import trace self.tracer = trace.Trace(trace=False, count=True) save_modules = sys.modules.keys() print('Run tests sequentially') previous_test = None for test_index, test in enumerate(self.tests, 1): start_time = time.monotonic() text = test if previous_test: text = '%s -- %s' % (text, previous_test) self.display_progress(test_index, text) if self.tracer: cmd = ( 'result = runtest(self.ns, test); self.accumulate_result(test, result)' ) ns = dict(locals()) self.tracer.runctx(cmd, globals=globals(), locals=ns) result = ns['result'] else: try: result = runtest(self.ns, test) except KeyboardInterrupt: self.interrupted = True self.accumulate_result(test, (INTERRUPTED, None)) break else: self.accumulate_result(test, result) previous_test = format_test_result(test, result[0]) test_time = time.monotonic() - start_time if test_time >= PROGRESS_MIN_TIME: previous_test = '%s in %s' % (previous_test, format_duration(test_time)) elif result[0] == PASSED: previous_test = None if self.ns.findleaks: gc.collect() if gc.garbage: print('Warning: test created', len(gc.garbage), end=' ') print('uncollectable object(s).') self.found_garbage.extend(gc.garbage) del gc.garbage[:] for module in sys.modules.keys(): if module not in save_modules and module.startswith('test.'): support.unload(module) if previous_test: print(previous_test)
def rerun_failed_tests(self): self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.first_result = self.get_tests_result() self.log() self.log("Re-running failed tests in verbose mode") rerun_list = self.rerun[:] self.rerun = [] for result in rerun_list: test_name = result.name errors = result.errors or [] failures = result.failures or [] error_names = [ test_full_name.split(" ")[0] for (test_full_name, *_) in errors ] failure_names = [ test_full_name.split(" ")[0] for (test_full_name, *_) in failures ] self.ns.verbose = True orig_match_tests = self.ns.match_tests if errors or failures: if self.ns.match_tests is None: self.ns.match_tests = [] self.ns.match_tests.extend(error_names) self.ns.match_tests.extend(failure_names) matching = "matching: " + ", ".join(self.ns.match_tests) self.log( f"Re-running {test_name} in verbose mode ({matching})") else: self.log(f"Re-running {test_name} in verbose mode") result = runtest(self.ns, test_name) self.ns.match_tests = orig_match_tests self.accumulate_result(result, rerun=True) if isinstance(result, Interrupted): break if self.bad: print(count(len(self.bad), 'test'), "failed again:") printlist(self.bad) self.display_result()
def run_tests_slave(slaveargs): ns_dict, testname = json.loads(slaveargs) ns = types.SimpleNamespace(**ns_dict) setup_tests(ns) try: result = runtest(ns, testname) except KeyboardInterrupt: result = INTERRUPTED, '' except BaseException as e: traceback.print_exc() result = CHILD_ERROR, str(e) print() # Force a newline (just in case) print(json.dumps(result), flush=True) sys.exit(0)
def run_tests_worker(worker_args): ns_dict, testname = json.loads(worker_args) ns = types.SimpleNamespace(**ns_dict) setup_tests(ns) try: result = runtest(ns, testname) except KeyboardInterrupt: result = INTERRUPTED, '', None except BaseException as e: traceback.print_exc() result = CHILD_ERROR, str(e) print() # Force a newline (just in case) print(json.dumps(result), flush=True) sys.exit(0)
def run(self, ns: types.SimpleNamespace) -> None: """Read commands from pipe and execute them""" # Create a temporary directory for test runs t = Regrtest() t.ns = ns t.set_temp_dir() test_cwd = t.create_temp_dir() setup_tests(t.ns) # Run the tests in a context manager that temporarily changes the CWD to a # temporary and writable directory. If it's not possible to create or # change the CWD, the original CWD will be used. The original CWD is # available from support.SAVEDCWD. with support.temp_cwd(test_cwd, quiet=True): msg = self.pipe.recv() while not isinstance(msg, ShutdownWorker): if isinstance(msg, RunTest): result = runtest(ns, msg.test_name) self.pipe.send(TestComplete(msg.test_name, result)) msg = self.pipe.recv() self.pipe.send(WorkerDone())
def rerun_failed_tests(self): self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.ns.match_tests = None print('Re-running failed tests in verbose mode') for test in self.bad[:]: print('Re-running test %r in verbose mode' % test, flush=True) try: self.ns.verbose = True ok = runtest(self.ns, test) except KeyboardInterrupt: self.interrupted = True print() break else: if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: self.bad.remove(test) else: if self.bad: print(count(len(self.bad), 'test'), 'failed again:') printlist(self.bad)
def rerun_failed_tests(self): self.ns.verbose = True self.ns.failfast = False self.ns.verbose3 = False self.ns.match_tests = None print("Re-running failed tests in verbose mode") for test in self.bad[:]: print("Re-running test %r in verbose mode" % test, flush=True) try: self.ns.verbose = True ok = runtest(self.ns, test) except KeyboardInterrupt: # print a newline separate from the ^C print() break else: if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: self.bad.remove(test) else: if self.bad: print(count(len(self.bad), "test"), "failed again:") printlist(self.bad)
def run_tests_sequential(self): if self.ns.trace: import trace self.tracer = trace.Trace(trace=False, count=True) save_modules = sys.modules.keys() for test_index, test in enumerate(self.tests, 1): self.display_progress(test_index, test) if self.tracer: # If we're tracing code coverage, then we don't exit with status # if on a false return value from main. cmd = "result = runtest(self.ns, test); " "self.accumulate_result(test, result)" self.tracer.runctx(cmd, globals=globals(), locals=vars()) else: try: result = runtest(self.ns, test) self.accumulate_result(test, result) except KeyboardInterrupt: self.interrupted = True break if self.ns.findleaks: gc.collect() if gc.garbage: print("Warning: test created", len(gc.garbage), end=" ") print("uncollectable object(s).") # move the uncollectable objects somewhere so we don't see # them again self.found_garbage.extend(gc.garbage) del gc.garbage[:] # Unload the newly imported modules (best effort finalization) for module in sys.modules.keys(): if module not in save_modules and module.startswith("test."): support.unload(module)
def main(tests=None, **kwargs): """Execute a test suite. This also parses command-line options and modifies its behavior accordingly. tests -- a list of strings containing test names (optional) testdir -- the directory in which to look for tests (optional) Users other than the Python test suite will certainly want to specify testdir; if it's omitted, the directory containing the Python test suite is searched for. If the tests argument is omitted, the tests listed on the command-line will be used. If that's empty, too, then all *.py files beginning with test_ will be used. The other default arguments (verbose, quiet, exclude, single, randomize, findleaks, use_resources, trace, coverdir, print_slow, and random_seed) allow programmers calling main() directly to set the values that would normally be set by flags on the command line. """ # Display the Python traceback on fatal errors (e.g. segfault) faulthandler.enable(all_threads=True) # Display the Python traceback on SIGALRM or SIGUSR1 signal signals = [] if hasattr(signal, 'SIGALRM'): signals.append(signal.SIGALRM) if hasattr(signal, 'SIGUSR1'): signals.append(signal.SIGUSR1) for signum in signals: faulthandler.register(signum, chain=True) replace_stdout() support.record_original_stdout(sys.stdout) ns = _parse_args(sys.argv[1:], **kwargs) if ns.huntrleaks: # Avoid false positives due to various caches # filling slowly with random data: warm_caches() if ns.memlimit is not None: support.set_memlimit(ns.memlimit) if ns.threshold is not None: import gc gc.set_threshold(ns.threshold) if ns.nowindows: import msvcrt msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS| msvcrt.SEM_NOALIGNMENTFAULTEXCEPT| msvcrt.SEM_NOGPFAULTERRORBOX| msvcrt.SEM_NOOPENFILEERRORBOX) try: msvcrt.CrtSetReportMode except AttributeError: # release build pass else: for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]: msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE) msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR) if ns.wait: input("Press any key to continue...") if ns.slaveargs is not None: args, kwargs = json.loads(ns.slaveargs) if kwargs.get('huntrleaks'): unittest.BaseTestSuite._cleanup = False try: result = runtest(*args, **kwargs) except KeyboardInterrupt: result = INTERRUPTED, '' except BaseException as e: traceback.print_exc() result = CHILD_ERROR, str(e) sys.stdout.flush() print() # Force a newline (just in case) print(json.dumps(result)) sys.exit(0) good = [] bad = [] skipped = [] resource_denieds = [] environment_changed = [] interrupted = False if ns.findleaks: try: import gc except ImportError: print('No GC available, disabling findleaks.') ns.findleaks = False else: # Uncomment the line below to report garbage that is not # freeable by reference counting alone. By default only # garbage that is not collectable by the GC is reported. #gc.set_debug(gc.DEBUG_SAVEALL) found_garbage = [] if ns.huntrleaks: unittest.BaseTestSuite._cleanup = False if ns.single: filename = os.path.join(TEMPDIR, 'pynexttest') try: with open(filename, 'r') as fp: next_test = fp.read().strip() tests = [next_test] except OSError: pass if ns.fromfile: tests = [] with open(os.path.join(support.SAVEDCWD, ns.fromfile)) as fp: count_pat = re.compile(r'\[\s*\d+/\s*\d+\]') for line in fp: line = count_pat.sub('', line) guts = line.split() # assuming no test has whitespace in its name if guts and not guts[0].startswith('#'): tests.extend(guts) # Strip .py extensions. removepy(ns.args) removepy(tests) stdtests = STDTESTS[:] nottests = NOTTESTS.copy() if ns.exclude: for arg in ns.args: if arg in stdtests: stdtests.remove(arg) nottests.add(arg) ns.args = [] # For a partial run, we do not need to clutter the output. if ns.verbose or ns.header or not (ns.quiet or ns.single or tests or ns.args): # Print basic platform information print("==", platform.python_implementation(), *sys.version.split()) print("== ", platform.platform(aliased=True), "%s-endian" % sys.byteorder) print("== ", "hash algorithm:", sys.hash_info.algorithm, "64bit" if sys.maxsize > 2**32 else "32bit") print("== ", os.getcwd()) print("Testing with flags:", sys.flags) # if testdir is set, then we are not running the python tests suite, so # don't add default tests to be executed or skipped (pass empty values) if ns.testdir: alltests = findtests(ns.testdir, list(), set()) else: alltests = findtests(ns.testdir, stdtests, nottests) selected = tests or ns.args or alltests if ns.single: selected = selected[:1] try: next_single_test = alltests[alltests.index(selected[0])+1] except IndexError: next_single_test = None # Remove all the selected tests that precede start if it's set. if ns.start: try: del selected[:selected.index(ns.start)] except ValueError: print("Couldn't find starting test (%s), using all tests" % ns.start) if ns.randomize: if ns.random_seed is None: ns.random_seed = random.randrange(10000000) random.seed(ns.random_seed) print("Using random seed", ns.random_seed) random.shuffle(selected) if ns.trace: import trace, tempfile tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix, tempfile.gettempdir()], trace=False, count=True) test_times = [] support.verbose = ns.verbose # Tell tests to be moderately quiet support.use_resources = ns.use_resources save_modules = sys.modules.keys() def accumulate_result(test, result): ok, test_time = result test_times.append((test_time, test)) if ok == PASSED: good.append(test) elif ok == FAILED: bad.append(test) elif ok == ENV_CHANGED: environment_changed.append(test) elif ok == SKIPPED: skipped.append(test) elif ok == RESOURCE_DENIED: skipped.append(test) resource_denieds.append(test) if ns.forever: def test_forever(tests=list(selected)): while True: for test in tests: yield test if bad: return tests = test_forever() test_count = '' test_count_width = 3 else: tests = iter(selected) test_count = '/{}'.format(len(selected)) test_count_width = len(test_count) - 1 if ns.use_mp: try: from threading import Thread except ImportError: print("Multiprocess option requires thread support") sys.exit(2) from queue import Queue debug_output_pat = re.compile(r"\[\d+ refs, \d+ blocks\]$") output = Queue() pending = MultiprocessTests(tests) def work(): # A worker thread. try: while True: try: test = next(pending) except StopIteration: output.put((None, None, None, None)) return retcode, stdout, stderr = run_test_in_subprocess(test, ns) # Strip last refcount output line if it exists, since it # comes from the shutdown of the interpreter in the subcommand. stderr = debug_output_pat.sub("", stderr) stdout, _, result = stdout.strip().rpartition("\n") if retcode != 0: result = (CHILD_ERROR, "Exit code %s" % retcode) output.put((test, stdout.rstrip(), stderr.rstrip(), result)) return if not result: output.put((None, None, None, None)) return result = json.loads(result) output.put((test, stdout.rstrip(), stderr.rstrip(), result)) except BaseException: output.put((None, None, None, None)) raise workers = [Thread(target=work) for i in range(ns.use_mp)] for worker in workers: worker.start() finished = 0 test_index = 1 try: while finished < ns.use_mp: test, stdout, stderr, result = output.get() if test is None: finished += 1 continue accumulate_result(test, result) if not ns.quiet: fmt = "[{1:{0}}{2}/{3}] {4}" if bad else "[{1:{0}}{2}] {4}" print(fmt.format( test_count_width, test_index, test_count, len(bad), test)) if stdout: print(stdout) if stderr: print(stderr, file=sys.stderr) sys.stdout.flush() sys.stderr.flush() if result[0] == INTERRUPTED: raise KeyboardInterrupt if result[0] == CHILD_ERROR: raise Exception("Child error on {}: {}".format(test, result[1])) test_index += 1 except KeyboardInterrupt: interrupted = True pending.interrupted = True for worker in workers: worker.join() else: for test_index, test in enumerate(tests, 1): if not ns.quiet: fmt = "[{1:{0}}{2}/{3}] {4}" if bad else "[{1:{0}}{2}] {4}" print(fmt.format( test_count_width, test_index, test_count, len(bad), test)) sys.stdout.flush() if ns.trace: # If we're tracing code coverage, then we don't exit with status # if on a false return value from main. tracer.runctx('runtest(test, ns.verbose, ns.quiet, timeout=ns.timeout)', globals=globals(), locals=vars()) else: try: result = runtest(test, ns.verbose, ns.quiet, ns.huntrleaks, output_on_failure=ns.verbose3, timeout=ns.timeout, failfast=ns.failfast, match_tests=ns.match_tests) accumulate_result(test, result) except KeyboardInterrupt: interrupted = True break if ns.findleaks: gc.collect() if gc.garbage: print("Warning: test created", len(gc.garbage), end=' ') print("uncollectable object(s).") # move the uncollectable objects somewhere so we don't see # them again found_garbage.extend(gc.garbage) del gc.garbage[:] # Unload the newly imported modules (best effort finalization) for module in sys.modules.keys(): if module not in save_modules and module.startswith("test."): support.unload(module) if interrupted: # print a newline after ^C print() print("Test suite interrupted by signal SIGINT.") omitted = set(selected) - set(good) - set(bad) - set(skipped) print(count(len(omitted), "test"), "omitted:") printlist(omitted) if good and not ns.quiet: if not bad and not skipped and not interrupted and len(good) > 1: print("All", end=' ') print(count(len(good), "test"), "OK.") if ns.print_slow: test_times.sort(reverse=True) print("10 slowest tests:") for time, test in test_times[:10]: print("%s: %.1fs" % (test, time)) if bad: print(count(len(bad), "test"), "failed:") printlist(bad) if environment_changed: print("{} altered the execution environment:".format( count(len(environment_changed), "test"))) printlist(environment_changed) if skipped and not ns.quiet: print(count(len(skipped), "test"), "skipped:") printlist(skipped) if ns.verbose2 and bad: print("Re-running failed tests in verbose mode") for test in bad[:]: print("Re-running test %r in verbose mode" % test) sys.stdout.flush() try: ns.verbose = True ok = runtest(test, True, ns.quiet, ns.huntrleaks, timeout=ns.timeout) except KeyboardInterrupt: # print a newline separate from the ^C print() break else: if ok[0] in {PASSED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED}: bad.remove(test) else: if bad: print(count(len(bad), 'test'), "failed again:") printlist(bad) if ns.single: if next_single_test: with open(filename, 'w') as fp: fp.write(next_single_test + '\n') else: os.unlink(filename) if ns.trace: r = tracer.results() r.write_results(show_missing=True, summary=True, coverdir=ns.coverdir) if ns.runleaks: os.system("leaks %d" % os.getpid()) sys.exit(len(bad) > 0 or interrupted)