Kaydet (Commit) a0ccc54e authored tarafından Victor Stinner's avatar Victor Stinner Kaydeden (comit) GitHub

Synchronize libregrtest from master to 3.6 (#2244)

* bpo-30523: regrtest: Add --list-cases option (#2238)
* bpo-30284: Fix regrtest for out of tree build (#1481)
* bpo-30540: regrtest: add --matchfile option (#1909)
* bpo-30258: regrtest: Fix run_tests_multiprocess() (#1479)
* bpo-30263: regrtest: log system load (#1452)
üst 085a57a8
...@@ -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,...',
...@@ -239,6 +250,9 @@ def _create_parser(): ...@@ -239,6 +250,9 @@ def _create_parser():
group.add_argument('--list-tests', action='store_true', group.add_argument('--list-tests', action='store_true',
help="only write the name of tests that will be run, " help="only write the name of tests that will be run, "
"don't execute them") "don't execute them")
group.add_argument('--list-cases', action='store_true',
help='only write the name of test cases that will be run'
' , don\'t execute them')
group.add_argument('-P', '--pgo', dest='pgo', action='store_true', group.add_argument('-P', '--pgo', dest='pgo', action='store_true',
help='enable Profile Guided Optimization training') help='enable Profile Guided Optimization training')
...@@ -343,10 +357,19 @@ def _parse_args(args, **kwargs): ...@@ -343,10 +357,19 @@ def _parse_args(args, **kwargs):
ns.use_resources.append(r) ns.use_resources.append(r)
if ns.random_seed is not None: if ns.random_seed is not None:
ns.randomize = True ns.randomize = True
if ns.verbose:
ns.header = True
if ns.huntrleaks and ns.verbose3: if ns.huntrleaks and ns.verbose3:
ns.verbose3 = False ns.verbose3 = False
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
...@@ -10,9 +10,10 @@ import sysconfig ...@@ -10,9 +10,10 @@ import sysconfig
import tempfile import tempfile
import textwrap import textwrap
import time import time
import unittest
from test.libregrtest.cmdline import _parse_args from test.libregrtest.cmdline import _parse_args
from test.libregrtest.runtest import ( from test.libregrtest.runtest import (
findtests, runtest, findtests, runtest, get_abs_module,
STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED, STDTESTS, NOTTESTS, PASSED, FAILED, ENV_CHANGED, SKIPPED, RESOURCE_DENIED,
INTERRUPTED, CHILD_ERROR, INTERRUPTED, CHILD_ERROR,
PROGRESS_MIN_TIME, format_test_result) PROGRESS_MIN_TIME, format_test_result)
...@@ -28,7 +29,13 @@ except ImportError: ...@@ -28,7 +29,13 @@ except ImportError:
# to keep the test files in a subfolder. This eases the cleanup of leftover # to keep the test files in a subfolder. This eases the cleanup of leftover
# files using the "make distclean" command. # files using the "make distclean" command.
if sysconfig.is_python_build(): if sysconfig.is_python_build():
TEMPDIR = os.path.join(sysconfig.get_config_var('srcdir'), 'build') TEMPDIR = sysconfig.get_config_var('abs_builddir')
if TEMPDIR is None:
# bpo-30284: On Windows, only srcdir is available. Using abs_builddir
# mostly matters on UNIX when building Python out of the source tree,
# especially when the source tree is read only.
TEMPDIR = sysconfig.get_config_var('srcdir')
TEMPDIR = os.path.join(TEMPDIR, 'build')
else: else:
TEMPDIR = tempfile.gettempdir() TEMPDIR = tempfile.gettempdir()
TEMPDIR = os.path.abspath(TEMPDIR) TEMPDIR = os.path.abspath(TEMPDIR)
...@@ -107,7 +114,7 @@ class Regrtest: ...@@ -107,7 +114,7 @@ class Regrtest:
self.test_times.append((test_time, test)) self.test_times.append((test_time, test))
if ok == PASSED: if ok == PASSED:
self.good.append(test) self.good.append(test)
elif ok == FAILED: elif ok in (FAILED, CHILD_ERROR):
self.bad.append(test) self.bad.append(test)
elif ok == ENV_CHANGED: elif ok == ENV_CHANGED:
self.environment_changed.append(test) self.environment_changed.append(test)
...@@ -116,22 +123,28 @@ class Regrtest: ...@@ -116,22 +123,28 @@ class Regrtest:
elif ok == RESOURCE_DENIED: elif ok == RESOURCE_DENIED:
self.skipped.append(test) self.skipped.append(test)
self.resource_denieds.append(test) self.resource_denieds.append(test)
elif ok != INTERRUPTED:
raise ValueError("invalid test result: %r" % ok)
def display_progress(self, test_index, test): def display_progress(self, test_index, test):
if self.ns.quiet: if self.ns.quiet:
return return
# "[ 51/405/1] test_tcl passed"
line = f"{test_index:{self.test_count_width}}{self.test_count}"
if self.bad and not self.ns.pgo: if self.bad and not self.ns.pgo:
fmt = "{time} [{test_index:{count_width}}{test_count}/{nbad}] {test_name}" line = f"{line}/{len(self.bad)}"
else: line = f"[{line}] {test}"
fmt = "{time} [{test_index:{count_width}}{test_count}] {test_name}"
# add the system load prefix: "load avg: 1.80 "
if hasattr(os, 'getloadavg'):
load_avg_1min = os.getloadavg()[0]
line = f"load avg: {load_avg_1min:.2f} {line}"
# add the timestamp prefix: "0:01:05 "
test_time = time.monotonic() - self.start_time test_time = time.monotonic() - self.start_time
test_time = datetime.timedelta(seconds=int(test_time)) test_time = datetime.timedelta(seconds=int(test_time))
line = fmt.format(count_width=self.test_count_width, line = f"{test_time} {line}"
test_index=test_index,
test_count=self.test_count,
nbad=len(self.bad),
test_name=test,
time=test_time)
print(line, flush=True) print(line, flush=True)
def parse_args(self, kwargs): def parse_args(self, kwargs):
...@@ -179,19 +192,14 @@ class Regrtest: ...@@ -179,19 +192,14 @@ class Regrtest:
self.tests = [] self.tests = []
# regex to match 'test_builtin' in line: # regex to match 'test_builtin' in line:
# '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec' # '0:00:00 [ 4/400] test_builtin -- test_dict took 1 sec'
regex = (r'^(?:[0-9]+:[0-9]+:[0-9]+ *)?' regex = re.compile(r'\btest_[a-zA-Z0-9_]+\b')
r'(?:\[[0-9/ ]+\] *)?'
r'(test_[a-zA-Z0-9_]+)')
regex = re.compile(regex)
with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp: with open(os.path.join(support.SAVEDCWD, self.ns.fromfile)) as fp:
for line in fp: for line in fp:
line = line.split('#', 1)[0]
line = line.strip() line = line.strip()
if line.startswith('#'): match = regex.search(line)
continue if match is not None:
match = regex.match(line) self.tests.append(match.group())
if match is None:
continue
self.tests.append(match.group(1))
removepy(self.tests) removepy(self.tests)
...@@ -241,6 +249,29 @@ class Regrtest: ...@@ -241,6 +249,29 @@ class Regrtest:
for name in self.selected: for name in self.selected:
print(name) print(name)
def _list_cases(self, suite):
for test in suite:
if isinstance(test, unittest.loader._FailedTest):
continue
if isinstance(test, unittest.TestSuite):
self._list_cases(test)
elif isinstance(test, unittest.TestCase):
print(test.id())
def list_cases(self):
for test in self.selected:
abstest = get_abs_module(self.ns, test)
try:
suite = unittest.defaultTestLoader.loadTestsFromName(abstest)
self._list_cases(suite)
except unittest.SkipTest:
self.skipped.append(test)
if self.skipped:
print(file=sys.stderr)
print(count(len(self.skipped), "test"), "skipped:", file=sys.stderr)
printlist(self.skipped, file=sys.stderr)
def rerun_failed_tests(self): def rerun_failed_tests(self):
self.ns.verbose = True self.ns.verbose = True
self.ns.failfast = False self.ns.failfast = False
...@@ -381,23 +412,28 @@ class Regrtest: ...@@ -381,23 +412,28 @@ class Regrtest:
if self.bad: if self.bad:
return return
def display_header(self):
# Print basic platform information
print("==", platform.python_implementation(), *sys.version.split())
print("==", platform.platform(aliased=True),
"%s-endian" % sys.byteorder)
print("== hash algorithm:", sys.hash_info.algorithm,
"64bit" if sys.maxsize > 2**32 else "32bit")
print("== cwd:", os.getcwd())
cpu_count = os.cpu_count()
if cpu_count:
print("== CPU count:", cpu_count)
print("== encodings: locale=%s, FS=%s"
% (locale.getpreferredencoding(False),
sys.getfilesystemencoding()))
print("Testing with flags:", sys.flags)
def run_tests(self): def run_tests(self):
# For a partial run, we do not need to clutter the output. # For a partial run, we do not need to clutter the output.
if (self.ns.verbose if (self.ns.header
or self.ns.header or not(self.ns.pgo or self.ns.quiet or self.ns.single
or not (self.ns.pgo or self.ns.quiet or self.ns.single or self.tests or self.ns.args)):
or self.tests or self.ns.args)): self.display_header()
# Print basic platform information
print("==", platform.python_implementation(), *sys.version.split())
print("== ", platform.platform(aliased=True),
"%s-endian" % sys.byteorder)
print("== ", "hash algorithm:", sys.hash_info.algorithm,
"64bit" if sys.maxsize > 2**32 else "32bit")
print("== cwd:", os.getcwd())
print("== encodings: locale=%s, FS=%s"
% (locale.getpreferredencoding(False),
sys.getfilesystemencoding()))
print("Testing with flags:", sys.flags)
if self.ns.randomize: if self.ns.randomize:
print("Using random seed", self.ns.random_seed) print("Using random seed", self.ns.random_seed)
...@@ -487,6 +523,10 @@ class Regrtest: ...@@ -487,6 +523,10 @@ class Regrtest:
self.list_tests() self.list_tests()
sys.exit(0) sys.exit(0)
if self.ns.list_cases:
self.list_cases()
sys.exit(0)
self.run_tests() self.run_tests()
self.display_result() self.display_result()
...@@ -513,7 +553,7 @@ def count(n, word): ...@@ -513,7 +553,7 @@ def count(n, word):
return "%d %ss" % (n, word) return "%d %ss" % (n, word)
def printlist(x, width=70, indent=4): def printlist(x, width=70, indent=4, file=None):
"""Print the elements of iterable x to stdout. """Print the elements of iterable x to stdout.
Optional arg width (default 70) is the maximum line length. Optional arg width (default 70) is the maximum line length.
...@@ -524,7 +564,8 @@ def printlist(x, width=70, indent=4): ...@@ -524,7 +564,8 @@ def printlist(x, width=70, indent=4):
blanks = ' ' * indent blanks = ' ' * indent
# Print the sorted list: 'x' may be a '--random' list or a set() # Print the sorted list: 'x' may be a '--random' list or a set()
print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width, print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
initial_indent=blanks, subsequent_indent=blanks)) initial_indent=blanks, subsequent_indent=blanks),
file=file)
def main(tests=None, **kwargs): def main(tests=None, **kwargs):
......
...@@ -84,7 +84,7 @@ def dash_R(the_module, test, indirect_test, huntrleaks): ...@@ -84,7 +84,7 @@ def dash_R(the_module, test, indirect_test, huntrleaks):
indirect_test() indirect_test()
alloc_after, rc_after, fd_after = dash_R_cleanup(fs, ps, pic, zdc, alloc_after, rc_after, fd_after = dash_R_cleanup(fs, ps, pic, zdc,
abcs) abcs)
print('.', end='', flush=True) print('.', end='', file=sys.stderr, flush=True)
if i >= nwarmup: if i >= nwarmup:
rc_deltas[i] = rc_after - rc_before rc_deltas[i] = rc_after - rc_before
alloc_deltas[i] = alloc_after - alloc_before alloc_deltas[i] = alloc_after - alloc_before
......
...@@ -71,6 +71,14 @@ def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS): ...@@ -71,6 +71,14 @@ def findtests(testdir=None, stdtests=STDTESTS, nottests=NOTTESTS):
return stdtests + sorted(tests) return stdtests + sorted(tests)
def get_abs_module(ns, test):
if test.startswith('test.') or ns.testdir:
return test
else:
# Always import it from the test package
return 'test.' + test
def runtest(ns, test): def runtest(ns, test):
"""Run a single test. """Run a single test.
...@@ -141,11 +149,7 @@ def runtest_inner(ns, test, display_failure=True): ...@@ -141,11 +149,7 @@ def runtest_inner(ns, test, display_failure=True):
test_time = 0.0 test_time = 0.0
refleak = False # True if the test leaked references. refleak = False # True if the test leaked references.
try: try:
if test.startswith('test.') or ns.testdir: abstest = get_abs_module(ns, test)
abstest = test
else:
# Always import it from the test package
abstest = 'test.' + test
clear_caches() clear_caches()
with saved_test_environment(test, ns.verbose, ns.quiet, pgo=ns.pgo) as environment: with saved_test_environment(test, ns.verbose, ns.quiet, pgo=ns.pgo) as environment:
start_time = time.time() start_time = time.time()
......
...@@ -41,7 +41,7 @@ def run_test_in_subprocess(testname, ns): ...@@ -41,7 +41,7 @@ def run_test_in_subprocess(testname, ns):
slaveargs = json.dumps(slaveargs) slaveargs = json.dumps(slaveargs)
cmd = [sys.executable, *support.args_from_interpreter_flags(), cmd = [sys.executable, *support.args_from_interpreter_flags(),
'-X', 'faulthandler', '-u', # Unbuffered stdout and stderr
'-m', 'test.regrtest', '-m', 'test.regrtest',
'--slaveargs', slaveargs] '--slaveargs', slaveargs]
if ns.pgo: if ns.pgo:
...@@ -124,13 +124,13 @@ class MultiprocessThread(threading.Thread): ...@@ -124,13 +124,13 @@ class MultiprocessThread(threading.Thread):
finally: finally:
self.current_test = None self.current_test = None
stdout, _, result = stdout.strip().rpartition("\n")
if retcode != 0: if retcode != 0:
result = (CHILD_ERROR, "Exit code %s" % retcode) result = (CHILD_ERROR, "Exit code %s" % retcode)
self.output.put((test, stdout.rstrip(), stderr.rstrip(), self.output.put((test, stdout.rstrip(), stderr.rstrip(),
result)) result))
return True return False
stdout, _, result = stdout.strip().rpartition("\n")
if not result: if not result:
self.output.put((None, None, None, None)) self.output.put((None, None, None, None))
return True return True
...@@ -203,6 +203,8 @@ def run_tests_multiprocess(regrtest): ...@@ -203,6 +203,8 @@ def run_tests_multiprocess(regrtest):
and test_time >= PROGRESS_MIN_TIME and test_time >= PROGRESS_MIN_TIME
and not regrtest.ns.pgo): and not regrtest.ns.pgo):
text += ' (%.0f sec)' % test_time text += ' (%.0f sec)' % test_time
elif ok == CHILD_ERROR:
text = '%s (%s)' % (text, test_time)
running = get_running(workers) running = get_running(workers)
if running and not regrtest.ns.pgo: if running and not regrtest.ns.pgo:
text += ' -- running: %s' % ', '.join(running) text += ' -- running: %s' % ', '.join(running)
...@@ -216,9 +218,6 @@ def run_tests_multiprocess(regrtest): ...@@ -216,9 +218,6 @@ def run_tests_multiprocess(regrtest):
if result[0] == INTERRUPTED: if result[0] == INTERRUPTED:
raise KeyboardInterrupt raise KeyboardInterrupt
if result[0] == CHILD_ERROR:
msg = "Child error on {}: {}".format(test, result[1])
raise Exception(msg)
test_index += 1 test_index += 1
except KeyboardInterrupt: except KeyboardInterrupt:
regrtest.interrupted = True regrtest.interrupted = True
......
...@@ -1915,9 +1915,15 @@ def run_unittest(*classes): ...@@ -1915,9 +1915,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)
......
...@@ -118,6 +118,9 @@ class ParseArgsTestCase(unittest.TestCase): ...@@ -118,6 +118,9 @@ class ParseArgsTestCase(unittest.TestCase):
ns = libregrtest._parse_args(['--header']) ns = libregrtest._parse_args(['--header'])
self.assertTrue(ns.header) self.assertTrue(ns.header)
ns = libregrtest._parse_args(['--verbose'])
self.assertTrue(ns.header)
def test_randomize(self): def test_randomize(self):
for opt in '-r', '--randomize': for opt in '-r', '--randomize':
with self.subTest(opt=opt): with self.subTest(opt=opt):
...@@ -156,9 +159,24 @@ class ParseArgsTestCase(unittest.TestCase): ...@@ -156,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):
...@@ -272,7 +290,6 @@ class ParseArgsTestCase(unittest.TestCase): ...@@ -272,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:')
...@@ -354,7 +371,7 @@ class BaseTestCase(unittest.TestCase): ...@@ -354,7 +371,7 @@ class BaseTestCase(unittest.TestCase):
self.assertRegex(output, regex) self.assertRegex(output, regex)
def parse_executed_tests(self, output): def parse_executed_tests(self, output):
regex = (r'^[0-9]+:[0-9]+:[0-9]+ \[ *[0-9]+(?:/ *[0-9]+)?\] (%s)' regex = (r'^[0-9]+:[0-9]+:[0-9]+ (?:load avg: [0-9]+\.[0-9]{2} )?\[ *[0-9]+(?:/ *[0-9]+)*\] (%s)'
% self.TESTNAME_REGEX) % self.TESTNAME_REGEX)
parser = re.finditer(regex, output, re.MULTILINE) parser = re.finditer(regex, output, re.MULTILINE)
return list(match.group(1) for match in parser) return list(match.group(1) for match in parser)
...@@ -454,7 +471,6 @@ class BaseTestCase(unittest.TestCase): ...@@ -454,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)
...@@ -676,6 +692,14 @@ class ArgsTestCase(BaseTestCase): ...@@ -676,6 +692,14 @@ class ArgsTestCase(BaseTestCase):
output = self.run_tests('--fromfile', filename) output = self.run_tests('--fromfile', filename)
self.check_executed_tests(output, tests) self.check_executed_tests(output, tests)
# test format 'Lib/test/test_opcodes.py'
with open(filename, "w") as fp:
for name in tests:
print('Lib/test/%s.py' % name, file=fp)
output = self.run_tests('--fromfile', filename)
self.check_executed_tests(output, tests)
def test_interrupted(self): def test_interrupted(self):
code = TEST_INTERRUPTED code = TEST_INTERRUPTED
test = self.create_test('sigint', code=code) test = self.create_test('sigint', code=code)
...@@ -801,6 +825,79 @@ class ArgsTestCase(BaseTestCase): ...@@ -801,6 +825,79 @@ class ArgsTestCase(BaseTestCase):
self.assertEqual(output.rstrip().splitlines(), self.assertEqual(output.rstrip().splitlines(),
tests) tests)
def test_list_cases(self):
# test --list-cases
code = textwrap.dedent("""
import unittest
class Tests(unittest.TestCase):
def test_method1(self):
pass
def test_method2(self):
pass
""")
testname = self.create_test(code=code)
all_methods = ['%s.Tests.test_method1' % testname,
'%s.Tests.test_method2' % testname]
output = self.run_tests('--list-cases', testname)
self.assertEqual(output.splitlines(), all_methods)
def test_crashed(self):
# Any code which causes a crash
code = 'import faulthandler; faulthandler._sigsegv()'
crash_test = self.create_test(name="crash", code=code)
ok_test = self.create_test(name="ok")
tests = [crash_test, ok_test]
output = self.run_tests("-j2", *tests, exitcode=1)
self.check_executed_tests(output, tests, failed=crash_test,
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):
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