Issue #16799: Switched from getopt to argparse style in regrtest's argument

parsing.  Added more tests for regrtest's argument parsing.
This commit is contained in:
Serhiy Storchaka 2013-08-29 12:26:23 +03:00
parent 48e6a8c88a
commit 64f7c4e4ca
3 changed files with 498 additions and 358 deletions

View File

@ -233,18 +233,20 @@ def _create_parser():
# We add help explicitly to control what argument group it renders under.
group.add_argument('-h', '--help', action='help',
help='show this help message and exit')
group.add_argument('--timeout', metavar='TIMEOUT',
group.add_argument('--timeout', metavar='TIMEOUT', type=float,
help='dump the traceback and exit if a test takes '
'more than TIMEOUT seconds; disabled if TIMEOUT '
'is negative or equals to zero')
group.add_argument('--wait', action='store_true', help='wait for user '
'input, e.g., allow a debugger to be attached')
group.add_argument('--wait', action='store_true',
help='wait for user input, e.g., allow a debugger '
'to be attached')
group.add_argument('--slaveargs', metavar='ARGS')
group.add_argument('-S', '--start', metavar='START', help='the name of '
'the test at which to start.' + more_details)
group.add_argument('-S', '--start', metavar='START',
help='the name of the test at which to start.' +
more_details)
group = parser.add_argument_group('Verbosity')
group.add_argument('-v', '--verbose', action='store_true',
group.add_argument('-v', '--verbose', action='count',
help='run tests in verbose mode with output to stdout')
group.add_argument('-w', '--verbose2', action='store_true',
help='re-run failed tests in verbose mode')
@ -254,7 +256,7 @@ def _create_parser():
help='print traceback for failed tests')
group.add_argument('-q', '--quiet', action='store_true',
help='no output unless one or more tests fail')
group.add_argument('-o', '--slow', action='store_true',
group.add_argument('-o', '--slow', action='store_true', dest='print_slow',
help='print the slowest 10 tests')
group.add_argument('--header', action='store_true',
help='print header with interpreter info')
@ -262,45 +264,60 @@ def _create_parser():
group = parser.add_argument_group('Selecting tests')
group.add_argument('-r', '--randomize', action='store_true',
help='randomize test execution order.' + more_details)
group.add_argument('--randseed', metavar='SEED', help='pass a random seed '
'to reproduce a previous random run')
group.add_argument('-f', '--fromfile', metavar='FILE', help='read names '
'of tests to run from a file.' + more_details)
group.add_argument('--randseed', metavar='SEED',
dest='random_seed', type=int,
help='pass a random seed to reproduce a previous '
'random run')
group.add_argument('-f', '--fromfile', metavar='FILE',
help='read names of tests to run from a file.' +
more_details)
group.add_argument('-x', '--exclude', action='store_true',
help='arguments are tests to *exclude*')
group.add_argument('-s', '--single', action='store_true', help='single '
'step through a set of tests.' + more_details)
group.add_argument('-m', '--match', metavar='PAT', help='match test cases '
'and methods with glob pattern PAT')
group.add_argument('-G', '--failfast', action='store_true', help='fail as '
'soon as a test fails (only with -v or -W)')
group.add_argument('-u', '--use', metavar='RES1,RES2,...', help='specify '
'which special resource intensive tests to run.' +
more_details)
group.add_argument('-M', '--memlimit', metavar='LIMIT', help='run very '
'large memory-consuming tests.' + more_details)
group.add_argument('-s', '--single', action='store_true',
help='single step through a set of tests.' +
more_details)
group.add_argument('-m', '--match', metavar='PAT',
dest='match_tests',
help='match test cases and methods with glob pattern PAT')
group.add_argument('-G', '--failfast', action='store_true',
help='fail as soon as a test fails (only with -v or -W)')
group.add_argument('-u', '--use', metavar='RES1,RES2,...',
action='append', type=resources_list,
help='specify which special resource intensive tests '
'to run.' + more_details)
group.add_argument('-M', '--memlimit', metavar='LIMIT',
help='run very large memory-consuming tests.' +
more_details)
group.add_argument('--testdir', metavar='DIR',
type=relative_filename,
help='execute test files in the specified directory '
'(instead of the Python stdlib test suite)')
group = parser.add_argument_group('Special runs')
group.add_argument('-l', '--findleaks', action='store_true', help='if GC '
'is available detect tests that leak memory')
group.add_argument('-l', '--findleaks', action='store_true',
help='if GC is available detect tests that leak memory')
group.add_argument('-L', '--runleaks', action='store_true',
help='run the leaks(1) command just before exit.' +
more_details)
more_details)
group.add_argument('-R', '--huntrleaks', metavar='RUNCOUNTS',
type=huntrleaks,
help='search for reference leaks (needs debug build, '
'very slow).' + more_details)
group.add_argument('-j', '--multiprocess', metavar='PROCESSES',
dest='use_mp', type=int,
help='run PROCESSES processes at once')
group.add_argument('-T', '--coverage', action='store_true', help='turn on '
'code coverage tracing using the trace module')
group.add_argument('-T', '--coverage', action='store_true',
dest='trace',
help='turn on code coverage tracing using the trace '
'module')
group.add_argument('-D', '--coverdir', metavar='DIR',
type=relative_filename,
help='directory where coverage files are put')
group.add_argument('-N', '--nocoverdir', action='store_true',
group.add_argument('-N', '--nocoverdir',
action='store_const', const=None, dest='coverdir',
help='put coverage files alongside modules')
group.add_argument('-t', '--threshold', metavar='THRESHOLD',
type=int,
help='call gc.set_threshold(THRESHOLD)')
group.add_argument('-n', '--nowindows', action='store_true',
help='suppress error message boxes on Windows')
@ -313,43 +330,103 @@ def _create_parser():
return parser
# TODO: remove this function as described in issue #16799, for example.
# We use this function since regrtest.main() was originally written to use
# getopt for parsing.
def _convert_namespace_to_getopt(ns):
"""Convert an argparse.Namespace object to a getopt-style opts list.
def relative_filename(string):
# CWD is replaced with a temporary dir before calling main(), so we
# join it with the saved CWD so it ends up where the user expects.
return os.path.join(support.SAVEDCWD, string)
The return value of this function mimics the first element of
getopt.getopt()'s (opts, args) return value. In addition, the (option,
value) pairs in the opts list are sorted by option and use the long
option string. The args part of (opts, args) can be mimicked by the
args attribute of the Namespace object we are using in regrtest.
"""
opts = []
args_dict = vars(ns)
for key in sorted(args_dict.keys()):
if key == 'args':
def huntrleaks(string):
args = string.split(':')
if len(args) not in (2, 3):
raise argparse.ArgumentTypeError(
'needs 2 or 3 colon-separated arguments')
nwarmup = int(args[0]) if args[0] else 5
ntracked = int(args[1]) if args[1] else 4
fname = args[2] if len(args) > 2 and args[2] else 'reflog.txt'
return nwarmup, ntracked, fname
def resources_list(string):
u = [x.lower() for x in string.split(',')]
for r in u:
if r == 'all' or r == 'none':
continue
val = args_dict[key]
# Don't continue if val equals '' because this means an option
# accepting a value was provided the empty string. Such values should
# show up in the returned opts list.
if val is None or val is False:
continue
if val is True:
# Then an option with action store_true was passed. getopt
# includes these with value '' in the opts list.
val = ''
opts.append(('--' + key, val))
return opts
if r[0] == '-':
r = r[1:]
if r not in RESOURCE_NAMES:
raise argparse.ArgumentTypeError('invalid resource: ' + r)
return u
def main(tests=None, testdir=None, verbose=0, quiet=False,
def _parse_args(args, **kwargs):
# Defaults
ns = argparse.Namespace(testdir=None, verbose=0, quiet=False,
exclude=False, single=False, randomize=False, fromfile=None,
findleaks=False, use_resources=None, trace=False, coverdir='coverage',
runleaks=False, huntrleaks=False, verbose2=False, print_slow=False,
random_seed=None, use_mp=None, verbose3=False, forever=False,
header=False, failfast=False, match_tests=None):
header=False, failfast=False, match_tests=None)
for k, v in kwargs.items():
if not hasattr(ns, k):
raise TypeError('%r is an invalid keyword argument '
'for this function' % k)
setattr(ns, k, v)
if ns.use_resources is None:
ns.use_resources = []
parser = _create_parser()
parser.parse_args(args=args, namespace=ns)
if ns.single and ns.fromfile:
parser.error("-s and -f don't go together!")
if ns.use_mp and ns.trace:
parser.error("-T and -j don't go together!")
if ns.use_mp and ns.findleaks:
parser.error("-l and -j don't go together!")
if ns.use_mp and ns.memlimit:
parser.error("-M and -j don't go together!")
if ns.failfast and not (ns.verbose or ns.verbose3):
parser.error("-G/--failfast needs either -v or -W")
if ns.quiet:
ns.verbose = 0
if ns.timeout is not None:
if hasattr(faulthandler, 'dump_traceback_later'):
if ns.timeout <= 0:
ns.timeout = None
else:
print("Warning: The timeout option requires "
"faulthandler.dump_traceback_later")
ns.timeout = None
if ns.use_mp is not None:
if ns.use_mp <= 0:
# Use all cores + extras for tests that like to sleep
ns.use_mp = 2 + (os.cpu_count() or 1)
if ns.use_mp == 1:
ns.use_mp = None
if ns.use:
for a in ns.use:
for r in a:
if r == 'all':
ns.use_resources[:] = RESOURCE_NAMES
continue
if r == 'none':
del ns.use_resources[:]
continue
remove = False
if r[0] == '-':
remove = True
r = r[1:]
if remove:
if r in ns.use_resources:
ns.use_resources.remove(r)
elif r not in ns.use_resources:
ns.use_resources.append(r)
if ns.random_seed is not None:
ns.randomize = True
return ns
def main(tests=None, **kwargs):
"""Execute a test suite.
This also parses command-line options and modifies its behavior
@ -372,7 +449,6 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
directly to set the values that would normally be set by flags
on the command line.
"""
# Display the Python traceback on fatal errors (e.g. segfault)
faulthandler.enable(all_threads=True)
@ -389,174 +465,48 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
support.record_original_stdout(sys.stdout)
parser = _create_parser()
ns = parser.parse_args()
opts = _convert_namespace_to_getopt(ns)
args = ns.args
usage = parser.error
ns = _parse_args(sys.argv[1:], **kwargs)
# Defaults
if random_seed is None:
random_seed = random.randrange(10000000)
if use_resources is None:
use_resources = []
debug = False
start = None
timeout = None
for o, a in opts:
if o in ('-v', '--verbose'):
verbose += 1
elif o in ('-w', '--verbose2'):
verbose2 = True
elif o in ('-d', '--debug'):
debug = True
elif o in ('-W', '--verbose3'):
verbose3 = True
elif o in ('-G', '--failfast'):
failfast = True
elif o in ('-q', '--quiet'):
quiet = True;
verbose = 0
elif o in ('-x', '--exclude'):
exclude = True
elif o in ('-S', '--start'):
start = a
elif o in ('-s', '--single'):
single = True
elif o in ('-o', '--slow'):
print_slow = True
elif o in ('-r', '--randomize'):
randomize = True
elif o == '--randseed':
randomize = True
random_seed = int(a)
elif o in ('-f', '--fromfile'):
fromfile = a
elif o in ('-m', '--match'):
match_tests = a
elif o in ('-l', '--findleaks'):
findleaks = True
elif o in ('-L', '--runleaks'):
runleaks = True
elif o in ('-t', '--threshold'):
import gc
gc.set_threshold(int(a))
elif o in ('-T', '--coverage'):
trace = True
elif o in ('-D', '--coverdir'):
# CWD is replaced with a temporary dir before calling main(), so we
# need join it with the saved CWD so it goes where the user expects.
coverdir = os.path.join(support.SAVEDCWD, a)
elif o in ('-N', '--nocoverdir'):
coverdir = None
elif o in ('-R', '--huntrleaks'):
huntrleaks = a.split(':')
if len(huntrleaks) not in (2, 3):
print(a, huntrleaks)
usage('-R takes 2 or 3 colon-separated arguments')
if not huntrleaks[0]:
huntrleaks[0] = 5
else:
huntrleaks[0] = int(huntrleaks[0])
if not huntrleaks[1]:
huntrleaks[1] = 4
else:
huntrleaks[1] = int(huntrleaks[1])
if len(huntrleaks) == 2 or not huntrleaks[2]:
huntrleaks[2:] = ["reflog.txt"]
# Avoid false positives due to various caches
# filling slowly with random data:
warm_caches()
elif o in ('-M', '--memlimit'):
support.set_memlimit(a)
elif o in ('-u', '--use'):
u = [x.lower() for x in a.split(',')]
for r in u:
if r == 'all':
use_resources[:] = RESOURCE_NAMES
continue
if r == 'none':
del use_resources[:]
continue
remove = False
if r[0] == '-':
remove = True
r = r[1:]
if r not in RESOURCE_NAMES:
usage('Invalid -u/--use option: ' + a)
if remove:
if r in use_resources:
use_resources.remove(r)
elif r not in use_resources:
use_resources.append(r)
elif o in ('-n', '--nowindows'):
import msvcrt
msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS|
msvcrt.SEM_NOALIGNMENTFAULTEXCEPT|
msvcrt.SEM_NOGPFAULTERRORBOX|
msvcrt.SEM_NOOPENFILEERRORBOX)
try:
msvcrt.CrtSetReportMode
except AttributeError:
# release build
pass
else:
for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]:
msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE)
msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR)
elif o in ('-F', '--forever'):
forever = True
elif o in ('-j', '--multiprocess'):
use_mp = int(a)
if use_mp <= 0:
# Use all cores + extras for tests that like to sleep
use_mp = 2 + (os.cpu_count() or 1)
if use_mp == 1:
use_mp = None
elif o == '--header':
header = True
elif o == '--slaveargs':
args, kwargs = json.loads(a)
try:
result = runtest(*args, **kwargs)
except KeyboardInterrupt:
result = INTERRUPTED, ''
except BaseException as e:
traceback.print_exc()
result = CHILD_ERROR, str(e)
sys.stdout.flush()
print() # Force a newline (just in case)
print(json.dumps(result))
sys.exit(0)
elif o == '--testdir':
# CWD is replaced with a temporary dir before calling main(), so we
# join it with the saved CWD so it ends up where the user expects.
testdir = os.path.join(support.SAVEDCWD, a)
elif o == '--timeout':
if hasattr(faulthandler, 'dump_traceback_later'):
timeout = float(a)
if timeout <= 0:
timeout = None
else:
print("Warning: The timeout option requires "
"faulthandler.dump_traceback_later")
timeout = None
elif o == '--wait':
input("Press any key to continue...")
if ns.huntrleaks:
# Avoid false positives due to various caches
# filling slowly with random data:
warm_caches()
if ns.memlimit is not None:
support.set_memlimit(ns.memlimit)
if ns.threshold is not None:
import gc
gc.set_threshold(ns.threshold)
if ns.nowindows:
import msvcrt
msvcrt.SetErrorMode(msvcrt.SEM_FAILCRITICALERRORS|
msvcrt.SEM_NOALIGNMENTFAULTEXCEPT|
msvcrt.SEM_NOGPFAULTERRORBOX|
msvcrt.SEM_NOOPENFILEERRORBOX)
try:
msvcrt.CrtSetReportMode
except AttributeError:
# release build
pass
else:
print(("No handler for option {}. Please report this as a bug "
"at http://bugs.python.org.").format(o), file=sys.stderr)
sys.exit(1)
if single and fromfile:
usage("-s and -f don't go together!")
if use_mp and trace:
usage("-T and -j don't go together!")
if use_mp and findleaks:
usage("-l and -j don't go together!")
if use_mp and support.max_memuse:
usage("-M and -j don't go together!")
if failfast and not (verbose or verbose3):
usage("-G/--failfast needs either -v or -W")
for m in [msvcrt.CRT_WARN, msvcrt.CRT_ERROR, msvcrt.CRT_ASSERT]:
msvcrt.CrtSetReportMode(m, msvcrt.CRTDBG_MODE_FILE)
msvcrt.CrtSetReportFile(m, msvcrt.CRTDBG_FILE_STDERR)
if ns.wait:
input("Press any key to continue...")
if ns.slaveargs is not None:
args, kwargs = json.loads(ns.slaveargs)
try:
result = runtest(*args, **kwargs)
except KeyboardInterrupt:
result = INTERRUPTED, ''
except BaseException as e:
traceback.print_exc()
result = CHILD_ERROR, str(e)
sys.stdout.flush()
print() # Force a newline (just in case)
print(json.dumps(result))
sys.exit(0)
good = []
bad = []
@ -565,12 +515,12 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
environment_changed = []
interrupted = False
if findleaks:
if ns.findleaks:
try:
import gc
except ImportError:
print('No GC available, disabling findleaks.')
findleaks = False
ns.findleaks = False
else:
# Uncomment the line below to report garbage that is not
# freeable by reference counting alone. By default only
@ -578,42 +528,40 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
#gc.set_debug(gc.DEBUG_SAVEALL)
found_garbage = []
if single:
if ns.single:
filename = os.path.join(TEMPDIR, 'pynexttest')
try:
fp = open(filename, 'r')
next_test = fp.read().strip()
tests = [next_test]
fp.close()
with open(filename, 'r') as fp:
next_test = fp.read().strip()
tests = [next_test]
except OSError:
pass
if fromfile:
if ns.fromfile:
tests = []
fp = open(os.path.join(support.SAVEDCWD, fromfile))
count_pat = re.compile(r'\[\s*\d+/\s*\d+\]')
for line in fp:
line = count_pat.sub('', line)
guts = line.split() # assuming no test has whitespace in its name
if guts and not guts[0].startswith('#'):
tests.extend(guts)
fp.close()
with open(os.path.join(support.SAVEDCWD, ns.fromfile)) as fp:
count_pat = re.compile(r'\[\s*\d+/\s*\d+\]')
for line in fp:
line = count_pat.sub('', line)
guts = line.split() # assuming no test has whitespace in its name
if guts and not guts[0].startswith('#'):
tests.extend(guts)
# Strip .py extensions.
removepy(args)
removepy(ns.args)
removepy(tests)
stdtests = STDTESTS[:]
nottests = NOTTESTS.copy()
if exclude:
for arg in args:
if ns.exclude:
for arg in ns.args:
if arg in stdtests:
stdtests.remove(arg)
nottests.add(arg)
args = []
ns.args = []
# For a partial run, we do not need to clutter the output.
if verbose or header or not (quiet or single or tests or args):
if ns.verbose or ns.header or not (ns.quiet or ns.single or tests or ns.args):
# Print basic platform information
print("==", platform.python_implementation(), *sys.version.split())
print("== ", platform.platform(aliased=True),
@ -623,37 +571,39 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
# if testdir is set, then we are not running the python tests suite, so
# don't add default tests to be executed or skipped (pass empty values)
if testdir:
alltests = findtests(testdir, list(), set())
if ns.testdir:
alltests = findtests(ns.testdir, list(), set())
else:
alltests = findtests(testdir, stdtests, nottests)
alltests = findtests(ns.testdir, stdtests, nottests)
selected = tests or args or alltests
if single:
selected = tests or ns.args or alltests
if ns.single:
selected = selected[:1]
try:
next_single_test = alltests[alltests.index(selected[0])+1]
except IndexError:
next_single_test = None
# Remove all the selected tests that precede start if it's set.
if start:
if ns.start:
try:
del selected[:selected.index(start)]
del selected[:selected.index(ns.start)]
except ValueError:
print("Couldn't find starting test (%s), using all tests" % start)
if randomize:
random.seed(random_seed)
print("Using random seed", random_seed)
print("Couldn't find starting test (%s), using all tests" % ns.start)
if ns.randomize:
if ns.random_seed is None:
ns.random_seed = random.randrange(10000000)
random.seed(ns.random_seed)
print("Using random seed", ns.random_seed)
random.shuffle(selected)
if trace:
if ns.trace:
import trace, tempfile
tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,
tempfile.gettempdir()],
trace=False, count=True)
test_times = []
support.verbose = verbose # Tell tests to be moderately quiet
support.use_resources = use_resources
support.verbose = ns.verbose # Tell tests to be moderately quiet
support.use_resources = ns.use_resources
save_modules = sys.modules.keys()
def accumulate_result(test, result):
@ -671,7 +621,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
skipped.append(test)
resource_denieds.append(test)
if forever:
if ns.forever:
def test_forever(tests=list(selected)):
while True:
for test in tests:
@ -686,7 +636,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
test_count = '/{}'.format(len(selected))
test_count_width = len(test_count) - 1
if use_mp:
if ns.use_mp:
try:
from threading import Thread
except ImportError:
@ -710,11 +660,12 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
output.put((None, None, None, None))
return
args_tuple = (
(test, verbose, quiet),
dict(huntrleaks=huntrleaks, use_resources=use_resources,
debug=debug, output_on_failure=verbose3,
timeout=timeout, failfast=failfast,
match_tests=match_tests)
(test, ns.verbose, ns.quiet),
dict(huntrleaks=ns.huntrleaks,
use_resources=ns.use_resources,
debug=ns.debug, output_on_failure=ns.verbose3,
timeout=ns.timeout, failfast=ns.failfast,
match_tests=ns.match_tests)
)
# -E is needed by some tests, e.g. test_import
# Running the child from the same working directory ensures
@ -743,19 +694,19 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
except BaseException:
output.put((None, None, None, None))
raise
workers = [Thread(target=work) for i in range(use_mp)]
workers = [Thread(target=work) for i in range(ns.use_mp)]
for worker in workers:
worker.start()
finished = 0
test_index = 1
try:
while finished < use_mp:
while finished < ns.use_mp:
test, stdout, stderr, result = output.get()
if test is None:
finished += 1
continue
accumulate_result(test, result)
if not quiet:
if not ns.quiet:
fmt = "[{1:{0}}{2}/{3}] {4}" if bad else "[{1:{0}}{2}] {4}"
print(fmt.format(
test_count_width, test_index, test_count,
@ -778,29 +729,30 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
worker.join()
else:
for test_index, test in enumerate(tests, 1):
if not quiet:
if not ns.quiet:
fmt = "[{1:{0}}{2}/{3}] {4}" if bad else "[{1:{0}}{2}] {4}"
print(fmt.format(
test_count_width, test_index, test_count, len(bad), test))
sys.stdout.flush()
if trace:
if ns.trace:
# If we're tracing code coverage, then we don't exit with status
# if on a false return value from main.
tracer.runctx('runtest(test, verbose, quiet, timeout=timeout)',
tracer.runctx('runtest(test, ns.verbose, ns.quiet, timeout=ns.timeout)',
globals=globals(), locals=vars())
else:
try:
result = runtest(test, verbose, quiet, huntrleaks, debug,
output_on_failure=verbose3,
timeout=timeout, failfast=failfast,
match_tests=match_tests)
result = runtest(test, ns.verbose, ns.quiet,
ns.huntrleaks, ns.debug,
output_on_failure=ns.verbose3,
timeout=ns.timeout, failfast=ns.failfast,
match_tests=ns.match_tests)
accumulate_result(test, result)
except KeyboardInterrupt:
interrupted = True
break
except:
raise
if findleaks:
if ns.findleaks:
gc.collect()
if gc.garbage:
print("Warning: test created", len(gc.garbage), end=' ')
@ -821,11 +773,11 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
omitted = set(selected) - set(good) - set(bad) - set(skipped)
print(count(len(omitted), "test"), "omitted:")
printlist(omitted)
if good and not quiet:
if good and not ns.quiet:
if not bad and not skipped and not interrupted and len(good) > 1:
print("All", end=' ')
print(count(len(good), "test"), "OK.")
if print_slow:
if ns.print_slow:
test_times.sort(reverse=True)
print("10 slowest tests:")
for time, test in test_times[:10]:
@ -839,18 +791,19 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
print("{} altered the execution environment:".format(
count(len(environment_changed), "test")))
printlist(environment_changed)
if skipped and not quiet:
if skipped and not ns.quiet:
print(count(len(skipped), "test"), "skipped:")
printlist(skipped)
if verbose2 and bad:
if ns.verbose2 and bad:
print("Re-running failed tests in verbose mode")
for test in bad:
print("Re-running test %r in verbose mode" % test)
sys.stdout.flush()
try:
verbose = True
ok = runtest(test, True, quiet, huntrleaks, debug, timeout=timeout)
ns.verbose = True
ok = runtest(test, True, ns.quiet, ns.huntrleaks, ns.debug,
timeout=ns.timeout)
except KeyboardInterrupt:
# print a newline separate from the ^C
print()
@ -858,18 +811,18 @@ def main(tests=None, testdir=None, verbose=0, quiet=False,
except:
raise
if single:
if ns.single:
if next_single_test:
with open(filename, 'w') as fp:
fp.write(next_single_test + '\n')
else:
os.unlink(filename)
if trace:
if ns.trace:
r = tracer.results()
r.write_results(show_missing=True, summary=True, coverdir=coverdir)
r.write_results(show_missing=True, summary=True, coverdir=ns.coverdir)
if runleaks:
if ns.runleaks:
os.system("leaks %d" % os.getpid())
sys.exit(len(bad) > 0 or interrupted)

View File

@ -4,97 +4,281 @@ Tests of regrtest.py.
import argparse
import getopt
import os.path
import unittest
from test import regrtest, support
def old_parse_args(args):
"""Parse arguments as regrtest did strictly prior to 3.4.
Raises getopt.GetoptError on bad arguments.
"""
return getopt.getopt(args, 'hvqxsoS:rf:lu:t:TD:NLR:FdwWM:nj:Gm:',
['help', 'verbose', 'verbose2', 'verbose3', 'quiet',
'exclude', 'single', 'slow', 'randomize', 'fromfile=', 'findleaks',
'use=', 'threshold=', 'coverdir=', 'nocoverdir',
'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=',
'multiprocess=', 'coverage', 'slaveargs=', 'forever', 'debug',
'start=', 'nowindows', 'header', 'testdir=', 'timeout=', 'wait',
'failfast', 'match='])
class ParseArgsTestCase(unittest.TestCase):
"""Test that regrtest's parsing code matches the prior getopt behavior."""
"""Test regrtest's argument parsing."""
def _parse_args(self, args):
# This is the same logic as that used in regrtest.main()
parser = regrtest._create_parser()
ns = parser.parse_args(args=args)
opts = regrtest._convert_namespace_to_getopt(ns)
return opts, ns.args
def checkError(self, args, msg):
with support.captured_stderr() as err, self.assertRaises(SystemExit):
regrtest._parse_args(args)
self.assertIn(msg, err.getvalue())
def _check_args(self, args, expected=None):
"""
The expected parameter is for cases when the behavior of the new
parse_args differs from the old (but deliberately so).
"""
if expected is None:
try:
expected = old_parse_args(args)
except getopt.GetoptError:
# Suppress usage string output when an argparse.ArgumentError
# error is raised.
with support.captured_stderr():
self.assertRaises(SystemExit, self._parse_args, args)
return
# The new parse_args() sorts by long option string.
expected[0].sort()
actual = self._parse_args(args)
self.assertEqual(actual, expected)
def test_help(self):
for opt in '-h', '--help':
with self.subTest(opt=opt):
with support.captured_stdout() as out, \
self.assertRaises(SystemExit):
regrtest._parse_args([opt])
self.assertIn('Run Python regression tests.', out.getvalue())
def test_unrecognized_argument(self):
self._check_args(['--xxx'])
def test_timeout(self):
ns = regrtest._parse_args(['--timeout', '4.2'])
self.assertEqual(ns.timeout, 4.2)
self.checkError(['--timeout'], 'expected one argument')
self.checkError(['--timeout', 'foo'], 'invalid float value')
def test_value_not_provided(self):
self._check_args(['--start'])
def test_wait(self):
ns = regrtest._parse_args(['--wait'])
self.assertTrue(ns.wait)
def test_short_option(self):
# getopt returns the short option whereas argparse returns the long.
expected = ([('--quiet', '')], [])
self._check_args(['-q'], expected=expected)
def test_slaveargs(self):
ns = regrtest._parse_args(['--slaveargs', '[[], {}]'])
self.assertEqual(ns.slaveargs, '[[], {}]')
self.checkError(['--slaveargs'], 'expected one argument')
def test_long_option(self):
self._check_args(['--quiet'])
def test_start(self):
for opt in '-S', '--start':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, 'foo'])
self.assertEqual(ns.start, 'foo')
self.checkError([opt], 'expected one argument')
def test_long_option__partial(self):
self._check_args(['--qui'])
def test_verbose(self):
ns = regrtest._parse_args(['-v'])
self.assertEqual(ns.verbose, 1)
ns = regrtest._parse_args(['-vvv'])
self.assertEqual(ns.verbose, 3)
ns = regrtest._parse_args(['--verbose'])
self.assertEqual(ns.verbose, 1)
ns = regrtest._parse_args(['--verbose'] * 3)
self.assertEqual(ns.verbose, 3)
ns = regrtest._parse_args([])
self.assertEqual(ns.verbose, 0)
def test_two_options(self):
self._check_args(['--quiet', '--exclude'])
def test_verbose2(self):
for opt in '-w', '--verbose2':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.verbose2)
def test_option_with_value(self):
self._check_args(['--start', 'foo'])
def test_verbose3(self):
for opt in '-W', '--verbose3':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.verbose3)
def test_option_with_empty_string_value(self):
self._check_args(['--start', ''])
def test_debug(self):
for opt in '-d', '--debug':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.debug)
def test_arg(self):
self._check_args(['foo'])
def test_quiet(self):
for opt in '-q', '--quiet':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.quiet)
self.assertEqual(ns.verbose, 0)
def test_option_and_arg(self):
self._check_args(['--quiet', 'foo'])
def test_slow(self):
for opt in '-o', '--slow':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.print_slow)
def test_fromfile(self):
self._check_args(['--fromfile', 'file'])
def test_match(self):
self._check_args(['--match', 'pattern'])
def test_header(self):
ns = regrtest._parse_args(['--header'])
self.assertTrue(ns.header)
def test_randomize(self):
self._check_args(['--randomize'])
for opt in '-r', '--randomize':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.randomize)
def test_randseed(self):
ns = regrtest._parse_args(['--randseed', '12345'])
self.assertEqual(ns.random_seed, 12345)
self.assertTrue(ns.randomize)
self.checkError(['--randseed'], 'expected one argument')
self.checkError(['--randseed', 'foo'], 'invalid int value')
def test_fromfile(self):
for opt in '-f', '--fromfile':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, 'foo'])
self.assertEqual(ns.fromfile, 'foo')
self.checkError([opt], 'expected one argument')
self.checkError([opt, 'foo', '-s'], "don't go together")
def test_exclude(self):
for opt in '-x', '--exclude':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.exclude)
def test_single(self):
for opt in '-s', '--single':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.single)
self.checkError([opt, '-f', 'foo'], "don't go together")
def test_match(self):
for opt in '-m', '--match':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, 'pattern'])
self.assertEqual(ns.match_tests, 'pattern')
self.checkError([opt], 'expected one argument')
def test_failfast(self):
for opt in '-G', '--failfast':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, '-v'])
self.assertTrue(ns.failfast)
ns = regrtest._parse_args([opt, '-W'])
self.assertTrue(ns.failfast)
self.checkError([opt], '-G/--failfast needs either -v or -W')
def test_use(self):
for opt in '-u', '--use':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, 'gui,network'])
self.assertEqual(ns.use_resources, ['gui', 'network'])
ns = regrtest._parse_args([opt, 'gui,none,network'])
self.assertEqual(ns.use_resources, ['network'])
expected = list(regrtest.RESOURCE_NAMES)
expected.remove('gui')
ns = regrtest._parse_args([opt, 'all,-gui'])
self.assertEqual(ns.use_resources, expected)
self.checkError([opt], 'expected one argument')
self.checkError([opt, 'foo'], 'invalid resource')
def test_memlimit(self):
for opt in '-M', '--memlimit':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, '4G'])
self.assertEqual(ns.memlimit, '4G')
self.checkError([opt], 'expected one argument')
def test_testdir(self):
ns = regrtest._parse_args(['--testdir', 'foo'])
self.assertEqual(ns.testdir, os.path.join(support.SAVEDCWD, 'foo'))
self.checkError(['--testdir'], 'expected one argument')
def test_findleaks(self):
for opt in '-l', '--findleaks':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.findleaks)
def test_findleaks(self):
for opt in '-L', '--runleaks':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.runleaks)
def test_findleaks(self):
for opt in '-R', '--huntrleaks':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, ':'])
self.assertEqual(ns.huntrleaks, (5, 4, 'reflog.txt'))
ns = regrtest._parse_args([opt, '6:'])
self.assertEqual(ns.huntrleaks, (6, 4, 'reflog.txt'))
ns = regrtest._parse_args([opt, ':3'])
self.assertEqual(ns.huntrleaks, (5, 3, 'reflog.txt'))
ns = regrtest._parse_args([opt, '6:3:leaks.log'])
self.assertEqual(ns.huntrleaks, (6, 3, 'leaks.log'))
self.checkError([opt], 'expected one argument')
self.checkError([opt, '6'],
'needs 2 or 3 colon-separated arguments')
self.checkError([opt, 'foo:'], 'invalid huntrleaks value')
self.checkError([opt, '6:foo'], 'invalid huntrleaks value')
def test_multiprocess(self):
for opt in '-j', '--multiprocess':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, '2'])
self.assertEqual(ns.use_mp, 2)
self.checkError([opt], 'expected one argument')
self.checkError([opt, 'foo'], 'invalid int value')
self.checkError([opt, '2', '-T'], "don't go together")
self.checkError([opt, '2', '-l'], "don't go together")
self.checkError([opt, '2', '-M', '4G'], "don't go together")
def test_findleaks(self):
for opt in '-T', '--coverage':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.trace)
def test_coverdir(self):
for opt in '-D', '--coverdir':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, 'foo'])
self.assertEqual(ns.coverdir,
os.path.join(support.SAVEDCWD, 'foo'))
self.checkError([opt], 'expected one argument')
def test_nocoverdir(self):
for opt in '-N', '--nocoverdir':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertIsNone(ns.coverdir)
def test_threshold(self):
for opt in '-t', '--threshold':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt, '1000'])
self.assertEqual(ns.threshold, 1000)
self.checkError([opt], 'expected one argument')
self.checkError([opt, 'foo'], 'invalid int value')
def test_nowindows(self):
for opt in '-n', '--nowindows':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.nowindows)
def test_forever(self):
for opt in '-F', '--forever':
with self.subTest(opt=opt):
ns = regrtest._parse_args([opt])
self.assertTrue(ns.forever)
def test_main():
support.run_unittest(__name__)
def test_unrecognized_argument(self):
self.checkError(['--xxx'], 'usage:')
def test_long_option__partial(self):
ns = regrtest._parse_args(['--qui'])
self.assertTrue(ns.quiet)
self.assertEqual(ns.verbose, 0)
def test_two_options(self):
ns = regrtest._parse_args(['--quiet', '--exclude'])
self.assertTrue(ns.quiet)
self.assertEqual(ns.verbose, 0)
self.assertTrue(ns.exclude)
def test_option_with_empty_string_value(self):
ns = regrtest._parse_args(['--start', ''])
self.assertEqual(ns.start, '')
def test_arg(self):
ns = regrtest._parse_args(['foo'])
self.assertEqual(ns.args, ['foo'])
def test_option_and_arg(self):
ns = regrtest._parse_args(['--quiet', 'foo'])
self.assertTrue(ns.quiet)
self.assertEqual(ns.verbose, 0)
self.assertEqual(ns.args, ['foo'])
if __name__ == '__main__':
test_main()
unittest.main()

View File

@ -153,6 +153,9 @@ Library
Tests
-----
- Issue #16799: Switched from getopt to argparse style in regrtest's argument
parsing. Added more tests for regrtest's argument parsing.
- Issue #18792: Use "127.0.0.1" or "::1" instead of "localhost" as much as
possible, since "localhost" goes through a DNS lookup under recent Windows
versions.