From b9c73e8cf0d0bc70c555521974bc78ae2e890473 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 29 Jul 2011 23:53:38 +0200 Subject: [PATCH] Issue #12626: In regrtest, allow to filter tests using a glob filter with the `-m` (or `--match`) option. This works with all test cases using the unittest module. This is useful with long test suites such as test_io or test_subprocess. --- Lib/test/regrtest.py | 16 ++++++++++------ Lib/test/support.py | 22 ++++++++++++++++++++++ Misc/NEWS | 5 +++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 6fad78fc0b0..077434baaf7 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -38,6 +38,7 @@ Selecting tests -f/--fromfile -- read names of tests to run from a file (see below) -x/--exclude -- arguments are tests to *exclude* -s/--single -- single step through a set of tests (see below) +-m/--match PAT -- match test cases and methods with glob pattern PAT -G/--failfast -- fail as soon as a test fails (only with -v or -W) -u/--use RES1,RES2,... -- specify which special resource intensive tests to run @@ -242,7 +243,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, 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, forever=False, - header=False, failfast=False): + header=False, failfast=False, match_tests=None): """Execute a test suite. This also parses command-line options and modifies its behavior @@ -270,13 +271,13 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, support.record_original_stdout(sys.stdout) try: - opts, args = getopt.getopt(sys.argv[1:], 'hvqxsSrf:lu:t:TD:NLR:FwWM:nj:G', + opts, args = getopt.getopt(sys.argv[1:], 'hvqxsSrf:lu:t:TD:NLR:FwWM:nj:Gm:', ['help', 'verbose', 'verbose2', 'verbose3', 'quiet', 'exclude', 'single', 'slow', 'random', 'fromfile', 'findleaks', 'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir', 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', 'multiprocess=', 'coverage', 'slaveargs=', 'forever', 'debug', - 'start=', 'nowindows', 'header', 'failfast']) + 'start=', 'nowindows', 'header', 'failfast', 'match']) except getopt.error as msg: usage(msg) @@ -318,6 +319,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, random_seed = int(a) elif o in ('-f', '--fromfile'): fromfile = a + elif o in ('-m', '--match'): + match_tests = a elif o in ('-l', '--findleaks'): findleaks = True elif o in ('-L', '--runleaks'): @@ -551,7 +554,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, (test, verbose, quiet), dict(huntrleaks=huntrleaks, use_resources=use_resources, debug=debug, output_on_failure=verbose3, - failfast=failfast) + failfast=failfast, match_tests=match_tests) ) yield (test, args_tuple) pending = tests_and_args() @@ -630,7 +633,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, try: result = runtest(test, verbose, quiet, huntrleaks, debug, output_on_failure=verbose3, - failfast=failfast) + failfast=failfast, match_tests=match_tests) accumulate_result(test, result) except KeyboardInterrupt: interrupted = True @@ -777,7 +780,7 @@ def replace_stdout(): def runtest(test, verbose, quiet, huntrleaks=False, debug=False, use_resources=None, - output_on_failure=False, failfast=False): + output_on_failure=False, failfast=False, match_tests=None): """Run a single test. test -- the name of the test @@ -800,6 +803,7 @@ def runtest(test, verbose, quiet, if use_resources is not None: support.use_resources = use_resources try: + support.match_tests = match_tests if failfast: support.failfast = True if output_on_failure: diff --git a/Lib/test/support.py b/Lib/test/support.py index 09846a85a20..e82a4f9316d 100644 --- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -21,6 +21,7 @@ import subprocess import imp import time import sysconfig +import fnmatch import logging.handlers try: @@ -177,6 +178,7 @@ max_memuse = 0 # Disable bigmem tests (they will still be run with # small sizes, to make sure they work.) real_max_memuse = 0 failfast = False +match_tests = None # _original_stdout is meant to hold stdout at the time regrtest began. # This may be "the real" stdout, or IDLE's emulation of stdout, or whatever. @@ -1183,6 +1185,18 @@ def check_impl_detail(**guards): return guards.get(platform.python_implementation().lower(), default) +def _filter_suite(suite, pred): + """Recursively filter test cases in a suite based on a predicate.""" + newtests = [] + for test in suite._tests: + if isinstance(test, unittest.TestSuite): + _filter_suite(test, pred) + newtests.append(test) + else: + if pred(test): + newtests.append(test) + suite._tests = newtests + def _run_suite(suite): """Run tests from a unittest.TestSuite-derived class.""" @@ -1218,6 +1232,14 @@ def run_unittest(*classes): suite.addTest(cls) else: suite.addTest(unittest.makeSuite(cls)) + def case_pred(test): + if match_tests is None: + return True + for name in test.id().split("."): + if fnmatch.fnmatchcase(name, match_tests): + return True + return False + _filter_suite(suite, case_pred) _run_suite(suite) diff --git a/Misc/NEWS b/Misc/NEWS index b080316f20b..ef432e0d296 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -137,6 +137,11 @@ Tools/Demos Tests ----- +- Issue #12626: In regrtest, allow to filter tests using a glob filter + with the ``-m`` (or ``--match``) option. This works with all test cases + using the unittest module. This is useful with long test suites + such as test_io or test_subprocess. + - Issue #12624: It is now possible to fail after the first failure when running in verbose mode (``-v`` or ``-W``), by using the ``--failfast`` (or ``-G``) option to regrtest. This is useful with long test suites