Skip to content

Commit bd5ba6e

Browse files
committed
bpo-37957: Allow regrtest to receive a file with test (and subtests) to ignore
1 parent da6ce58 commit bd5ba6e

File tree

7 files changed

+174
-24
lines changed

7 files changed

+174
-24
lines changed

Lib/test/libregrtest/cmdline.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,17 @@ def _create_parser():
207207
group.add_argument('-m', '--match', metavar='PAT',
208208
dest='match_tests', action='append',
209209
help='match test cases and methods with glob pattern PAT')
210+
group.add_argument('-i', '--ignore', metavar='PAT',
211+
dest='ignore_tests', action='append',
212+
help='ignore test cases and methods with glob pattern PAT')
210213
group.add_argument('--matchfile', metavar='FILENAME',
211214
dest='match_filename',
212215
help='similar to --match but get patterns from a '
213216
'text file, one pattern per line')
217+
group.add_argument('--ignorefile', metavar='FILENAME',
218+
dest='ignore_filename',
219+
help='similar to --matchfile but it receives patterns '
220+
'from text file to ignore')
214221
group.add_argument('-G', '--failfast', action='store_true',
215222
help='fail as soon as a test fails (only with -v or -W)')
216223
group.add_argument('-u', '--use', metavar='RES1,RES2,...',
@@ -317,7 +324,8 @@ def _parse_args(args, **kwargs):
317324
findleaks=1, use_resources=None, trace=False, coverdir='coverage',
318325
runleaks=False, huntrleaks=False, verbose2=False, print_slow=False,
319326
random_seed=None, use_mp=None, verbose3=False, forever=False,
320-
header=False, failfast=False, match_tests=None, pgo=False)
327+
header=False, failfast=False, match_tests=None, ignore_tests=None,
328+
pgo=False)
321329
for k, v in kwargs.items():
322330
if not hasattr(ns, k):
323331
raise TypeError('%r is an invalid keyword argument '
@@ -395,6 +403,12 @@ def _parse_args(args, **kwargs):
395403
with open(ns.match_filename) as fp:
396404
for line in fp:
397405
ns.match_tests.append(line.strip())
406+
if ns.ignore_filename:
407+
if ns.ignore_tests is None:
408+
ns.ignore_tests = []
409+
with open(ns.ignore_filename) as fp:
410+
for line in fp:
411+
ns.ignore_tests.append(line.strip())
398412
if ns.forever:
399413
# --forever implies --failfast
400414
ns.failfast = True

Lib/test/libregrtest/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ def _list_cases(self, suite):
287287

288288
def list_cases(self):
289289
support.verbose = False
290-
support.set_match_tests(self.ns.match_tests)
290+
support.set_match_tests(self.ns.match_tests, self.ns.ignore_tests)
291291

292292
for test_name in self.selected:
293293
abstest = get_abs_module(self.ns, test_name)

Lib/test/libregrtest/runtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def _runtest(ns, test_name):
123123

124124
start_time = time.perf_counter()
125125
try:
126-
support.set_match_tests(ns.match_tests)
126+
support.set_match_tests(ns.match_tests, ns.ignore_tests)
127127
support.junit_xml_list = xml_list = [] if ns.xmlpath else None
128128
if ns.failfast:
129129
support.failfast = True

Lib/test/support/__init__.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,7 +2049,9 @@ def _run_suite(suite):
20492049

20502050
# By default, don't filter tests
20512051
_match_test_func = None
2052-
_match_test_patterns = None
2052+
2053+
_accept_test_patterns = None
2054+
_ignore_test_patterns = None
20532055

20542056

20552057
def match_test(test):
@@ -2070,21 +2072,48 @@ def _is_full_match_test(pattern):
20702072
return ('.' in pattern) and (not re.search(r'[?*\[\]]', pattern))
20712073

20722074

2073-
def set_match_tests(patterns):
2074-
global _match_test_func, _match_test_patterns
2075+
def set_match_tests(accept_patterns, reject_patterns):
2076+
global _match_test_func, _accept_test_patterns, _ignore_test_patterns
20752077

2076-
if patterns == _match_test_patterns:
2077-
# No change: no need to recompile patterns.
2078-
return
20792078

2079+
if accept_patterns is None:
2080+
accept_patterns = ()
2081+
if reject_patterns is None:
2082+
reject_patterns = ()
2083+
2084+
accept_func = reject_func = None
2085+
2086+
if accept_patterns != _accept_test_patterns:
2087+
accept_patterns, accept_func = _update_match_function(accept_patterns)
2088+
if reject_patterns != _ignore_test_patterns:
2089+
reject_patterns, reject_func = _update_match_function(reject_patterns)
2090+
2091+
# Create a copy since patterns can be mutable and so modified later
2092+
_accept_test_patterns = tuple(accept_patterns)
2093+
_ignore_test_patterns = tuple(reject_patterns)
2094+
2095+
if accept_func or reject_func:
2096+
def match_function(test_id):
2097+
accept = True
2098+
reject = False
2099+
if accept_func:
2100+
accept = accept_func(test_id)
2101+
if reject_func:
2102+
reject = reject_func(test_id)
2103+
return accept and not reject
2104+
2105+
_match_test_func = match_function
2106+
2107+
2108+
def _update_match_function(patterns):
20802109
if not patterns:
20812110
func = None
20822111
# set_match_tests(None) behaves as set_match_tests(())
20832112
patterns = ()
20842113
elif all(map(_is_full_match_test, patterns)):
20852114
# Simple case: all patterns are full test identifier.
20862115
# The test.bisect_cmd utility only uses such full test identifiers.
2087-
func = set(patterns).__contains__
2116+
func = lambda elem: elem in set(patterns)
20882117
else:
20892118
regex = '|'.join(map(fnmatch.translate, patterns))
20902119
# The search *is* case sensitive on purpose:
@@ -2104,10 +2133,7 @@ def match_test_regex(test_id):
21042133

21052134
func = match_test_regex
21062135

2107-
# Create a copy since patterns can be mutable and so modified later
2108-
_match_test_patterns = tuple(patterns)
2109-
_match_test_func = func
2110-
2136+
return patterns, func
21112137

21122138

21132139
def run_unittest(*classes):

Lib/test/test_regrtest.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,24 @@ def test_single(self):
155155
self.assertTrue(ns.single)
156156
self.checkError([opt, '-f', 'foo'], "don't go together")
157157

158+
def test_ignore(self):
159+
for opt in '-i', '--ignore':
160+
with self.subTest(opt=opt):
161+
ns = libregrtest._parse_args([opt, 'pattern'])
162+
self.assertEqual(ns.ignore_tests, ['pattern'])
163+
self.checkError([opt], 'expected one argument')
164+
165+
self.addCleanup(support.unlink, support.TESTFN)
166+
with open(support.TESTFN, "w") as fp:
167+
print('matchfile1', file=fp)
168+
print('matchfile2', file=fp)
169+
170+
filename = os.path.abspath(support.TESTFN)
171+
ns = libregrtest._parse_args(['-m', 'match',
172+
'--ignorefile', filename])
173+
self.assertEqual(ns.ignore_tests,
174+
['matchfile1', 'matchfile2'])
175+
158176
def test_match(self):
159177
for opt in '-m', '--match':
160178
with self.subTest(opt=opt):
@@ -961,6 +979,47 @@ def parse_methods(self, output):
961979
regex = re.compile("^(test[^ ]+).*ok$", flags=re.MULTILINE)
962980
return [match.group(1) for match in regex.finditer(output)]
963981

982+
def test_ignorefile(self):
983+
code = textwrap.dedent("""
984+
import unittest
985+
986+
class Tests(unittest.TestCase):
987+
def test_method1(self):
988+
pass
989+
def test_method2(self):
990+
pass
991+
def test_method3(self):
992+
pass
993+
def test_method4(self):
994+
pass
995+
""")
996+
all_methods = ['test_method1', 'test_method2',
997+
'test_method3', 'test_method4']
998+
testname = self.create_test(code=code)
999+
1000+
# by default, all methods should be run
1001+
output = self.run_tests("-v", testname)
1002+
methods = self.parse_methods(output)
1003+
self.assertEqual(methods, all_methods)
1004+
1005+
# only run a subset
1006+
filename = support.TESTFN
1007+
self.addCleanup(support.unlink, filename)
1008+
1009+
subset = [
1010+
# only ignore the method name
1011+
'test_method1',
1012+
# ignore the full identifier
1013+
'%s.Tests.test_method3' % testname]
1014+
with open(filename, "w") as fp:
1015+
for name in subset:
1016+
print(name, file=fp)
1017+
1018+
output = self.run_tests("-v", "--ignorefile", filename, testname)
1019+
methods = self.parse_methods(output)
1020+
subset = ['test_method2', 'test_method4']
1021+
self.assertEqual(methods, subset)
1022+
9641023
def test_matchfile(self):
9651024
code = textwrap.dedent("""
9661025
import unittest

Lib/test/test_support.py

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -527,52 +527,100 @@ def id(self):
527527
test_access = Test('test.test_os.FileTests.test_access')
528528
test_chdir = Test('test.test_os.Win32ErrorTests.test_chdir')
529529

530+
# Test acceptance
530531
with support.swap_attr(support, '_match_test_func', None):
531532
# match all
532-
support.set_match_tests([])
533+
support.set_match_tests([], None)
533534
self.assertTrue(support.match_test(test_access))
534535
self.assertTrue(support.match_test(test_chdir))
535536

536537
# match all using None
537-
support.set_match_tests(None)
538+
support.set_match_tests(None, None)
538539
self.assertTrue(support.match_test(test_access))
539540
self.assertTrue(support.match_test(test_chdir))
540541

541542
# match the full test identifier
542-
support.set_match_tests([test_access.id()])
543+
support.set_match_tests([test_access.id()], None)
543544
self.assertTrue(support.match_test(test_access))
544545
self.assertFalse(support.match_test(test_chdir))
545546

546547
# match the module name
547-
support.set_match_tests(['test_os'])
548+
support.set_match_tests(['test_os'], None)
548549
self.assertTrue(support.match_test(test_access))
549550
self.assertTrue(support.match_test(test_chdir))
550551

551552
# Test '*' pattern
552-
support.set_match_tests(['test_*'])
553+
support.set_match_tests(['test_*'], None)
553554
self.assertTrue(support.match_test(test_access))
554555
self.assertTrue(support.match_test(test_chdir))
555556

556557
# Test case sensitivity
557-
support.set_match_tests(['filetests'])
558+
support.set_match_tests(['filetests'], None)
558559
self.assertFalse(support.match_test(test_access))
559-
support.set_match_tests(['FileTests'])
560+
support.set_match_tests(['FileTests'], None)
560561
self.assertTrue(support.match_test(test_access))
561562

562563
# Test pattern containing '.' and a '*' metacharacter
563-
support.set_match_tests(['*test_os.*.test_*'])
564+
support.set_match_tests(['*test_os.*.test_*'], None)
564565
self.assertTrue(support.match_test(test_access))
565566
self.assertTrue(support.match_test(test_chdir))
566567

567568
# Multiple patterns
568-
support.set_match_tests([test_access.id(), test_chdir.id()])
569+
support.set_match_tests([test_access.id(), test_chdir.id()], None)
569570
self.assertTrue(support.match_test(test_access))
570571
self.assertTrue(support.match_test(test_chdir))
571572

572-
support.set_match_tests(['test_access', 'DONTMATCH'])
573+
support.set_match_tests(['test_access', 'DONTMATCH'], None)
573574
self.assertTrue(support.match_test(test_access))
574575
self.assertFalse(support.match_test(test_chdir))
575576

577+
# Test rejection
578+
with support.swap_attr(support, '_match_test_func', None):
579+
# match all
580+
support.set_match_tests(None, [])
581+
self.assertTrue(support.match_test(test_access))
582+
self.assertTrue(support.match_test(test_chdir))
583+
584+
# match all using None
585+
support.set_match_tests(None, None)
586+
self.assertTrue(support.match_test(test_access))
587+
self.assertTrue(support.match_test(test_chdir))
588+
589+
# match the full test identifier
590+
support.set_match_tests(None, [test_access.id()])
591+
self.assertFalse(support.match_test(test_access))
592+
self.assertTrue(support.match_test(test_chdir))
593+
594+
# match the module name
595+
support.set_match_tests(None, ['test_os'])
596+
self.assertFalse(support.match_test(test_access))
597+
self.assertFalse(support.match_test(test_chdir))
598+
599+
# Test '*' pattern
600+
support.set_match_tests(None, ['test_*'])
601+
self.assertFalse(support.match_test(test_access))
602+
self.assertFalse(support.match_test(test_chdir))
603+
604+
# Test case sensitivity
605+
support.set_match_tests(None, ['filetests'])
606+
self.assertTrue(support.match_test(test_access))
607+
support.set_match_tests(None, ['FileTests'])
608+
self.assertFalse(support.match_test(test_access))
609+
610+
# Test pattern containing '.' and a '*' metacharacter
611+
support.set_match_tests(None, ['*test_os.*.test_*'])
612+
self.assertFalse(support.match_test(test_access))
613+
self.assertFalse(support.match_test(test_chdir))
614+
615+
# Multiple patterns
616+
support.set_match_tests(None, [test_access.id(), test_chdir.id()])
617+
self.assertFalse(support.match_test(test_access))
618+
self.assertFalse(support.match_test(test_chdir))
619+
620+
support.set_match_tests(None, ['test_access', 'DONTMATCH'])
621+
self.assertFalse(support.match_test(test_access))
622+
self.assertTrue(support.match_test(test_chdir))
623+
576624
def test_fd_count(self):
577625
# We cannot test the absolute value of fd_count(): on old Linux
578626
# kernel or glibc versions, os.urandom() keeps a FD open on
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
test.regrtest now can receive a list of test patterns to ignore (using the
2+
-i/--ignore argument) or a file with a list of patterns to ignore (using the
3+
--ignore-file argument). Patch by Pablo Galindo.

0 commit comments

Comments
 (0)