Skip to content

Commit 24c2c20

Browse files
authored
bpo-30540, bpo-30523: Add --matchfile and --list-cases options to regrtest (#2249)
[2.7] bpo-30540, bpo-30523: Add --matchfile and --list-cases options to regrtest
1 parent 8bb0863 commit 24c2c20

3 files changed

Lines changed: 141 additions & 12 deletions

File tree

‎Lib/test/regrtest.py‎

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
-x/--exclude -- arguments are tests to *exclude*
3939
-s/--single -- single step through a set of tests (see below)
4040
-m/--match PAT -- match test cases and methods with glob pattern PAT
41+
--matchfile FILENAME -- filters tests using a text file, one pattern per line
4142
-G/--failfast -- fail as soon as a test fails (only with -v or -W)
4243
-u/--use RES1,RES2,...
4344
-- specify which special resource intensive tests to run
@@ -64,6 +65,8 @@
6465
(instead of the Python stdlib test suite)
6566
--list-tests -- only write the name of tests that will be run,
6667
don't execute them
68+
--list-cases -- only write the name of test cases that will be run,
69+
don't execute them
6770
6871
6972
Additional Option Details:
@@ -157,6 +160,13 @@
157160
To enable all resources except one, use '-uall,-<resource>'. For
158161
example, to run all the tests except for the bsddb tests, give the
159162
option '-uall,-bsddb'.
163+
164+
--matchfile filters tests using a text file, one pattern per line.
165+
Pattern examples:
166+
167+
- test method: test_stat_attributes
168+
- test class: FileTests
169+
- test identifier: test_os.FileTests.test_stat_attributes
160170
"""
161171

162172
import StringIO
@@ -316,7 +326,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
316326
'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir',
317327
'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=',
318328
'multiprocess=', 'slaveargs=', 'forever', 'header', 'pgo',
319-
'failfast', 'match=', 'testdir=', 'list-tests', 'coverage'])
329+
'failfast', 'match=', 'testdir=', 'list-tests', 'list-cases',
330+
'coverage', 'matchfile='])
320331
except getopt.error, msg:
321332
usage(2, msg)
322333

@@ -327,6 +338,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
327338
use_resources = []
328339
slaveargs = None
329340
list_tests = False
341+
list_cases_opt = False
330342
for o, a in opts:
331343
if o in ('-h', '--help'):
332344
usage(0)
@@ -354,7 +366,16 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
354366
elif o in ('-f', '--fromfile'):
355367
fromfile = a
356368
elif o in ('-m', '--match'):
357-
match_tests = a
369+
if match_tests is None:
370+
match_tests = []
371+
match_tests.append(a)
372+
elif o == '--matchfile':
373+
if match_tests is None:
374+
match_tests = []
375+
filename = os.path.join(test_support.SAVEDCWD, a)
376+
with open(filename) as fp:
377+
for line in fp:
378+
match_tests.append(line.strip())
358379
elif o in ('-l', '--findleaks'):
359380
findleaks = True
360381
elif o in ('-L', '--runleaks'):
@@ -416,6 +437,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
416437
testdir = a
417438
elif o == '--list-tests':
418439
list_tests = True
440+
elif o == '--list-cases':
441+
list_cases_opt = True
419442
else:
420443
print >>sys.stderr, ("No handler for option {}. Please "
421444
"report this as a bug at http://bugs.python.org.").format(o)
@@ -534,6 +557,10 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
534557
print(name)
535558
sys.exit(0)
536559

560+
if list_cases_opt:
561+
list_cases(testdir, selected)
562+
sys.exit(0)
563+
537564
if trace:
538565
import trace
539566
tracer = trace.Trace(trace=False, count=True)
@@ -1124,11 +1151,7 @@ def runtest_inner(test, verbose, quiet, huntrleaks=False, pgo=False, testdir=Non
11241151
try:
11251152
if capture_stdout:
11261153
sys.stdout = capture_stdout
1127-
if test.startswith('test.') or testdir:
1128-
abstest = test
1129-
else:
1130-
# Always import it from the test package
1131-
abstest = 'test.' + test
1154+
abstest = get_abs_module(testdir, test)
11321155
clear_caches()
11331156
with saved_test_environment(test, verbose, quiet, pgo) as environment:
11341157
start_time = time.time()
@@ -1452,7 +1475,7 @@ def count(n, word):
14521475
else:
14531476
return "%d %ss" % (n, word)
14541477

1455-
def printlist(x, width=70, indent=4):
1478+
def printlist(x, width=70, indent=4, file=None):
14561479
"""Print the elements of iterable x to stdout.
14571480
14581481
Optional arg width (default 70) is the maximum line length.
@@ -1463,8 +1486,37 @@ def printlist(x, width=70, indent=4):
14631486
from textwrap import fill
14641487
blanks = ' ' * indent
14651488
# Print the sorted list: 'x' may be a '--random' list or a set()
1466-
print fill(' '.join(str(elt) for elt in sorted(x)), width,
1467-
initial_indent=blanks, subsequent_indent=blanks)
1489+
print >>file, fill(' '.join(str(elt) for elt in sorted(x)), width,
1490+
initial_indent=blanks, subsequent_indent=blanks)
1491+
1492+
def get_abs_module(testdir, test):
1493+
if test.startswith('test.') or testdir:
1494+
return test
1495+
else:
1496+
# Always import it from the test package
1497+
return 'test.' + test
1498+
1499+
def _list_cases(suite):
1500+
for test in suite:
1501+
if isinstance(test, unittest.TestSuite):
1502+
_list_cases(test)
1503+
elif isinstance(test, unittest.TestCase):
1504+
print(test.id())
1505+
1506+
def list_cases(testdir, selected):
1507+
skipped = []
1508+
for test in selected:
1509+
abstest = get_abs_module(testdir, test)
1510+
try:
1511+
suite = unittest.defaultTestLoader.loadTestsFromName(abstest)
1512+
_list_cases(suite)
1513+
except unittest.SkipTest:
1514+
skipped.append(test)
1515+
1516+
if skipped:
1517+
print >>sys.stderr
1518+
print >>sys.stderr, count(len(skipped), "test"), "skipped:"
1519+
printlist(skipped, file=sys.stderr)
14681520

14691521
# Map sys.platform to a string containing the basenames of tests
14701522
# expected to be skipped on that platform.

‎Lib/test/support/__init__.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,9 +1557,15 @@ def run_unittest(*classes):
15571557
def case_pred(test):
15581558
if match_tests is None:
15591559
return True
1560-
for name in test.id().split("."):
1561-
if fnmatch.fnmatchcase(name, match_tests):
1560+
test_id = test.id()
1561+
1562+
for match_test in match_tests:
1563+
if fnmatch.fnmatchcase(test_id, match_test):
15621564
return True
1565+
1566+
for name in test_id.split("."):
1567+
if fnmatch.fnmatchcase(name, match_test):
1568+
return True
15631569
return False
15641570
_filter_suite(suite, case_pred)
15651571
_run_suite(suite)

‎Lib/test/test_regrtest.py‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,77 @@ def test_crashed(self):
482482
self.check_executed_tests(output, tests, failed=crash_test,
483483
randomize=True)
484484

485+
def parse_methods(self, output):
486+
regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE)
487+
return [match.group(1) for match in regex.finditer(output)]
488+
489+
def test_matchfile(self):
490+
# Any code which causes a crash
491+
code = textwrap.dedent("""
492+
import unittest
493+
from test import support
494+
495+
class Tests(unittest.TestCase):
496+
def test_method1(self):
497+
pass
498+
def test_method2(self):
499+
pass
500+
def test_method3(self):
501+
pass
502+
def test_method4(self):
503+
pass
504+
505+
def test_main():
506+
support.run_unittest(Tests)
507+
""")
508+
all_methods = ['test_method1', 'test_method2',
509+
'test_method3', 'test_method4']
510+
testname = self.create_test(code=code)
511+
512+
# by default, all methods should be run
513+
output = self.run_tests("-v", testname)
514+
methods = self.parse_methods(output)
515+
self.assertEqual(methods, all_methods)
516+
517+
# only run a subset
518+
filename = support.TESTFN
519+
self.addCleanup(support.unlink, filename)
520+
521+
subset = [
522+
# only match the method name
523+
'test_method1',
524+
# match the full identifier
525+
'%s.Tests.test_method3' % testname]
526+
with open(filename, "w") as fp:
527+
for name in subset:
528+
print(name, file=fp)
529+
530+
output = self.run_tests("-v", "--matchfile", filename, testname)
531+
methods = self.parse_methods(output)
532+
subset = ['test_method1', 'test_method3']
533+
self.assertEqual(methods, subset)
534+
535+
def test_list_cases(self):
536+
# test --list-cases
537+
code = textwrap.dedent("""
538+
import unittest
539+
from test import support
540+
541+
class Tests(unittest.TestCase):
542+
def test_method1(self):
543+
pass
544+
def test_method2(self):
545+
pass
546+
547+
def test_main():
548+
support.run_unittest(Tests)
549+
""")
550+
testname = self.create_test(code=code)
551+
all_methods = ['%s.Tests.test_method1' % testname,
552+
'%s.Tests.test_method2' % testname]
553+
output = self.run_tests('--list-cases', testname)
554+
self.assertEqual(output.splitlines(), all_methods)
555+
485556

486557
def test_main():
487558
support.run_unittest(ProgramsTestCase, ArgsTestCase)

0 commit comments

Comments
 (0)