optparse has been deprecated since Python 3.2. Best to get on the new
boat before the old one sinks.
It looks like argparse formats its usage string differently than
optparse. Meh.
lothiraldan | |
yuja |
hg-reviewers |
optparse has been deprecated since Python 3.2. Best to get on the new
boat before the old one sinks.
It looks like argparse formats its usage string differently than
optparse. Meh.
Automatic diff as part of commit; lint not applicable. |
Automatic diff as part of commit; unit tests not applicable. |
# | # | ||||
# (You could use any subset of the tests: test-s* happens to match | # (You could use any subset of the tests: test-s* happens to match | ||||
# enough that it's worth doing parallel runs, few enough that it | # enough that it's worth doing parallel runs, few enough that it | ||||
# completes fairly quickly, includes both shell and Python scripts, and | # completes fairly quickly, includes both shell and Python scripts, and | ||||
# includes some scripts that run daemon processes.) | # includes some scripts that run daemon processes.) | ||||
from __future__ import absolute_import, print_function | from __future__ import absolute_import, print_function | ||||
import argparse | |||||
import difflib | import difflib | ||||
import distutils.version as version | import distutils.version as version | ||||
import errno | import errno | ||||
import json | import json | ||||
import optparse | |||||
import os | import os | ||||
import random | import random | ||||
import re | import re | ||||
import shutil | import shutil | ||||
import signal | import signal | ||||
import socket | import socket | ||||
import subprocess | import subprocess | ||||
import sys | import sys | ||||
cases.update(l[11:].split()) | cases.update(l[11:].split()) | ||||
except IOError as ex: | except IOError as ex: | ||||
if ex.errno != errno.ENOENT: | if ex.errno != errno.ENOENT: | ||||
raise | raise | ||||
return cases | return cases | ||||
def getparser(): | def getparser(): | ||||
"""Obtain the OptionParser used by the CLI.""" | """Obtain the OptionParser used by the CLI.""" | ||||
parser = optparse.OptionParser("%prog [options] [tests]") | parser = argparse.ArgumentParser(usage='%(prog)s [options] [tests]') | ||||
# keep these sorted | # keep these sorted | ||||
parser.add_option("--blacklist", action="append", | parser.add_argument("--blacklist", action="append", | ||||
help="skip tests listed in the specified blacklist file") | help="skip tests listed in the specified blacklist file") | ||||
parser.add_option("--whitelist", action="append", | parser.add_argument("--whitelist", action="append", | ||||
help="always run tests listed in the specified whitelist file") | help="always run tests listed in the specified whitelist file") | ||||
parser.add_option("--test-list", action="append", | parser.add_argument("--test-list", action="append", | ||||
help="read tests to run from the specified file") | help="read tests to run from the specified file") | ||||
parser.add_option("--changed", type="string", | parser.add_argument("--changed", | ||||
help="run tests that are changed in parent rev or working directory") | help="run tests that are changed in parent rev or working directory") | ||||
parser.add_option("-C", "--annotate", action="store_true", | parser.add_argument("-C", "--annotate", action="store_true", | ||||
help="output files annotated with coverage") | help="output files annotated with coverage") | ||||
parser.add_option("-c", "--cover", action="store_true", | parser.add_argument("-c", "--cover", action="store_true", | ||||
help="print a test coverage report") | help="print a test coverage report") | ||||
parser.add_option("--color", choices=["always", "auto", "never"], | parser.add_argument("--color", choices=["always", "auto", "never"], | ||||
default=os.environ.get('HGRUNTESTSCOLOR', 'auto'), | default=os.environ.get('HGRUNTESTSCOLOR', 'auto'), | ||||
help="colorisation: always|auto|never (default: auto)") | help="colorisation: always|auto|never (default: auto)") | ||||
parser.add_option("-d", "--debug", action="store_true", | parser.add_argument("-d", "--debug", action="store_true", | ||||
help="debug mode: write output of test scripts to console" | help="debug mode: write output of test scripts to console" | ||||
" rather than capturing and diffing it (disables timeout)") | " rather than capturing and diffing it (disables timeout)") | ||||
parser.add_option("-f", "--first", action="store_true", | parser.add_argument("-f", "--first", action="store_true", | ||||
help="exit on the first test failure") | help="exit on the first test failure") | ||||
parser.add_option("-H", "--htmlcov", action="store_true", | parser.add_argument("-H", "--htmlcov", action="store_true", | ||||
help="create an HTML report of the coverage of the files") | help="create an HTML report of the coverage of the files") | ||||
parser.add_option("-i", "--interactive", action="store_true", | parser.add_argument("-i", "--interactive", action="store_true", | ||||
help="prompt to accept changed output") | help="prompt to accept changed output") | ||||
parser.add_option("-j", "--jobs", type="int", | parser.add_argument("-j", "--jobs", type=int, | ||||
help="number of jobs to run in parallel" | help="number of jobs to run in parallel" | ||||
" (default: $%s or %d)" % defaults['jobs']) | " (default: $%s or %d)" % defaults['jobs']) | ||||
parser.add_option("--keep-tmpdir", action="store_true", | parser.add_argument("--keep-tmpdir", action="store_true", | ||||
help="keep temporary directory after running tests") | help="keep temporary directory after running tests") | ||||
parser.add_option("-k", "--keywords", | parser.add_argument("-k", "--keywords", | ||||
help="run tests matching keywords") | help="run tests matching keywords") | ||||
parser.add_option("--list-tests", action="store_true", | parser.add_argument("--list-tests", action="store_true", | ||||
help="list tests instead of running them") | help="list tests instead of running them") | ||||
parser.add_option("-l", "--local", action="store_true", | parser.add_argument("-l", "--local", action="store_true", | ||||
help="shortcut for --with-hg=<testdir>/../hg, " | help="shortcut for --with-hg=<testdir>/../hg, " | ||||
"and --with-chg=<testdir>/../contrib/chg/chg if --chg is set") | "and --with-chg=<testdir>/../contrib/chg/chg if --chg is set") | ||||
parser.add_option("--loop", action="store_true", | parser.add_argument("--loop", action="store_true", | ||||
help="loop tests repeatedly") | help="loop tests repeatedly") | ||||
parser.add_option("--runs-per-test", type="int", dest="runs_per_test", | parser.add_argument("--runs-per-test", type=int, dest="runs_per_test", | ||||
help="run each test N times (default=1)", default=1) | help="run each test N times (default=1)", default=1) | ||||
parser.add_option("-n", "--nodiff", action="store_true", | parser.add_argument("-n", "--nodiff", action="store_true", | ||||
help="skip showing test changes") | help="skip showing test changes") | ||||
parser.add_option("--outputdir", type="string", | parser.add_argument("--outputdir", | ||||
help="directory to write error logs to (default=test directory)") | help="directory to write error logs to (default=test directory)") | ||||
parser.add_option("-p", "--port", type="int", | parser.add_argument("-p", "--port", type=int, | ||||
help="port on which servers should listen" | help="port on which servers should listen" | ||||
" (default: $%s or %d)" % defaults['port']) | " (default: $%s or %d)" % defaults['port']) | ||||
parser.add_option("--compiler", type="string", | parser.add_argument("--compiler", | ||||
help="compiler to build with") | help="compiler to build with") | ||||
parser.add_option("--pure", action="store_true", | parser.add_argument("--pure", action="store_true", | ||||
help="use pure Python code instead of C extensions") | help="use pure Python code instead of C extensions") | ||||
parser.add_option("-R", "--restart", action="store_true", | parser.add_argument("-R", "--restart", action="store_true", | ||||
help="restart at last error") | help="restart at last error") | ||||
parser.add_option("-r", "--retest", action="store_true", | parser.add_argument("-r", "--retest", action="store_true", | ||||
help="retest failed tests") | help="retest failed tests") | ||||
parser.add_option("-S", "--noskips", action="store_true", | parser.add_argument("-S", "--noskips", action="store_true", | ||||
help="don't report skip tests verbosely") | help="don't report skip tests verbosely") | ||||
parser.add_option("--shell", type="string", | parser.add_argument("--shell", | ||||
help="shell to use (default: $%s or %s)" % defaults['shell']) | help="shell to use (default: $%s or %s)" % defaults['shell']) | ||||
parser.add_option("-t", "--timeout", type="int", | parser.add_argument("-t", "--timeout", type=int, | ||||
help="kill errant tests after TIMEOUT seconds" | help="kill errant tests after TIMEOUT seconds" | ||||
" (default: $%s or %d)" % defaults['timeout']) | " (default: $%s or %d)" % defaults['timeout']) | ||||
parser.add_option("--slowtimeout", type="int", | parser.add_argument("--slowtimeout", type=int, | ||||
help="kill errant slow tests after SLOWTIMEOUT seconds" | help="kill errant slow tests after SLOWTIMEOUT seconds" | ||||
" (default: $%s or %d)" % defaults['slowtimeout']) | " (default: $%s or %d)" % defaults['slowtimeout']) | ||||
parser.add_option("--time", action="store_true", | parser.add_argument("--time", action="store_true", | ||||
help="time how long each test takes") | help="time how long each test takes") | ||||
parser.add_option("--json", action="store_true", | parser.add_argument("--json", action="store_true", | ||||
help="store test result data in 'report.json' file") | help="store test result data in 'report.json' file") | ||||
parser.add_option("--tmpdir", type="string", | parser.add_argument("--tmpdir", | ||||
help="run tests in the given temporary directory" | help="run tests in the given temporary directory" | ||||
" (implies --keep-tmpdir)") | " (implies --keep-tmpdir)") | ||||
parser.add_option("-v", "--verbose", action="store_true", | parser.add_argument("-v", "--verbose", action="store_true", | ||||
help="output verbose messages") | help="output verbose messages") | ||||
parser.add_option("--xunit", type="string", | parser.add_argument("--xunit", | ||||
help="record xunit results at specified path") | help="record xunit results at specified path") | ||||
parser.add_option("--view", type="string", | parser.add_argument("--view", | ||||
help="external diff viewer") | help="external diff viewer") | ||||
parser.add_option("--with-hg", type="string", | parser.add_argument("--with-hg", | ||||
metavar="HG", | metavar="HG", | ||||
help="test using specified hg script rather than a " | help="test using specified hg script rather than a " | ||||
"temporary installation") | "temporary installation") | ||||
parser.add_option("--chg", action="store_true", | parser.add_argument("--chg", action="store_true", | ||||
help="install and use chg wrapper in place of hg") | help="install and use chg wrapper in place of hg") | ||||
parser.add_option("--with-chg", metavar="CHG", | parser.add_argument("--with-chg", metavar="CHG", | ||||
help="use specified chg wrapper in place of hg") | help="use specified chg wrapper in place of hg") | ||||
parser.add_option("--ipv6", action="store_true", | parser.add_argument("--ipv6", action="store_true", | ||||
help="prefer IPv6 to IPv4 for network related tests") | help="prefer IPv6 to IPv4 for network related tests") | ||||
parser.add_option("-3", "--py3k-warnings", action="store_true", | parser.add_argument("-3", "--py3k-warnings", action="store_true", | ||||
help="enable Py3k warnings on Python 2.7+") | help="enable Py3k warnings on Python 2.7+") | ||||
# This option should be deleted once test-check-py3-compat.t and other | # This option should be deleted once test-check-py3-compat.t and other | ||||
# Python 3 tests run with Python 3. | # Python 3 tests run with Python 3. | ||||
parser.add_option("--with-python3", metavar="PYTHON3", | parser.add_argument("--with-python3", metavar="PYTHON3", | ||||
help="Python 3 interpreter (if running under Python 2)" | help="Python 3 interpreter (if running under Python 2)" | ||||
" (TEMPORARY)") | " (TEMPORARY)") | ||||
parser.add_option('--extra-config-opt', action="append", | parser.add_argument('--extra-config-opt', action="append", | ||||
help='set the given config opt in the test hgrc') | help='set the given config opt in the test hgrc') | ||||
parser.add_option('--random', action="store_true", | parser.add_argument('--random', action="store_true", | ||||
help='run tests in random order') | help='run tests in random order') | ||||
parser.add_option('--profile-runner', action='store_true', | parser.add_argument('--profile-runner', action='store_true', | ||||
help='run statprof on run-tests') | help='run statprof on run-tests') | ||||
parser.add_option('--allow-slow-tests', action='store_true', | parser.add_argument('--allow-slow-tests', action='store_true', | ||||
help='allow extremely slow tests') | help='allow extremely slow tests') | ||||
parser.add_option('--showchannels', action='store_true', | parser.add_argument('--showchannels', action='store_true', | ||||
help='show scheduling channels') | help='show scheduling channels') | ||||
parser.add_option('--known-good-rev', type="string", | parser.add_argument('--known-good-rev', | ||||
metavar="known_good_rev", | metavar="known_good_rev", | ||||
help=("Automatically bisect any failures using this " | help=("Automatically bisect any failures using this " | ||||
"revision as a known-good revision.")) | "revision as a known-good revision.")) | ||||
parser.add_option('--bisect-repo', type="string", | parser.add_argument('--bisect-repo', | ||||
metavar='bisect_repo', | metavar='bisect_repo', | ||||
help=("Path of a repo to bisect. Use together with " | help=("Path of a repo to bisect. Use together with " | ||||
"--known-good-rev")) | "--known-good-rev")) | ||||
parser.add_argument('tests', metavar='TESTS', nargs='*', | |||||
help='Tests to run') | |||||
for option, (envvar, default) in defaults.items(): | for option, (envvar, default) in defaults.items(): | ||||
defaults[option] = type(default)(os.environ.get(envvar, default)) | defaults[option] = type(default)(os.environ.get(envvar, default)) | ||||
parser.set_defaults(**defaults) | parser.set_defaults(**defaults) | ||||
return parser | return parser | ||||
def parseargs(args, parser): | def parseargs(args, parser): | ||||
"""Parse arguments with our OptionParser and validate results.""" | """Parse arguments with our OptionParser and validate results.""" | ||||
(options, args) = parser.parse_args(args) | options = parser.parse_args(args) | ||||
# jython is always pure | # jython is always pure | ||||
if 'java' in sys.platform or '__pypy__' in sys.modules: | if 'java' in sys.platform or '__pypy__' in sys.modules: | ||||
options.pure = True | options.pure = True | ||||
if options.with_hg: | if options.with_hg: | ||||
options.with_hg = canonpath(_bytespath(options.with_hg)) | options.with_hg = canonpath(_bytespath(options.with_hg)) | ||||
if not (os.path.isfile(options.with_hg) and | if not (os.path.isfile(options.with_hg) and | ||||
if options.whitelist: | if options.whitelist: | ||||
options.whitelisted = parselistfiles(options.whitelist, 'whitelist') | options.whitelisted = parselistfiles(options.whitelist, 'whitelist') | ||||
else: | else: | ||||
options.whitelisted = {} | options.whitelisted = {} | ||||
if options.showchannels: | if options.showchannels: | ||||
options.nodiff = True | options.nodiff = True | ||||
return (options, args) | return options | ||||
def rename(src, dst): | def rename(src, dst): | ||||
"""Like os.rename(), trade atomicity and opened files friendliness | """Like os.rename(), trade atomicity and opened files friendliness | ||||
for existing destination support. | for existing destination support. | ||||
""" | """ | ||||
shutil.copy(src, dst) | shutil.copy(src, dst) | ||||
os.remove(src) | os.remove(src) | ||||
self._portoffset = 0 | self._portoffset = 0 | ||||
self._ports = {} | self._ports = {} | ||||
def run(self, args, parser=None): | def run(self, args, parser=None): | ||||
"""Run the test suite.""" | """Run the test suite.""" | ||||
oldmask = os.umask(0o22) | oldmask = os.umask(0o22) | ||||
try: | try: | ||||
parser = parser or getparser() | parser = parser or getparser() | ||||
options, args = parseargs(args, parser) | options = parseargs(args, parser) | ||||
# positional arguments are paths to test files to run, so | tests = [_bytespath(a) for a in options.tests] | ||||
# we make sure they're all bytestrings | |||||
args = [_bytespath(a) for a in args] | |||||
if options.test_list is not None: | if options.test_list is not None: | ||||
for listfile in options.test_list: | for listfile in options.test_list: | ||||
with open(listfile, 'rb') as f: | with open(listfile, 'rb') as f: | ||||
args.extend(t for t in f.read().splitlines() if t) | tests.extend(t for t in f.read().splitlines() if t) | ||||
self.options = options | self.options = options | ||||
self._checktools() | self._checktools() | ||||
testdescs = self.findtests(args) | testdescs = self.findtests(tests) | ||||
if options.profile_runner: | if options.profile_runner: | ||||
import statprof | import statprof | ||||
statprof.start() | statprof.start() | ||||
result = self._run(testdescs) | result = self._run(testdescs) | ||||
if options.profile_runner: | if options.profile_runner: | ||||
statprof.stop() | statprof.stop() | ||||
statprof.display() | statprof.display() | ||||
return result | return result |
# Ran 0 tests, 0 skipped, 0 failed. | # Ran 0 tests, 0 skipped, 0 failed. | ||||
$ rm hg | $ rm hg | ||||
#endif | #endif | ||||
#if execbit | #if execbit | ||||
$ touch hg | $ touch hg | ||||
$ run-tests.py --with-hg=./hg | $ run-tests.py --with-hg=./hg | ||||
Usage: run-tests.py [options] [tests] | usage: run-tests.py [options] [tests] | ||||
run-tests.py: error: --with-hg must specify an executable hg script | run-tests.py: error: --with-hg must specify an executable hg script | ||||
[2] | [2] | ||||
$ rm hg | $ rm hg | ||||
#endif | #endif | ||||
Features for testing optional lines | Features for testing optional lines | ||||
=================================== | =================================== | ||||
! | ! | ||||
Failed test-bisect-dependent.t: output changed | Failed test-bisect-dependent.t: output changed | ||||
Failed to identify failure point for test-bisect-dependent.t | Failed to identify failure point for test-bisect-dependent.t | ||||
# Ran 1 tests, 0 skipped, 1 failed. | # Ran 1 tests, 0 skipped, 1 failed. | ||||
python hash seed: * (glob) | python hash seed: * (glob) | ||||
[1] | [1] | ||||
$ rt --bisect-repo=../test-bisect test-bisect-dependent.t | $ rt --bisect-repo=../test-bisect test-bisect-dependent.t | ||||
Usage: run-tests.py [options] [tests] | usage: run-tests.py [options] [tests] | ||||
run-tests.py: error: --bisect-repo cannot be used without --known-good-rev | run-tests.py: error: --bisect-repo cannot be used without --known-good-rev | ||||
[2] | [2] | ||||
$ rt --known-good-rev=0 --bisect-repo=../bisect test-bisect-dependent.t | $ rt --known-good-rev=0 --bisect-repo=../bisect test-bisect-dependent.t | ||||
--- $TESTTMP/anothertests/bisect-dependent/test-bisect-dependent.t | --- $TESTTMP/anothertests/bisect-dependent/test-bisect-dependent.t | ||||
+++ $TESTTMP/anothertests/bisect-dependent/test-bisect-dependent.t.err | +++ $TESTTMP/anothertests/bisect-dependent/test-bisect-dependent.t.err | ||||
@@ -1,2 +1,2 @@ | @@ -1,2 +1,2 @@ |