Skip to content

Commit 2499741

Browse files
pablogsalJake Taylor
authored andcommitted
bpo-37957: Allow regrtest to receive a file with test (and subtests) to ignore (pythonGH-16989)
When building Python in some uncommon platforms there are some known tests that will fail. Right now, the test suite has the ability to ignore entire tests using the -x option and to receive a filter file using the --matchfile filter. The problem with the --matchfile option is that it receives a file with patterns to accept and when you want to ignore a couple of tests and subtests, is too cumbersome to lists ALL tests that are not the ones that you want to accept and he problem with -x is that is not easy to ignore just a subtests that fail and the whole test needs to be ignored. For these reasons, add a new option to allow to ignore a list of test and subtests for these situations.
1 parent 207b5c0 commit 2499741

File tree

7 files changed

+169
-24
lines changed

7 files changed

+169
-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: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,7 +2096,9 @@ def _run_suite(suite):
20962096

20972097
# By default, don't filter tests
20982098
_match_test_func = None
2099-
_match_test_patterns = None
2099+
2100+
_accept_test_patterns = None
2101+
_ignore_test_patterns = None
21002102

21012103

21022104
def match_test(test):
@@ -2112,18 +2114,45 @@ def _is_full_match_test(pattern):
21122114
# as a full test identifier.
21132115
# Example: 'test.test_os.FileTests.test_access'.
21142116
#
2115-
# Reject patterns which contain fnmatch patterns: '*', '?', '[...]'
2116-
# or '[!...]'. For example, reject 'test_access*'.
2117+
# ignore patterns which contain fnmatch patterns: '*', '?', '[...]'
2118+
# or '[!...]'. For example, ignore 'test_access*'.
21172119
return ('.' in pattern) and (not re.search(r'[?*\[\]]', pattern))
21182120

21192121

2120-
def set_match_tests(patterns):
2121-
global _match_test_func, _match_test_patterns
2122+
def set_match_tests(accept_patterns=None, ignore_patterns=None):
2123+
global _match_test_func, _accept_test_patterns, _ignore_test_patterns
21222124

2123-
if patterns == _match_test_patterns:
2124-
# No change: no need to recompile patterns.
2125-
return
21262125

2126+
if accept_patterns is None:
2127+
accept_patterns = ()
2128+
if ignore_patterns is None:
2129+
ignore_patterns = ()
2130+
2131+
accept_func = ignore_func = None
2132+
2133+
if accept_patterns != _accept_test_patterns:
2134+
accept_patterns, accept_func = _compile_match_function(accept_patterns)
2135+
if ignore_patterns != _ignore_test_patterns:
2136+
ignore_patterns, ignore_func = _compile_match_function(ignore_patterns)
2137+
2138+
# Create a copy since patterns can be mutable and so modified later
2139+
_accept_test_patterns = tuple(accept_patterns)
2140+
_ignore_test_patterns = tuple(ignore_patterns)
2141+
2142+
if accept_func is not None or ignore_func is not None:
2143+
def match_function(test_id):
2144+
accept = True
2145+
ignore = False
2146+
if accept_func:
2147+
accept = accept_func(test_id)
2148+
if ignore_func:
2149+
ignore = ignore_func(test_id)
2150+
return accept and not ignore
2151+
2152+
_match_test_func = match_function
2153+
2154+
2155+
def _compile_match_function(patterns):
21272156
if not patterns:
21282157
func = None
21292158
# set_match_tests(None) behaves as set_match_tests(())
@@ -2151,10 +2180,7 @@ def match_test_regex(test_id):
21512180

21522181
func = match_test_regex
21532182

2154-
# Create a copy since patterns can be mutable and so modified later
2155-
_match_test_patterns = tuple(patterns)
2156-
_match_test_func = func
2157-
2183+
return patterns, func
21582184

21592185

21602186
def run_unittest(*classes):

Lib/test/test_regrtest.py

Lines changed: 54 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,42 @@ 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+
# only run a subset
1001+
filename = support.TESTFN
1002+
self.addCleanup(support.unlink, filename)
1003+
1004+
subset = [
1005+
# only ignore the method name
1006+
'test_method1',
1007+
# ignore the full identifier
1008+
'%s.Tests.test_method3' % testname]
1009+
with open(filename, "w") as fp:
1010+
for name in subset:
1011+
print(name, file=fp)
1012+
1013+
output = self.run_tests("-v", "--ignorefile", filename, testname)
1014+
methods = self.parse_methods(output)
1015+
subset = ['test_method2', 'test_method4']
1016+
self.assertEqual(methods, subset)
1017+
9641018
def test_matchfile(self):
9651019
code = textwrap.dedent("""
9661020
import unittest

Lib/test/test_support.py

Lines changed: 57 additions & 9 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
532533
support.set_match_tests([])
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(ignore_patterns=[])
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)