Skip to content

Commit ef8320c

Browse files
authored
bpo-30540: regrtest: add --matchfile option (#1909)
* Add a new option taking a filename to get a list of test names to filter tests. * support.match_tests becomes a list. * Modify run_unittest() to accept to match the whole test identifier, not just a part of a test identifier. For example, the following command only runs test_default_timeout() of the BarrierTests class of test_threading: $ ./python -m test -v test_threading -m test.test_threading.BarrierTests.test_default_timeout Remove also some empty lines from test_regrtest.py to make flake8 tool happy.
1 parent 824f687 commit ef8320c

File tree

3 files changed

+89
-6
lines changed

3 files changed

+89
-6
lines changed

Lib/test/libregrtest/cmdline.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,13 @@
117117
To enable all resources except one, use '-uall,-<resource>'. For
118118
example, to run all the tests except for the gui tests, give the
119119
option '-uall,-gui'.
120+
121+
--matchfile filters tests using a text file, one pattern per line.
122+
Pattern examples:
123+
124+
- test method: test_stat_attributes
125+
- test class: FileTests
126+
- test identifier: test_os.FileTests.test_stat_attributes
120127
"""
121128

122129

@@ -189,8 +196,12 @@ def _create_parser():
189196
help='single step through a set of tests.' +
190197
more_details)
191198
group.add_argument('-m', '--match', metavar='PAT',
192-
dest='match_tests',
199+
dest='match_tests', action='append',
193200
help='match test cases and methods with glob pattern PAT')
201+
group.add_argument('--matchfile', metavar='FILENAME',
202+
dest='match_filename',
203+
help='similar to --match but get patterns from a '
204+
'text file, one pattern per line')
194205
group.add_argument('-G', '--failfast', action='store_true',
195206
help='fail as soon as a test fails (only with -v or -W)')
196207
group.add_argument('-u', '--use', metavar='RES1,RES2,...',
@@ -350,5 +361,12 @@ def _parse_args(args, **kwargs):
350361
print("WARNING: Disable --verbose3 because it's incompatible with "
351362
"--huntrleaks: see http://bugs.python.org/issue27103",
352363
file=sys.stderr)
364+
if ns.match_filename:
365+
if ns.match_tests is None:
366+
ns.match_tests = []
367+
filename = os.path.join(support.SAVEDCWD, ns.match_filename)
368+
with open(filename) as fp:
369+
for line in fp:
370+
ns.match_tests.append(line.strip())
353371

354372
return ns

Lib/test/support/__init__.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1922,9 +1922,15 @@ def run_unittest(*classes):
19221922
def case_pred(test):
19231923
if match_tests is None:
19241924
return True
1925-
for name in test.id().split("."):
1926-
if fnmatch.fnmatchcase(name, match_tests):
1925+
test_id = test.id()
1926+
1927+
for match_test in match_tests:
1928+
if fnmatch.fnmatchcase(test_id, match_test):
19271929
return True
1930+
1931+
for name in test_id.split("."):
1932+
if fnmatch.fnmatchcase(name, match_test):
1933+
return True
19281934
return False
19291935
_filter_suite(suite, case_pred)
19301936
_run_suite(suite)

Lib/test/test_regrtest.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,24 @@ def test_match(self):
159159
for opt in '-m', '--match':
160160
with self.subTest(opt=opt):
161161
ns = libregrtest._parse_args([opt, 'pattern'])
162-
self.assertEqual(ns.match_tests, 'pattern')
162+
self.assertEqual(ns.match_tests, ['pattern'])
163163
self.checkError([opt], 'expected one argument')
164164

165+
ns = libregrtest._parse_args(['-m', 'pattern1',
166+
'-m', 'pattern2'])
167+
self.assertEqual(ns.match_tests, ['pattern1', 'pattern2'])
168+
169+
self.addCleanup(support.unlink, support.TESTFN)
170+
with open(support.TESTFN, "w") as fp:
171+
print('matchfile1', file=fp)
172+
print('matchfile2', file=fp)
173+
174+
filename = os.path.abspath(support.TESTFN)
175+
ns = libregrtest._parse_args(['-m', 'match',
176+
'--matchfile', filename])
177+
self.assertEqual(ns.match_tests,
178+
['match', 'matchfile1', 'matchfile2'])
179+
165180
def test_failfast(self):
166181
for opt in '-G', '--failfast':
167182
with self.subTest(opt=opt):
@@ -275,7 +290,6 @@ def test_forever(self):
275290
ns = libregrtest._parse_args([opt])
276291
self.assertTrue(ns.forever)
277292

278-
279293
def test_unrecognized_argument(self):
280294
self.checkError(['--xxx'], 'usage:')
281295

@@ -457,7 +471,6 @@ def run_command(self, args, input=None, exitcode=0, **kw):
457471
self.fail(msg)
458472
return proc
459473

460-
461474
def run_python(self, args, **kw):
462475
args = [sys.executable, '-X', 'faulthandler', '-I', *args]
463476
proc = self.run_command(args, **kw)
@@ -823,6 +836,52 @@ def test_crashed(self):
823836
self.check_executed_tests(output, tests, failed=crash_test,
824837
randomize=True)
825838

839+
def parse_methods(self, output):
840+
regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE)
841+
return [match.group(1) for match in regex.finditer(output)]
842+
843+
def test_matchfile(self):
844+
# Any code which causes a crash
845+
code = textwrap.dedent("""
846+
import unittest
847+
848+
class Tests(unittest.TestCase):
849+
def test_method1(self):
850+
pass
851+
def test_method2(self):
852+
pass
853+
def test_method3(self):
854+
pass
855+
def test_method4(self):
856+
pass
857+
""")
858+
all_methods = ['test_method1', 'test_method2',
859+
'test_method3', 'test_method4']
860+
testname = self.create_test(code=code)
861+
862+
# by default, all methods should be run
863+
output = self.run_tests("-v", testname)
864+
methods = self.parse_methods(output)
865+
self.assertEqual(methods, all_methods)
866+
867+
# only run a subset
868+
filename = support.TESTFN
869+
self.addCleanup(support.unlink, filename)
870+
871+
subset = [
872+
# only match the method name
873+
'test_method1',
874+
# match the full identifier
875+
'%s.Tests.test_method3' % testname]
876+
with open(filename, "w") as fp:
877+
for name in subset:
878+
print(name, file=fp)
879+
880+
output = self.run_tests("-v", "--matchfile", filename, testname)
881+
methods = self.parse_methods(output)
882+
subset = ['test_method1', 'test_method3']
883+
self.assertEqual(methods, subset)
884+
826885

827886
if __name__ == '__main__':
828887
unittest.main()

0 commit comments

Comments
 (0)