Commit ef8320cf authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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.
parent 824f6879
...@@ -117,6 +117,13 @@ resources to test. Currently only the following are defined: ...@@ -117,6 +117,13 @@ resources to test. Currently only the following are defined:
To enable all resources except one, use '-uall,-<resource>'. For To enable all resources except one, use '-uall,-<resource>'. For
example, to run all the tests except for the gui tests, give the example, to run all the tests except for the gui tests, give the
option '-uall,-gui'. option '-uall,-gui'.
--matchfile filters tests using a text file, one pattern per line.
Pattern examples:
- test method: test_stat_attributes
- test class: FileTests
- test identifier: test_os.FileTests.test_stat_attributes
""" """
...@@ -189,8 +196,12 @@ def _create_parser(): ...@@ -189,8 +196,12 @@ def _create_parser():
help='single step through a set of tests.' + help='single step through a set of tests.' +
more_details) more_details)
group.add_argument('-m', '--match', metavar='PAT', group.add_argument('-m', '--match', metavar='PAT',
dest='match_tests', dest='match_tests', action='append',
help='match test cases and methods with glob pattern PAT') help='match test cases and methods with glob pattern PAT')
group.add_argument('--matchfile', metavar='FILENAME',
dest='match_filename',
help='similar to --match but get patterns from a '
'text file, one pattern per line')
group.add_argument('-G', '--failfast', action='store_true', group.add_argument('-G', '--failfast', action='store_true',
help='fail as soon as a test fails (only with -v or -W)') help='fail as soon as a test fails (only with -v or -W)')
group.add_argument('-u', '--use', metavar='RES1,RES2,...', group.add_argument('-u', '--use', metavar='RES1,RES2,...',
...@@ -350,5 +361,12 @@ def _parse_args(args, **kwargs): ...@@ -350,5 +361,12 @@ def _parse_args(args, **kwargs):
print("WARNING: Disable --verbose3 because it's incompatible with " print("WARNING: Disable --verbose3 because it's incompatible with "
"--huntrleaks: see http://bugs.python.org/issue27103", "--huntrleaks: see http://bugs.python.org/issue27103",
file=sys.stderr) file=sys.stderr)
if ns.match_filename:
if ns.match_tests is None:
ns.match_tests = []
filename = os.path.join(support.SAVEDCWD, ns.match_filename)
with open(filename) as fp:
for line in fp:
ns.match_tests.append(line.strip())
return ns return ns
...@@ -1922,9 +1922,15 @@ def run_unittest(*classes): ...@@ -1922,9 +1922,15 @@ def run_unittest(*classes):
def case_pred(test): def case_pred(test):
if match_tests is None: if match_tests is None:
return True return True
for name in test.id().split("."): test_id = test.id()
if fnmatch.fnmatchcase(name, match_tests):
for match_test in match_tests:
if fnmatch.fnmatchcase(test_id, match_test):
return True return True
for name in test_id.split("."):
if fnmatch.fnmatchcase(name, match_test):
return True
return False return False
_filter_suite(suite, case_pred) _filter_suite(suite, case_pred)
_run_suite(suite) _run_suite(suite)
......
...@@ -159,9 +159,24 @@ class ParseArgsTestCase(unittest.TestCase): ...@@ -159,9 +159,24 @@ class ParseArgsTestCase(unittest.TestCase):
for opt in '-m', '--match': for opt in '-m', '--match':
with self.subTest(opt=opt): with self.subTest(opt=opt):
ns = libregrtest._parse_args([opt, 'pattern']) ns = libregrtest._parse_args([opt, 'pattern'])
self.assertEqual(ns.match_tests, 'pattern') self.assertEqual(ns.match_tests, ['pattern'])
self.checkError([opt], 'expected one argument') self.checkError([opt], 'expected one argument')
ns = libregrtest._parse_args(['-m', 'pattern1',
'-m', 'pattern2'])
self.assertEqual(ns.match_tests, ['pattern1', 'pattern2'])
self.addCleanup(support.unlink, support.TESTFN)
with open(support.TESTFN, "w") as fp:
print('matchfile1', file=fp)
print('matchfile2', file=fp)
filename = os.path.abspath(support.TESTFN)
ns = libregrtest._parse_args(['-m', 'match',
'--matchfile', filename])
self.assertEqual(ns.match_tests,
['match', 'matchfile1', 'matchfile2'])
def test_failfast(self): def test_failfast(self):
for opt in '-G', '--failfast': for opt in '-G', '--failfast':
with self.subTest(opt=opt): with self.subTest(opt=opt):
...@@ -275,7 +290,6 @@ class ParseArgsTestCase(unittest.TestCase): ...@@ -275,7 +290,6 @@ class ParseArgsTestCase(unittest.TestCase):
ns = libregrtest._parse_args([opt]) ns = libregrtest._parse_args([opt])
self.assertTrue(ns.forever) self.assertTrue(ns.forever)
def test_unrecognized_argument(self): def test_unrecognized_argument(self):
self.checkError(['--xxx'], 'usage:') self.checkError(['--xxx'], 'usage:')
...@@ -457,7 +471,6 @@ class BaseTestCase(unittest.TestCase): ...@@ -457,7 +471,6 @@ class BaseTestCase(unittest.TestCase):
self.fail(msg) self.fail(msg)
return proc return proc
def run_python(self, args, **kw): def run_python(self, args, **kw):
args = [sys.executable, '-X', 'faulthandler', '-I', *args] args = [sys.executable, '-X', 'faulthandler', '-I', *args]
proc = self.run_command(args, **kw) proc = self.run_command(args, **kw)
...@@ -823,6 +836,52 @@ class ArgsTestCase(BaseTestCase): ...@@ -823,6 +836,52 @@ class ArgsTestCase(BaseTestCase):
self.check_executed_tests(output, tests, failed=crash_test, self.check_executed_tests(output, tests, failed=crash_test,
randomize=True) randomize=True)
def parse_methods(self, output):
regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE)
return [match.group(1) for match in regex.finditer(output)]
def test_matchfile(self):
# Any code which causes a crash
code = textwrap.dedent("""
import unittest
class Tests(unittest.TestCase):
def test_method1(self):
pass
def test_method2(self):
pass
def test_method3(self):
pass
def test_method4(self):
pass
""")
all_methods = ['test_method1', 'test_method2',
'test_method3', 'test_method4']
testname = self.create_test(code=code)
# by default, all methods should be run
output = self.run_tests("-v", testname)
methods = self.parse_methods(output)
self.assertEqual(methods, all_methods)
# only run a subset
filename = support.TESTFN
self.addCleanup(support.unlink, filename)
subset = [
# only match the method name
'test_method1',
# match the full identifier
'%s.Tests.test_method3' % testname]
with open(filename, "w") as fp:
for name in subset:
print(name, file=fp)
output = self.run_tests("-v", "--matchfile", filename, testname)
methods = self.parse_methods(output)
subset = ['test_method1', 'test_method3']
self.assertEqual(methods, subset)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment