diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 259e592f733..7785f37ebcb 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -43,6 +43,7 @@ Special runs -N/--nocoverdir -- Put coverage files alongside modules -t/--threshold THRESHOLD -- call gc.set_threshold(THRESHOLD) +-F/--forever -- run the selected tests in a loop, until an error happens If non-option arguments are present, they are names for tests to run, unless -x is given, in which case they are names for tests not to run. @@ -150,6 +151,7 @@ option '-uall,-bsddb'. import cStringIO import getopt +import itertools import json import os import random @@ -219,7 +221,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, exclude=False, single=False, randomize=False, fromfile=None, findleaks=False, use_resources=None, trace=False, coverdir='coverage', runleaks=False, huntrleaks=False, verbose2=False, print_slow=False, - random_seed=None, use_mp=None, verbose3=False): + random_seed=None, use_mp=None, verbose3=False, forever=False): """Execute a test suite. This also parses command-line options and modifies its behavior @@ -244,12 +246,12 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, test_support.record_original_stdout(sys.stdout) try: - opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsSrf:lu:t:TD:NLR:wWM:j:', + opts, args = getopt.getopt(sys.argv[1:], 'hvgqxsSrf:lu:t:TD:NLR:FwWM:j:', ['help', 'verbose', 'verbose2', 'verbose3', 'quiet', 'exclude', 'single', 'slow', 'random', 'fromfile', 'findleaks', 'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir', 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', - 'multiprocess=', 'slaveargs=']) + 'multiprocess=', 'slaveargs=', 'forever']) except getopt.error, msg: usage(2, msg) @@ -329,6 +331,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, use_resources.remove(r) elif r not in use_resources: use_resources.append(r) + elif o in ('-F', '--forever'): + forever = True elif o in ('-j', '--multiprocess'): use_mp = int(a) elif o == '--slaveargs': @@ -371,8 +375,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, filename = os.path.join(gettempdir(), 'pynexttest') try: fp = open(filename, 'r') - next = fp.read().strip() - tests = [next] + next_test = fp.read().strip() + tests = [next_test] fp.close() except IOError: pass @@ -411,6 +415,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, import trace tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], trace=False, count=True) + test_times = [] test_support.use_resources = use_resources save_modules = sys.modules.keys() @@ -431,6 +436,17 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, skipped.append(test) resource_denieds.append(test) + if forever: + def test_forever(tests=list(tests)): + while True: + for test in tests: + yield test + if bad: + return + tests = test_forever() + else: + tests = iter(tests) + if use_mp: from threading import Thread from Queue import Queue, Empty @@ -439,19 +455,21 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, debug_output_pat = re.compile(r"\[\d+ refs\]$") pending = deque() output = Queue() - for test in tests: - args_tuple = ( - (test, verbose, quiet, testdir), - dict(huntrleaks=huntrleaks, use_resources=use_resources) - ) - pending.append((test, args_tuple)) + def tests_and_args(): + for test in tests: + args_tuple = ( + (test, verbose, quiet, testdir), + dict(huntrleaks=huntrleaks, use_resources=use_resources) + ) + yield (test, args_tuple) + pending = tests_and_args() def work(): # A worker thread. try: while True: try: - test, args_tuple = pending.popleft() - except IndexError: + test, args_tuple = next(pending) + except StopIteration: output.put((None, None, None, None)) return # -E is needed by some tests, e.g. test_import @@ -464,6 +482,9 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, # comes from the shutdown of the interpreter in the subcommand. stderr = debug_output_pat.sub("", stderr) stdout, _, result = stdout.strip().rpartition("\n") + if not result: + output.put((None, None, None, None)) + return result = json.loads(result) if not quiet: stdout = test+'\n'+stdout @@ -475,20 +496,22 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, for worker in workers: worker.start() finished = 0 - while finished < use_mp: - test, stdout, stderr, result = output.get() - if test is None: - finished += 1 - continue - if stdout: - print stdout - if stderr: - print >>sys.stderr, stderr - if result[0] == INTERRUPTED: - assert result[1] == 'KeyboardInterrupt' - pending.clear() - raise KeyboardInterrupt # What else? - accumulate_result(test, result) + try: + while finished < use_mp: + test, stdout, stderr, result = output.get() + if test is None: + finished += 1 + continue + if stdout: + print stdout + if stderr: + print >>sys.stderr, stderr + if result[0] == INTERRUPTED: + assert result[1] == 'KeyboardInterrupt' + raise KeyboardInterrupt # What else? + accumulate_result(test, result) + except KeyboardInterrupt: + pending.close() for worker in workers: worker.join() else: diff --git a/Misc/NEWS b/Misc/NEWS index e03d4a78038..4e2b131bfde 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -1567,6 +1567,9 @@ Extension Modules Tests ----- +- Issue #7312: Add a -F flag to run the selected tests in a loop until + a test fails. Can be combined with -j. + - Issue #7295: Do not use a hardcoded file name in test_tarfile. - Issue #7270: Add some dedicated unit tests for multi-thread synchronization