Repository navigation
bpo-37957: Allow regrtest to receive a file with test (and subtests) to ignore #16989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
bd5ba6e
720ba4a
f0c1da4
16cec07
da9763c
e9ee42f
6a27c69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2049,7 +2049,9 @@ def _run_suite(suite): | |
|
|
||
| # By default, don't filter tests | ||
| _match_test_func = None | ||
| _match_test_patterns = None | ||
|
|
||
| _accept_test_patterns = None | ||
| _ignore_test_patterns = None | ||
|
|
||
|
|
||
| def match_test(test): | ||
|
|
@@ -2065,26 +2067,53 @@ def _is_full_match_test(pattern): | |
| # as a full test identifier. | ||
| # Example: 'test.test_os.FileTests.test_access'. | ||
| # | ||
| # Reject patterns which contain fnmatch patterns: '*', '?', '[...]' | ||
| # or '[!...]'. For example, reject 'test_access*'. | ||
| # ignore patterns which contain fnmatch patterns: '*', '?', '[...]' | ||
| # or '[!...]'. For example, ignore 'test_access*'. | ||
| return ('.' in pattern) and (not re.search(r'[?*\[\]]', pattern)) | ||
|
|
||
|
|
||
| def set_match_tests(patterns): | ||
| global _match_test_func, _match_test_patterns | ||
| def set_match_tests(accept_patterns=None, ignore_patterns=None): | ||
| global _match_test_func, _accept_test_patterns, _ignore_test_patterns | ||
|
|
||
| if patterns == _match_test_patterns: | ||
| # No change: no need to recompile patterns. | ||
| return | ||
|
|
||
| if accept_patterns is None: | ||
| accept_patterns = () | ||
| if ignore_patterns is None: | ||
| ignore_patterns = () | ||
|
|
||
| accept_func = ignore_func = None | ||
|
|
||
| if accept_patterns != _accept_test_patterns: | ||
| accept_patterns, accept_func = _compile_match_function(accept_patterns) | ||
| if ignore_patterns != _ignore_test_patterns: | ||
| ignore_patterns, ignore_func = _compile_match_function(ignore_patterns) | ||
|
|
||
| # Create a copy since patterns can be mutable and so modified later | ||
| _accept_test_patterns = tuple(accept_patterns) | ||
| _ignore_test_patterns = tuple(ignore_patterns) | ||
|
|
||
| if accept_func is not None or ignore_func is not None: | ||
| def match_function(test_id): | ||
| accept = True | ||
| ignore = False | ||
| if accept_func: | ||
| accept = accept_func(test_id) | ||
| if ignore_func: | ||
| ignore = ignore_func(test_id) | ||
| return accept and not ignore | ||
|
|
||
| _match_test_func = match_function | ||
|
|
||
|
|
||
| def _compile_match_function(patterns): | ||
| if not patterns: | ||
| func = None | ||
| # set_match_tests(None) behaves as set_match_tests(()) | ||
| patterns = () | ||
| elif all(map(_is_full_match_test, patterns)): | ||
| # Simple case: all patterns are full test identifier. | ||
| # The test.bisect_cmd utility only uses such full test identifiers. | ||
| func = set(patterns).__contains__ | ||
| func = lambda elem: elem in set(patterns) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum, you create a new temporary set object at each call.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, good catch! |
||
| else: | ||
| regex = '|'.join(map(fnmatch.translate, patterns)) | ||
| # The search *is* case sensitive on purpose: | ||
|
|
@@ -2104,10 +2133,7 @@ def match_test_regex(test_id): | |
|
|
||
| func = match_test_regex | ||
|
|
||
| # Create a copy since patterns can be mutable and so modified later | ||
| _match_test_patterns = tuple(patterns) | ||
| _match_test_func = func | ||
|
|
||
| return patterns, func | ||
|
|
||
|
|
||
| def run_unittest(*classes): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -155,6 +155,24 @@ def test_single(self): | |
| self.assertTrue(ns.single) | ||
| self.checkError([opt, '-f', 'foo'], "don't go together") | ||
|
|
||
| def test_ignore(self): | ||
| for opt in '-i', '--ignore': | ||
| with self.subTest(opt=opt): | ||
| ns = libregrtest._parse_args([opt, 'pattern']) | ||
| self.assertEqual(ns.ignore_tests, ['pattern']) | ||
| self.checkError([opt], 'expected one argument') | ||
|
|
||
| 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', | ||
| '--ignorefile', filename]) | ||
| self.assertEqual(ns.ignore_tests, | ||
| ['matchfile1', 'matchfile2']) | ||
|
|
||
| def test_match(self): | ||
| for opt in '-m', '--match': | ||
| with self.subTest(opt=opt): | ||
|
|
@@ -961,6 +979,47 @@ 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_ignorefile(self): | ||
| 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) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that this test is useful, but you can keep it if you prefer :-)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can remove it :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean the whole
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My comment is on these 4 lines:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have removed them :) |
||
|
|
||
| # only run a subset | ||
| filename = support.TESTFN | ||
| self.addCleanup(support.unlink, filename) | ||
|
|
||
| subset = [ | ||
| # only ignore the method name | ||
| 'test_method1', | ||
| # ignore 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", "--ignorefile", filename, testname) | ||
| methods = self.parse_methods(output) | ||
| subset = ['test_method2', 'test_method4'] | ||
| self.assertEqual(methods, subset) | ||
|
|
||
| def test_matchfile(self): | ||
| code = textwrap.dedent(""" | ||
| import unittest | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -527,52 +527,100 @@ def id(self): | |
| test_access = Test('test.test_os.FileTests.test_access') | ||
| test_chdir = Test('test.test_os.Win32ErrorTests.test_chdir') | ||
|
|
||
| # Test acceptance | ||
| with support.swap_attr(support, '_match_test_func', None): | ||
| # match all | ||
| support.set_match_tests([]) | ||
| support.set_match_tests([], None) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might omit None here now. It's up to you. |
||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # match all using None | ||
| support.set_match_tests(None) | ||
| support.set_match_tests(None, None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # match the full test identifier | ||
| support.set_match_tests([test_access.id()]) | ||
| support.set_match_tests([test_access.id()], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertFalse(support.match_test(test_chdir)) | ||
|
|
||
| # match the module name | ||
| support.set_match_tests(['test_os']) | ||
| support.set_match_tests(['test_os'], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # Test '*' pattern | ||
| support.set_match_tests(['test_*']) | ||
| support.set_match_tests(['test_*'], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # Test case sensitivity | ||
| support.set_match_tests(['filetests']) | ||
| support.set_match_tests(['filetests'], None) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| support.set_match_tests(['FileTests']) | ||
| support.set_match_tests(['FileTests'], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
|
|
||
| # Test pattern containing '.' and a '*' metacharacter | ||
| support.set_match_tests(['*test_os.*.test_*']) | ||
| support.set_match_tests(['*test_os.*.test_*'], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # Multiple patterns | ||
| support.set_match_tests([test_access.id(), test_chdir.id()]) | ||
| support.set_match_tests([test_access.id(), test_chdir.id()], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| support.set_match_tests(['test_access', 'DONTMATCH']) | ||
| support.set_match_tests(['test_access', 'DONTMATCH'], None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertFalse(support.match_test(test_chdir)) | ||
|
|
||
| # Test rejection | ||
| with support.swap_attr(support, '_match_test_func', None): | ||
| # match all | ||
| support.set_match_tests(None, []) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might write support.set_match_tests(ignore_patterns=[]) instead. It's up to you. |
||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # match all using None | ||
| support.set_match_tests(None, None) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # match the full test identifier | ||
| support.set_match_tests(None, [test_access.id()]) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| # match the module name | ||
| support.set_match_tests(None, ['test_os']) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| self.assertFalse(support.match_test(test_chdir)) | ||
|
|
||
| # Test '*' pattern | ||
| support.set_match_tests(None, ['test_*']) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| self.assertFalse(support.match_test(test_chdir)) | ||
|
|
||
| # Test case sensitivity | ||
| support.set_match_tests(None, ['filetests']) | ||
| self.assertTrue(support.match_test(test_access)) | ||
| support.set_match_tests(None, ['FileTests']) | ||
| self.assertFalse(support.match_test(test_access)) | ||
|
|
||
| # Test pattern containing '.' and a '*' metacharacter | ||
| support.set_match_tests(None, ['*test_os.*.test_*']) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| self.assertFalse(support.match_test(test_chdir)) | ||
|
|
||
| # Multiple patterns | ||
| support.set_match_tests(None, [test_access.id(), test_chdir.id()]) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| self.assertFalse(support.match_test(test_chdir)) | ||
|
|
||
| support.set_match_tests(None, ['test_access', 'DONTMATCH']) | ||
| self.assertFalse(support.match_test(test_access)) | ||
| self.assertTrue(support.match_test(test_chdir)) | ||
|
|
||
| def test_fd_count(self): | ||
| # We cannot test the absolute value of fd_count(): on old Linux | ||
| # kernel or glibc versions, os.urandom() keeps a FD open on | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| test.regrtest now can receive a list of test patterns to ignore (using the | ||
| -i/--ignore argument) or a file with a list of patterns to ignore (using the | ||
| --ignore-file argument). Patch by Pablo Galindo. |
Uh oh!
There was an error while loading. Please reload this page.