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.
This commit is contained in:
Antoine Pitrou 2011-07-29 23:53:38 +02:00
parent def35435ee
commit b9c73e8cf0
3 changed files with 37 additions and 6 deletions

View File

@ -38,6 +38,7 @@ Selecting tests
-f/--fromfile -- read names of tests to run from a file (see below) -f/--fromfile -- read names of tests to run from a file (see below)
-x/--exclude -- arguments are tests to *exclude* -x/--exclude -- arguments are tests to *exclude*
-s/--single -- single step through a set of tests (see below) -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) -G/--failfast -- fail as soon as a test fails (only with -v or -W)
-u/--use RES1,RES2,... -u/--use RES1,RES2,...
-- specify which special resource intensive tests to run -- 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', findleaks=False, use_resources=None, trace=False, coverdir='coverage',
runleaks=False, huntrleaks=False, verbose2=False, print_slow=False, runleaks=False, huntrleaks=False, verbose2=False, print_slow=False,
random_seed=None, use_mp=None, verbose3=False, forever=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. """Execute a test suite.
This also parses command-line options and modifies its behavior 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) support.record_original_stdout(sys.stdout)
try: 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', ['help', 'verbose', 'verbose2', 'verbose3', 'quiet',
'exclude', 'single', 'slow', 'random', 'fromfile', 'findleaks', 'exclude', 'single', 'slow', 'random', 'fromfile', 'findleaks',
'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir', 'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir',
'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=',
'multiprocess=', 'coverage', 'slaveargs=', 'forever', 'debug', 'multiprocess=', 'coverage', 'slaveargs=', 'forever', 'debug',
'start=', 'nowindows', 'header', 'failfast']) 'start=', 'nowindows', 'header', 'failfast', 'match'])
except getopt.error as msg: except getopt.error as msg:
usage(msg) usage(msg)
@ -318,6 +319,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
random_seed = int(a) random_seed = int(a)
elif o in ('-f', '--fromfile'): elif o in ('-f', '--fromfile'):
fromfile = a fromfile = a
elif o in ('-m', '--match'):
match_tests = a
elif o in ('-l', '--findleaks'): elif o in ('-l', '--findleaks'):
findleaks = True findleaks = True
elif o in ('-L', '--runleaks'): elif o in ('-L', '--runleaks'):
@ -551,7 +554,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
(test, verbose, quiet), (test, verbose, quiet),
dict(huntrleaks=huntrleaks, use_resources=use_resources, dict(huntrleaks=huntrleaks, use_resources=use_resources,
debug=debug, output_on_failure=verbose3, debug=debug, output_on_failure=verbose3,
failfast=failfast) failfast=failfast, match_tests=match_tests)
) )
yield (test, args_tuple) yield (test, args_tuple)
pending = tests_and_args() pending = tests_and_args()
@ -630,7 +633,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
try: try:
result = runtest(test, verbose, quiet, huntrleaks, debug, result = runtest(test, verbose, quiet, huntrleaks, debug,
output_on_failure=verbose3, output_on_failure=verbose3,
failfast=failfast) failfast=failfast, match_tests=match_tests)
accumulate_result(test, result) accumulate_result(test, result)
except KeyboardInterrupt: except KeyboardInterrupt:
interrupted = True interrupted = True
@ -777,7 +780,7 @@ def replace_stdout():
def runtest(test, verbose, quiet, def runtest(test, verbose, quiet,
huntrleaks=False, debug=False, use_resources=None, 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. """Run a single test.
test -- the name of the test test -- the name of the test
@ -800,6 +803,7 @@ def runtest(test, verbose, quiet,
if use_resources is not None: if use_resources is not None:
support.use_resources = use_resources support.use_resources = use_resources
try: try:
support.match_tests = match_tests
if failfast: if failfast:
support.failfast = True support.failfast = True
if output_on_failure: if output_on_failure:

View File

@ -21,6 +21,7 @@ import subprocess
import imp import imp
import time import time
import sysconfig import sysconfig
import fnmatch
import logging.handlers import logging.handlers
try: try:
@ -177,6 +178,7 @@ max_memuse = 0 # Disable bigmem tests (they will still be run with
# small sizes, to make sure they work.) # small sizes, to make sure they work.)
real_max_memuse = 0 real_max_memuse = 0
failfast = False failfast = False
match_tests = None
# _original_stdout is meant to hold stdout at the time regrtest began. # _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. # 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) 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): def _run_suite(suite):
"""Run tests from a unittest.TestSuite-derived class.""" """Run tests from a unittest.TestSuite-derived class."""
@ -1218,6 +1232,14 @@ def run_unittest(*classes):
suite.addTest(cls) suite.addTest(cls)
else: else:
suite.addTest(unittest.makeSuite(cls)) 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) _run_suite(suite)

View File

@ -137,6 +137,11 @@ Tools/Demos
Tests 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 - Issue #12624: It is now possible to fail after the first failure when
running in verbose mode (``-v`` or ``-W``), by using the ``--failfast`` running in verbose mode (``-v`` or ``-W``), by using the ``--failfast``
(or ``-G``) option to regrtest. This is useful with long test suites (or ``-G``) option to regrtest. This is useful with long test suites