##// END OF EJS Templates
dockerlib: allow non-unique uid and gid of $DBUILDUSER (issue4657)...
dockerlib: allow non-unique uid and gid of $DBUILDUSER (issue4657) There are make targets for building mercurial packages for various distributions using docker. One of the preparation steps before building is to create inside the docker image a user with the same uid/gid as the current user on the host system, so that the resulting files have appropriate ownership/permissions. It's possible to run `make docker-<distro>` as a user with uid or gid that is already present in a vanilla docker container of that distibution. For example, issue4657 is about failing to build fedora packages as a user with uid=999 and gid=999 because these ids are already used in fedora, and groupadd fails. useradd would fail too, if the flow ever got to it (and there was a user with such uid already). A straightforward (maybe too much) way to fix this is to allow non-unique uid and gid for the new user and group that get created inside the image. I'm not sure of the implications of this, but marmoute encouraged me to try and send this patch for stable.

File last commit:

r26612:a38924f7 default
r26888:271a8020 stable
Show More
run-tests.py
2247 lines | 81.2 KiB | text/x-python | PythonLexer
Stephen Darnell
Add a pure python version of run-tests....
r2110 #!/usr/bin/env python
#
# run-tests.py - Run a set of tests on Mercurial
#
# Copyright 2006 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Stephen Darnell
Add a pure python version of run-tests....
r2110
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 # Modifying this script is tricky because it has many modes:
# - serial (default) vs parallel (-jN, N > 1)
# - no coverage (default) vs coverage (-c, -C, -s)
# - temp install (default) vs specific hg script (--with-hg, --local)
# - tests are a mix of shell scripts and Python scripts
#
# If you change this script, it is recommended that you ensure you
# haven't broken it by running it in various modes with a representative
# sample of test scripts. For example:
Dirkjan Ochtman
kill trailing whitespace
r8843 #
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 # 1) serial, no coverage, temp install:
# ./run-tests.py test-s*
# 2) serial, no coverage, local hg:
# ./run-tests.py --local test-s*
# 3) serial, coverage, temp install:
# ./run-tests.py -c test-s*
# 4) serial, coverage, local hg:
# ./run-tests.py -c --local test-s* # unsupported
# 5) parallel, no coverage, temp install:
# ./run-tests.py -j2 test-s*
# 6) parallel, no coverage, local hg:
# ./run-tests.py -j2 --local test-s*
# 7) parallel, coverage, temp install:
# ./run-tests.py -j2 -c test-s* # currently broken
Greg Ward
run-tests: give each child its own tmp dir (issue1911)...
r9899 # 8) parallel, coverage, local install:
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 # ./run-tests.py -j2 -c --local test-s* # unsupported (and broken)
Greg Ward
run-tests: give each child its own tmp dir (issue1911)...
r9899 # 9) parallel, custom tmp dir:
# ./run-tests.py -j2 --tmpdir /tmp/myhgtests
timeless@mozdev.org
run-tests: use $HGTEST_RUN_TESTS_PURE...
r26158 # 10) parallel, pure, tests that call run-tests:
# ./run-tests.py --pure `grep -l run-tests.py *.t`
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 #
# (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
# completes fairly quickly, includes both shell and Python scripts, and
# includes some scripts that run daemon processes.)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 from __future__ import print_function
Dirkjan Ochtman
tests: use external coverage, mandate newer version...
r10648 from distutils import version
Vadim Gelfer
tests: add timeouts, make run-tests.py clean up dead daemon processes...
r2571 import difflib
import errno
import optparse
import os
Nicolas Dumazet
pylint, pyflakes: remove unused or duplicate imports
r10905 import shutil
Martin Geisler
util: always use subprocess
r8280 import subprocess
Vadim Gelfer
tests: add timeouts, make run-tests.py clean up dead daemon processes...
r2571 import signal
Pierre-Yves David
run-test: ensure the test ports are available before launching test...
r24967 import socket
Vadim Gelfer
tests: add timeouts, make run-tests.py clean up dead daemon processes...
r2571 import sys
Stephen Darnell
Add a pure python version of run-tests....
r2110 import tempfile
Vadim Gelfer
tests: add timeouts, make run-tests.py clean up dead daemon processes...
r2571 import time
Pierre-Yves David
test: display used python hash seed...
r18616 import random
Matt Mackall
tests: basic support for unified tests
r11741 import re
Matt Mackall
run-tests: add locking on results struct
r14000 import threading
Patrick Mezard
run-tests: do not duplicate killdaemons() code
r17464 import killdaemons as killmod
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 try:
import Queue as queue
except ImportError:
import queue
Augie Fackler
run-tests: add support for xunit test reports...
r22044 from xml.dom import minidom
Gregory Szorc
run-tests: initial support for running tests with unittest...
r21426 import unittest
Stephen Darnell
Add a pure python version of run-tests....
r2110
Augie Fackler
run-tests: introduce a name for os.environb...
r25037 osenvironb = getattr(os, 'environb', os.environ)
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 try:
Augie Fackler
run-tests: use a try/except ladder instead of looking for a specific version...
r23263 import json
except ImportError:
try:
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 import simplejson as json
Augie Fackler
run-tests: use a try/except ladder instead of looking for a specific version...
r23263 except ImportError:
json = None
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391
Matt Mackall
run-tests: do chdir for tests under a lock for thread safety
r14019 processlock = threading.Lock()
Brendan Cully
run-tests: lock popen wait/poll...
r19413
Augie Fackler
run-tests: insist that if people use Python 3, they use 3.5.x...
r25160 if sys.version_info > (3, 5, 0):
Augie Fackler
run-tests: introduce PYTHON3 boolean constant (issue4668)...
r25157 PYTHON3 = True
Augie Fackler
run-tests: work around the rename of xrange to range
r25033 xrange = range # we use xrange in one place, and we'd rather not use range
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 def _bytespath(p):
return p.encode('utf-8')
Augie Fackler
run-tests: replace open-coded .decode()s on paths with a helper (issue4667)...
r25162
def _strpath(p):
return p.decode('utf-8')
Augie Fackler
run-tests: insist that if people use Python 3, they use 3.5.x...
r25160 elif sys.version_info >= (3, 0, 0):
print('%s is only supported on Python 3.5+ and 2.6-2.7, not %s' %
(sys.argv[0], '.'.join(str(v) for v in sys.version_info[:3])))
sys.exit(70) # EX_SOFTWARE from `man 3 sysexit`
Augie Fackler
run-tests: introduce PYTHON3 boolean constant (issue4668)...
r25157 else:
PYTHON3 = False
Augie Fackler
run-tests: replace open-coded .decode()s on paths with a helper (issue4667)...
r25162
# In python 2.x, path operations are generally done using
# bytestrings by default, so we don't have to do any extra
# fiddling there. We define the wrapper functions anyway just to
# help keep code consistent between platforms.
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 def _bytespath(p):
return p
Augie Fackler
run-tests: work around the rename of xrange to range
r25033
Augie Fackler
run-tests: replace open-coded .decode()s on paths with a helper (issue4667)...
r25162 _strpath = _bytespath
Matt Harbison
run-tests: resurrect the wifexited polyfill (backout 6ab5a1c9ea3c)...
r25177 # For Windows support
wifexited = getattr(os, "WIFEXITED", lambda x: False)
Pierre-Yves David
run-test: ensure the test ports are available before launching test...
r24967 def checkportisavailable(port):
"""return true if a port seems free to bind on localhost"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', port))
s.close()
return True
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except socket.error as exc:
Pierre-Yves David
run-test: ensure the test ports are available before launching test...
r24967 if not exc.errno == errno.EADDRINUSE:
raise
return False
Martin Geisler
util: always use subprocess
r8280 closefds = os.name == 'posix'
Matt Mackall
run-tests: add env dict to isolate test environment
r19262 def Popen4(cmd, wd, timeout, env=None):
Matt Mackall
run-tests: do chdir for tests under a lock for thread safety
r14019 processlock.acquire()
Matt Mackall
run-tests: add env dict to isolate test environment
r19262 p = subprocess.Popen(cmd, shell=True, bufsize=-1, cwd=wd, env=env,
Martin Geisler
util: always use subprocess
r8280 close_fds=closefds,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
Matt Mackall
run-tests: do chdir for tests under a lock for thread safety
r14019 processlock.release()
Martin Geisler
util: always use subprocess
r8280 p.fromchild = p.stdout
p.tochild = p.stdin
p.childerr = p.stderr
Matt Mackall
run-tests: switch timeout handling from alarm to helper thread...
r14001
Patrick Mezard
run-tests: ignore timeout when Popen.terminate is unavailable...
r14337 p.timeout = False
Matt Mackall
run-tests: switch timeout handling from alarm to helper thread...
r14001 if timeout:
def t():
start = time.time()
while time.time() - start < timeout and p.returncode is None:
Matt Mackall
tests: shorten post-test sleeps...
r16346 time.sleep(.1)
Matt Mackall
run-tests: switch timeout handling from alarm to helper thread...
r14001 p.timeout = True
if p.returncode is None:
Thomas Arendsen Hein
run-tests: fallback to SIGTERM if subprocess.Popen does not have terminate()
r14821 terminate(p)
Matt Mackall
run-tests: switch timeout handling from alarm to helper thread...
r14001 threading.Thread(target=t).start()
Martin Geisler
util: always use subprocess
r8280 return p
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 PYTHON = _bytespath(sys.executable.replace('\\', '/'))
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 IMPL_PATH = b'PYTHONPATH'
Ronny Pfannschmidt
tests: adapt the test runner to work with jython
r10758 if 'java' in sys.platform:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 IMPL_PATH = b'JYTHONPATH'
Patrick Mezard
Add hghave utility and run-tests.py support....
r4881
Thomas Arendsen Hein
run-tests.py: Allow environment variables to set jobs/timeout/port.
r6366 defaults = {
'jobs': ('HGTEST_JOBS', 1),
'timeout': ('HGTEST_TIMEOUT', 180),
'port': ('HGTEST_PORT', 20059),
Mads Kiilerich
tests: let run-tests.py default to use 'sh' in $PATH instead of '/bin/sh'...
r15941 'shell': ('HGTEST_SHELL', 'sh'),
Thomas Arendsen Hein
run-tests.py: Allow environment variables to set jobs/timeout/port.
r6366 }
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 def parselistfiles(files, listtype, warn=True):
entries = dict()
for filename in files:
try:
path = os.path.expanduser(os.path.expandvars(filename))
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(path, "rb")
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except IOError as err:
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 if err.errno != errno.ENOENT:
raise
if warn:
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print("warning: no such %s file: %s" % (listtype, filename))
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 continue
for line in f.readlines():
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 line = line.split(b'#', 1)[0].strip()
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 if line:
entries[line] = filename
f.close()
return entries
Gregory Szorc
run-tests: allow option parser to be extended...
r21008 def getparser():
Gregory Szorc
run-tests: add some docstrings
r21383 """Obtain the OptionParser used by the CLI."""
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser = optparse.OptionParser("%prog [options] [tests]")
Matt Mackall
run-tests: sort options
r11039
# keep these sorted
parser.add_option("--blacklist", action="append",
help="skip tests listed in the specified blacklist file")
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 parser.add_option("--whitelist", action="append",
help="always run tests listed in the specified whitelist file")
Mads Kiilerich
tests: add run-tests --changed option for running tests changed in revisions...
r20821 parser.add_option("--changed", type="string",
help="run tests that are changed in parent rev or working directory")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-C", "--annotate", action="store_true",
help="output files annotated with coverage")
parser.add_option("-c", "--cover", action="store_true",
help="print a test coverage report")
Matt Mackall
run-tests: sort options
r11039 parser.add_option("-d", "--debug", action="store_true",
help="debug mode: write output of test scripts to console"
Mads Kiilerich
spelling: fixes from spell checker
r21024 " rather than capturing and diffing it (disables timeout)")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-f", "--first", action="store_true",
help="exit on the first test failure")
Markus Zapke-Gründemann
tests: add htmlcov option
r15859 parser.add_option("-H", "--htmlcov", action="store_true",
help="create an HTML report of the coverage of the files")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-i", "--interactive", action="store_true",
help="prompt to accept changed output")
parser.add_option("-j", "--jobs", type="int",
help="number of jobs to run in parallel"
" (default: $%s or %d)" % defaults['jobs'])
parser.add_option("--keep-tmpdir", action="store_true",
Greg Ward
run-tests: make --tmpdir option more useful....
r9706 help="keep temporary directory after running tests")
Matt Mackall
run-tests: sort options
r11039 parser.add_option("-k", "--keywords",
help="run tests matching keywords")
parser.add_option("-l", "--local", action="store_true",
help="shortcut for --with-hg=<testdir>/../hg")
Matt Mackall
run-tests: add --loop support...
r19283 parser.add_option("--loop", action="store_true",
help="loop tests repeatedly")
Augie Fackler
run-tests: add --runs-per-test flag...
r24329 parser.add_option("--runs-per-test", type="int", dest="runs_per_test",
help="run each test N times (default=1)", default=1)
Matt Mackall
run-tests: sort options
r11039 parser.add_option("-n", "--nodiff", action="store_true",
help="skip showing test changes")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-p", "--port", type="int",
help="port on which servers should listen"
" (default: $%s or %d)" % defaults['port'])
Bryan O'Sullivan
run-tests: add a --compiler option...
r17966 parser.add_option("--compiler", type="string",
help="compiler to build with")
Matt Mackall
run-tests: sort options
r11039 parser.add_option("--pure", action="store_true",
help="use pure Python code instead of C extensions")
parser.add_option("-R", "--restart", action="store_true",
help="restart at last error")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-r", "--retest", action="store_true",
help="retest failed tests")
Matt Mackall
run-tests: add --noskips option
r9580 parser.add_option("-S", "--noskips", action="store_true",
help="don't report skip tests verbosely")
Martin Geisler
run-tests: add --shell command line flag...
r14202 parser.add_option("--shell", type="string",
help="shell to use (default: $%s or %s)" % defaults['shell'])
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-t", "--timeout", type="int",
help="kill errant tests after TIMEOUT seconds"
" (default: $%s or %d)" % defaults['timeout'])
Siddharth Agarwal
run-tests: add --time option to log times for each test...
r17921 parser.add_option("--time", action="store_true",
help="time how long each test takes")
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 parser.add_option("--json", action="store_true",
help="store test result data in 'report.json' file")
Matt Mackall
run-tests: sort options
r11039 parser.add_option("--tmpdir", type="string",
help="run tests in the given temporary directory"
" (implies --keep-tmpdir)")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("-v", "--verbose", action="store_true",
help="output verbose messages")
Augie Fackler
run-tests: add support for xunit test reports...
r22044 parser.add_option("--xunit", type="string",
help="record xunit results at specified path")
Matt Mackall
run-tests: add --view switch to use external diff viewer
r11040 parser.add_option("--view", type="string",
help="external diff viewer")
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.add_option("--with-hg", type="string",
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 metavar="HG",
help="test using specified hg script rather than a "
"temporary installation")
Alejandro Santos
tests: add -3 switch to run-tests.py
r9028 parser.add_option("-3", "--py3k-warnings", action="store_true",
help="enable Py3k warnings on Python 2.6+")
Augie Fackler
run-tests: add flag to provide extra hgrc options for test runs
r14134 parser.add_option('--extra-config-opt', action="append",
help='set the given config opt in the test hgrc')
Mads Kiilerich
run-tests: introduce --random for running tests in random error...
r19057 parser.add_option('--random', action="store_true",
help='run tests in random order')
Augie Fackler
run-tests: add a --profile-runner option...
r25107 parser.add_option('--profile-runner', action='store_true',
help='run statprof on run-tests')
Augie Fackler
run-tests: add support for marking tests as very slow...
r26109 parser.add_option('--allow-slow-tests', action='store_true',
help='allow extremely slow tests')
Stephen Darnell
Add a pure python version of run-tests....
r2110
Martin Geisler
run-tests: use type of default to convert environment variable...
r14201 for option, (envvar, default) in defaults.items():
defaults[option] = type(default)(os.environ.get(envvar, default))
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 parser.set_defaults(**defaults)
Gregory Szorc
run-tests: allow option parser to be extended...
r21008
return parser
def parseargs(args, parser):
Gregory Szorc
run-tests: add some docstrings
r21383 """Parse arguments with our OptionParser and validate results."""
Gregory Szorc
run-tests: Pass arguments into argument parser...
r21006 (options, args) = parser.parse_args(args)
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091
Ronny Pfannschmidt
tests: adapt the test runner to work with jython
r10758 # jython is always pure
Ronny Pfannschmidt
run-tests: force to test pure on pypy as well
r10766 if 'java' in sys.platform or '__pypy__' in sys.modules:
Ronny Pfannschmidt
Fix run-tests.py -jX after 2ed667a9dfcb
r10765 options.pure = True
Ronny Pfannschmidt
tests: adapt the test runner to work with jython
r10758
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 if options.with_hg:
Mads Kiilerich
run-tests: expand user in --with-hg...
r15942 options.with_hg = os.path.expanduser(options.with_hg)
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 if not (os.path.isfile(options.with_hg) and
os.access(options.with_hg, os.X_OK)):
parser.error('--with-hg must specify an executable hg script')
if not os.path.basename(options.with_hg) == 'hg':
Thomas Arendsen Hein
run-tests: print a newline after all warnings
r14359 sys.stderr.write('warning: --with-hg should specify an hg script\n')
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 if options.local:
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 testdir = os.path.dirname(_bytespath(os.path.realpath(sys.argv[0])))
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 hgbin = os.path.join(os.path.dirname(testdir), b'hg')
Mads Kiilerich
tests: don't require 'hg' without extension on windows...
r16538 if os.name != 'nt' and not os.access(hgbin, os.X_OK):
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674 parser.error('--local specified, but %r not found or not executable'
% hgbin)
options.with_hg = hgbin
Markus Zapke-Gründemann
tests: add htmlcov option
r15859 options.anycoverage = options.cover or options.annotate or options.htmlcov
Dirkjan Ochtman
tests: use external coverage, mandate newer version...
r10648 if options.anycoverage:
try:
import coverage
covver = version.StrictVersion(coverage.__version__).version
if covver < (3, 3):
parser.error('coverage options require coverage 3.3 or later')
except ImportError:
parser.error('coverage options now require the coverage package')
Greg Ward
run-tests: reduce global variables set by parse_args().
r8095
Dirkjan Ochtman
tests: use external coverage, mandate newer version...
r10648 if options.anycoverage and options.local:
# this needs some path mangling somewhere, I guess
parser.error("sorry, coverage options do not work when --local "
"is specified")
Greg Ward
run-tests: redefine --with-hg so it takes the 'hg' script to run....
r8674
Gregory Szorc
run-tests: report code coverage from source directory...
r24506 if options.anycoverage and options.with_hg:
parser.error("sorry, coverage options do not work when --with-hg "
"is specified")
Matt Mackall
run-tests: make vlog a proper function
r19250 global verbose
Greg Ward
run-tests: reduce global variables set by parse_args().
r8095 if options.verbose:
Matt Mackall
run-tests: drop options.child and users
r19279 verbose = ''
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091
Nicolas Dumazet
run-tests: expand --tmpdir and create it if needed
r9394 if options.tmpdir:
options.tmpdir = os.path.expanduser(options.tmpdir)
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091 if options.jobs < 1:
Martin Geisler
run-tests: standardize on --foo instead of -f/--foo...
r9408 parser.error('--jobs must be positive')
Greg Ward
run-tests: add "debug" mode: don't capture child output, just show it....
r9707 if options.interactive and options.debug:
parser.error("-i/--interactive and -d/--debug are incompatible")
if options.debug:
if options.timeout != defaults['timeout']:
sys.stderr.write(
'warning: --timeout option ignored with --debug\n')
options.timeout = 0
Alejandro Santos
tests: add -3 switch to run-tests.py
r9028 if options.py3k_warnings:
Augie Fackler
run-tests: prefer PYTHON3 constant to many version_info checks (issue4668)...
r25158 if PYTHON3:
parser.error(
'--py3k-warnings can only be used on Python 2.6 and 2.7')
Nicolas Dumazet
run-tests: add a "--blacklist target" option to skip predefined test lists...
r9959 if options.blacklist:
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 options.blacklist = parselistfiles(options.blacklist, 'blacklist')
if options.whitelist:
Matt Mackall
run-tests: drop options.child and users
r19279 options.whitelisted = parselistfiles(options.whitelist, 'whitelist')
Augie Fackler
run-tests: allow whitelisting tests that should always run...
r14493 else:
options.whitelisted = {}
Greg Ward
run-tests: factor out parse_args(). Clarify use of globals a bit.
r8091
return (options, args)
Bryan O'Sullivan
Allow tests to run in parallel.
r5384
Patrick Mezard
Make run-tests.py --interactive work on Windows
r5800 def rename(src, dst):
"""Like os.rename(), trade atomicity and opened files friendliness
for existing destination support.
"""
shutil.copy(src, dst)
os.remove(src)
Augie Fackler
run-tests: use difflib.diff_bytes on Python 3...
r25045 _unified_diff = difflib.unified_diff
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: use difflib.diff_bytes on Python 3...
r25045 import functools
_unified_diff = functools.partial(difflib.diff_bytes, difflib.unified_diff)
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 def getdiff(expected, output, ref, err):
Simon Heimberg
run-tests: test result shows when a failed test could not start a server...
r21022 servefail = False
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 lines = []
Augie Fackler
run-tests: use difflib.diff_bytes on Python 3...
r25045 for line in _unified_diff(expected, output, ref, err):
if line.startswith(b'+++') or line.startswith(b'---'):
line = line.replace(b'\\', b'/')
if line.endswith(b' \n'):
line = line[:-2] + b'\n'
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 lines.append(line)
Simon Heimberg
run-tests: test result shows when a failed test could not start a server...
r21022 if not servefail and line.startswith(
Augie Fackler
run-tests: use difflib.diff_bytes on Python 3...
r25045 b'+ abort: child process failed to start'):
Simon Heimberg
run-tests: test result shows when a failed test could not start a server...
r21022 servefail = True
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 return servefail, lines
Stephen Darnell
Add a pure python version of run-tests....
r2110
Matt Mackall
run-tests: make vlog a proper function
r19250 verbose = False
def vlog(*msg):
Gregory Szorc
run-tests: avoid duplicate code in vlog()
r21535 """Log only when in verbose mode."""
if verbose is False:
return
return log(*msg)
Matt Mackall
run-tests: make vlog a proper function
r19250
Augie Fackler
run-tests: add support for xunit test reports...
r22044 # Bytes that break XML even in a CDATA block: control characters 0-31
# sans \t, \n and \r
Augie Fackler
run-tests: do cdata escaping using bytes instead of str
r25051 CDATA_EVIL = re.compile(br"[\000-\010\013\014\016-\037]")
Augie Fackler
run-tests: add support for xunit test reports...
r22044
def cdatasafe(data):
"""Make a string safe to include in a CDATA block.
Certain control characters are illegal in a CDATA block, and
there's no way to include a ]]> in a CDATA either. This function
replaces illegal bytes with ? and adds a space between the ]] so
that it won't break the CDATA block.
"""
Augie Fackler
run-tests: do cdata escaping using bytes instead of str
r25051 return CDATA_EVIL.sub(b'?', data).replace(b']]>', b'] ]>')
Augie Fackler
run-tests: add support for xunit test reports...
r22044
Matt Mackall
run-tests: add a log function
r19251 def log(*msg):
Gregory Szorc
run-tests: avoid duplicate code in vlog()
r21535 """Log something to stdout.
Arguments are strings to print.
"""
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
if verbose:
print(verbose, end=' ')
for m in msg:
print(m, end=' ')
print()
sys.stdout.flush()
Matt Mackall
run-tests: add a log function
r19251
Thomas Arendsen Hein
run-tests: fallback to SIGTERM if subprocess.Popen does not have terminate()
r14821 def terminate(proc):
"""Terminate subprocess (with fallback for Python versions < 2.6)"""
vlog('# Terminating process %d' % proc.pid)
try:
Augie Fackler
tests: use getattr instead of hasattr
r14971 getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
Thomas Arendsen Hein
run-tests: fallback to SIGTERM if subprocess.Popen does not have terminate()
r14821 except OSError:
pass
Matt Mackall
run-tests: use env dict to kill daemons
r19263 def killdaemons(pidfile):
return killmod.killdaemons(pidfile, tryhard=False, remove=True,
Patrick Mezard
run-tests: do not duplicate killdaemons() code
r17464 logfn=vlog)
Brendan Cully
run-tests: kill daemons on ^C with -j....
r10336
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 class Test(unittest.TestCase):
Gregory Szorc
run-tests: allow Test.run() to run multiple times...
r21309 """Encapsulates a single, runnable test.
Gregory Szorc
run-tests: refactor temporary directories in Test...
r21497 While this class conforms to the unittest.TestCase API, it differs in that
instances need to be instantiated manually. (Typically, unittest.TestCase
classes are instantiated automatically by scanning modules.)
Gregory Szorc
run-tests: allow Test.run() to run multiple times...
r21309 """
Gregory Szorc
run-tests: create classes for representing tests...
r21296
Gregory Szorc
run-tests: move SKIPPED_STATUS into Test class
r21380 # Status code reserved for skipped tests (used by hghave).
SKIPPED_STATUS = 80
Gregory Szorc
run-tests: remove global abort flag...
r21520 def __init__(self, path, tmpdir, keeptmpdir=False,
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 debug=False,
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 timeout=defaults['timeout'],
Gregory Szorc
run-tests: move py3kwarnings to Test.__init__
r21516 startport=defaults['port'], extraconfigopts=None,
Gregory Szorc
run-tests: move shell to Test.__init__
r21517 py3kwarnings=False, shell=None):
Gregory Szorc
run-tests: pass a full test path into Test.__init__...
r21502 """Create a test from parameters.
path is the full path to the file defining the test.
Gregory Szorc
run-tests: move computation of test paths into Test.__init__
r21338
Gregory Szorc
run-tests: pass temp dir into Test.__init__...
r21504 tmpdir is the main temporary directory to use for this test.
Gregory Szorc
run-tests: pass abort into Test.__init__
r21505
Gregory Szorc
run-tests: factor options.keep_tmpdir into an argument to Test.__init__
r21509 keeptmpdir determines whether to keep the test's temporary directory
after execution. It defaults to removal (False).
Gregory Szorc
run-tests: move debug into an argument to Test.__init__
r21510
debug mode will make the test execute verbosely, with unfiltered
output.
Gregory Szorc
run-tests: move diff options into arguments of Test.__init__
r21511
Gregory Szorc
run-tests: move timeout into Test.__init__
r21513 timeout controls the maximum run time of the test. It is ignored when
debug is True.
Gregory Szorc
run-tests: refactor port number declaration...
r21514
startport controls the starting port number to use for this test. Each
test will reserve 3 port numbers for execution. It is the caller's
responsibility to allocate a non-overlapping port range to Test
instances.
Gregory Szorc
run-tests: move extra config options to Test.__init__
r21515
extraconfigopts is an iterable of extra hgrc config options. Values
must have the form "key=value" (something understood by hgrc). Values
of the form "foo.key=value" will result in "[foo] key=value".
Gregory Szorc
run-tests: move py3kwarnings to Test.__init__
r21516
py3kwarnings enables Py3k warnings.
Gregory Szorc
run-tests: move shell to Test.__init__
r21517
shell is the shell to execute tests in.
Gregory Szorc
run-tests: pass a full test path into Test.__init__...
r21502 """
Augie Fackler
run-tests: be more judicious about bytes vs string on test attrs...
r25039 self.path = path
self.bname = os.path.basename(path)
Augie Fackler
run-tests: replace open-coded .decode()s on paths with a helper (issue4667)...
r25162 self.name = _strpath(self.bname)
Gregory Szorc
run-tests: pass a full test path into Test.__init__...
r21502 self._testdir = os.path.dirname(path)
Augie Fackler
run-tests: be more judicious about bytes vs string on test attrs...
r25039 self.errpath = os.path.join(self._testdir, b'%s.err' % self.bname)
Gregory Szorc
run-tests: rename Test._test to Test.name...
r21435
Augie Fackler
run-tests: be more judicious about bytes vs string on test attrs...
r25039 self._threadtmp = tmpdir
Gregory Szorc
run-tests: factor options.keep_tmpdir into an argument to Test.__init__
r21509 self._keeptmpdir = keeptmpdir
Gregory Szorc
run-tests: move debug into an argument to Test.__init__
r21510 self._debug = debug
Gregory Szorc
run-tests: move timeout into Test.__init__
r21513 self._timeout = timeout
Gregory Szorc
run-tests: refactor port number declaration...
r21514 self._startport = startport
Gregory Szorc
run-tests: move extra config options to Test.__init__
r21515 self._extraconfigopts = extraconfigopts or []
Gregory Szorc
run-tests: move py3kwarnings to Test.__init__
r21516 self._py3kwarnings = py3kwarnings
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 self._shell = _bytespath(shell)
Gregory Szorc
run-tests: remove global abort flag...
r21520
self._aborted = False
Gregory Szorc
run-tests: kill daemons during Test.cleanup()...
r21319 self._daemonpids = []
Gregory Szorc
run-tests: keep track of test execution state in Test...
r21447 self._finished = None
Gregory Szorc
run-tests: store test return code and output in Test instance...
r21449 self._ret = None
self._out = None
Gregory Szorc
run-tests: store skipped state on Test...
r21453 self._skipped = None
Gregory Szorc
run-tests: refactor testtmp...
r21454 self._testtmp = None
Gregory Szorc
run-tests: keep track of test execution state in Test...
r21447
Gregory Szorc
run-tests: capture reference output in Test.__init__...
r21318 # If we're not in --debug mode and reference output file exists,
# check test output against it.
Gregory Szorc
run-tests: move debug into an argument to Test.__init__
r21510 if debug:
Gregory Szorc
run-tests: capture reference output in Test.__init__...
r21318 self._refout = None # to match "out is None"
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 elif os.path.exists(self.refpath):
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(self.refpath, 'rb')
Gregory Szorc
run-tests: capture reference output in Test.__init__...
r21318 self._refout = f.read().splitlines(True)
f.close()
else:
self._refout = []
Pierre-Yves David
run-tests: implement Test._testMethodName...
r24965 # needed to get base class __repr__ running
@property
def _testMethodName(self):
return self.name
Gregory Szorc
run-tests: implement Test.__str__...
r21463 def __str__(self):
return self.name
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 def shortDescription(self):
return self.name
Gregory Szorc
run-tests: support setUp() and tearDown() in TestCase wrapper...
r21446 def setUp(self):
"""Tasks to perform before run()."""
Gregory Szorc
run-tests: keep track of test execution state in Test...
r21447 self._finished = False
Gregory Szorc
run-tests: store test return code and output in Test instance...
r21449 self._ret = None
self._out = None
Gregory Szorc
run-tests: store skipped state on Test...
r21453 self._skipped = None
Gregory Szorc
run-tests: support setUp() and tearDown() in TestCase wrapper...
r21446
Gregory Szorc
run-tests: refactor temporary directories in Test...
r21497 try:
os.mkdir(self._threadtmp)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as e:
Gregory Szorc
run-tests: refactor temporary directories in Test...
r21497 if e.errno != errno.EEXIST:
raise
Gregory Szorc
run-tests: refactor testtmp...
r21454 self._testtmp = os.path.join(self._threadtmp,
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 os.path.basename(self.path))
Gregory Szorc
run-tests: refactor testtmp...
r21454 os.mkdir(self._testtmp)
Gregory Szorc
run-tests: move errpath deletion to setUp()
r21457 # Remove any previous output files.
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 if os.path.exists(self.errpath):
Augie Fackler
run-tests: ignore ENOENT failures when removing old .err results...
r24332 try:
os.remove(self.errpath)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as e:
Augie Fackler
run-tests: ignore ENOENT failures when removing old .err results...
r24332 # We might have raced another test to clean up a .err
# file, so ignore ENOENT when removing a previous .err
# file.
if e.errno != errno.ENOENT:
raise
Gregory Szorc
run-tests: move errpath deletion to setUp()
r21457
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 def run(self, result):
Gregory Szorc
run-tests: add docstrings
r21536 """Run this test and report results against a TestResult instance."""
# This function is extremely similar to unittest.TestCase.run(). Once
# we require Python 2.7 (or at least its version of unittest), this
# function can largely go away.
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 self._result = result
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 result.startTest(self)
try:
try:
self.setUp()
except (KeyboardInterrupt, SystemExit):
Gregory Szorc
run-tests: remove global abort flag...
r21520 self._aborted = True
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 raise
except Exception:
result.addError(self, sys.exc_info())
return
success = False
try:
self.runTest()
except KeyboardInterrupt:
Gregory Szorc
run-tests: remove global abort flag...
r21520 self._aborted = True
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 raise
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except SkipTest as e:
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 result.addSkip(self, str(e))
Augie Fackler
run-tests: fix test result counts with --keyword specified or skips occurring...
r21997 # The base class will have already counted this as a
# test we "ran", but we want to exclude skipped tests
# from those we count towards those run.
result.testsRun -= 1
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except IgnoreTest as e:
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 result.addIgnore(self, str(e))
Augie Fackler
run-tests: fix test result counts with --keyword specified or skips occurring...
r21997 # As with skips, ignores also should be excluded from
# the number of tests executed.
result.testsRun -= 1
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except WarnTest as e:
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 result.addWarn(self, str(e))
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except self.failureException as e:
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 # This differs from unittest in that we don't capture
# the stack trace. This is for historical reasons and
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23139 # this decision could be revisited in the future,
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 # especially for PythonTest instances.
anuraggoel
run-tests: checks behaviour of test on failure while testing...
r21753 if result.addFailure(self, str(e)):
success = True
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 except Exception:
result.addError(self, sys.exc_info())
else:
success = True
try:
self.tearDown()
except (KeyboardInterrupt, SystemExit):
Gregory Szorc
run-tests: remove global abort flag...
r21520 self._aborted = True
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 raise
except Exception:
result.addError(self, sys.exc_info())
success = False
if success:
result.addSuccess(self)
finally:
Gregory Szorc
run-tests: remove global abort flag...
r21520 result.stopTest(self, interrupted=self._aborted)
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488
def runTest(self):
Gregory Szorc
run-tests: add some docstrings
r21383 """Run this test instance.
This will return a tuple describing the result of the test.
"""
Gregory Szorc
run-tests: refactor port number declaration...
r21514 env = self._getenv()
Gregory Szorc
run-tests: kill daemons during Test.cleanup()...
r21319 self._daemonpids.append(env['DAEMON_PIDS'])
Gregory Szorc
run-tests: move createhgrc into Test
r21382 self._createhgrc(env['HGRCPATH'])
Gregory Szorc
run-tests: move createhgrc() call into Test.run()...
r21300
Gregory Szorc
run-tests: rename Test._test to Test.name...
r21435 vlog('# Test', self.name)
Gregory Szorc
run-tests: move logging of test start into Test.run()
r21337
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 ret, out = self._run(env)
Gregory Szorc
run-tests: don't trap exceptions in Test.runTest()...
r21500 self._finished = True
self._ret = ret
self._out = out
Gregory Szorc
run-tests: capture execution results in a TestResult class...
r21305
Gregory Szorc
run-tests: move output difference processing to Test.run()
r21326 def describe(ret):
if ret < 0:
return 'killed by signal: %d' % -ret
return 'returned error code %d' % ret
Gregory Szorc
run-tests: store skipped state on Test...
r21453 self._skipped = False
Gregory Szorc
run-tests: remove remaining uses of TestResult
r21336
Gregory Szorc
run-tests: move SKIPPED_STATUS into Test class
r21380 if ret == self.SKIPPED_STATUS:
Gregory Szorc
run-tests: add skip processing to Test
r21324 if out is None: # Debug mode, nothing to parse.
missing = ['unknown']
failed = None
else:
Gregory Szorc
run-tests: move parsehghaveoutput() into TTest...
r21379 missing, failed = TTest.parsehghaveoutput(out)
Gregory Szorc
run-tests: add skip processing to Test
r21324
if not missing:
Mads Kiilerich
run-tests: report skipped tests as "skipped" - they might still be "relevant"
r22292 missing = ['skipped']
Gregory Szorc
run-tests: add skip processing to Test
r21324
if failed:
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 self.fail('hg have failed checking for %s' % failed[-1])
Gregory Szorc
run-tests: add skip processing to Test
r21324 else:
Gregory Szorc
run-tests: store skipped state on Test...
r21453 self._skipped = True
Gregory Szorc
run-tests: replace Test.skip() with raise SkipTest...
r21490 raise SkipTest(missing[-1])
Gregory Szorc
run-tests: generate timeout result in Test.run()
r21325 elif ret == 'timeout':
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 self.fail('timed out')
Gregory Szorc
run-tests: raise WarnTest outside of Test.fail()
r21522 elif ret is False:
raise WarnTest('no result code from test')
Gregory Szorc
run-tests: move output difference processing to Test.run()
r21326 elif out != self._refout:
Gregory Szorc
run-tests: write .err files earlier...
r21614 # Diff generation may rely on written .err file.
if (ret != 0 or out != self._refout) and not self._skipped \
and not self._debug:
f = open(self.errpath, 'wb')
for line in out:
f.write(line)
f.close()
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 # The result object handles diff calculation for us.
Matt Mackall
run-tests: hold iolock across diff/prompt when interactive...
r21763 if self._result.addOutputMismatch(self, ret, out, self._refout):
# change was accepted, skip failing
return
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521
Gregory Szorc
run-tests: move output difference processing to Test.run()
r21326 if ret:
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 msg = 'output changed and ' + describe(ret)
Gregory Szorc
run-tests: move output difference processing to Test.run()
r21326 else:
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 msg = 'output changed'
Gregory Szorc
run-tests: move output difference processing to Test.run()
r21326
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 self.fail(msg)
Gregory Szorc
run-tests: move remaining result processing to Test.run()
r21327 elif ret:
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 self.fail(describe(ret))
Gregory Szorc
run-tests: add skip processing to Test
r21324
Gregory Szorc
run-tests: support setUp() and tearDown() in TestCase wrapper...
r21446 def tearDown(self):
"""Tasks to perform after run()."""
Gregory Szorc
run-tests: kill daemons during tearDown()
r21456 for entry in self._daemonpids:
killdaemons(entry)
self._daemonpids = []
timeless@mozdev.org
run-tests: report paths saved by --keep-tmpdir
r26422 if self._keeptmpdir:
log('\nKeeping testtmp dir: %s\nKeeping threadtmp dir: %s' %
(self._testtmp, self._threadtmp))
else:
Gregory Szorc
run-tests: ignore failures from rmtree...
r21461 shutil.rmtree(self._testtmp, True)
Gregory Szorc
run-tests: refactor temporary directories in Test...
r21497 shutil.rmtree(self._threadtmp, True)
Gregory Szorc
run-tests: refactor testtmp...
r21454
Gregory Szorc
run-tests: move err file writing to tearDown()
r21455 if (self._ret != 0 or self._out != self._refout) and not self._skipped \
Gregory Szorc
run-tests: move debug into an argument to Test.__init__
r21510 and not self._debug and self._out:
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 f = open(self.errpath, 'wb')
Gregory Szorc
run-tests: move err file writing to tearDown()
r21455 for line in self._out:
f.write(line)
f.close()
Pierre-Yves David
run-test: include test name in the return vlog...
r24926 vlog("# Ret was:", self._ret, '(%s)' % self.name)
Gregory Szorc
run-tests: move some functionality to Test.tearDown()
r21452
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 def _run(self, env):
Gregory Szorc
run-tests: refactor runone() into gettest() and scheduletests()...
r21339 # This should be implemented in child classes to run tests.
Gregory Szorc
run-tests: replace Test.skip() with raise SkipTest...
r21490 raise SkipTest('unknown test type')
Gregory Szorc
run-tests: create classes for representing tests...
r21296
Gregory Szorc
run-tests: remove global abort flag...
r21520 def abort(self):
"""Terminate execution of this test."""
self._aborted = True
Gregory Szorc
run-tests: refactor testtmp...
r21454 def _getreplacements(self):
Gregory Szorc
run-tests: add docstrings
r21536 """Obtain a mapping of text replacements to apply to test output.
Test output needs to be normalized so it can be compared to expected
output. This function defines how some of that normalization will
occur.
"""
Gregory Szorc
run-tests: move replacements generation into Test...
r21298 r = [
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 (br':%d\b' % self._startport, b':$HGPORT'),
(br':%d\b' % (self._startport + 1), b':$HGPORT1'),
(br':%d\b' % (self._startport + 2), b':$HGPORT2'),
(br'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$',
br'\1 (glob)'),
Gregory Szorc
run-tests: move replacements generation into Test...
r21298 ]
if os.name == 'nt':
r.append(
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 (b''.join(c.isalpha() and b'[%s%s]' % (c.lower(), c.upper()) or
c in b'/\\' and br'[/\\]' or c.isdigit() and c or b'\\' + c
for c in self._testtmp), b'$TESTTMP'))
Gregory Szorc
run-tests: move replacements generation into Test...
r21298 else:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 r.append((re.escape(self._testtmp), b'$TESTTMP'))
Gregory Szorc
run-tests: move replacements generation into Test...
r21298
Gregory Szorc
run-tests: refactor port number declaration...
r21514 return r
Gregory Szorc
run-tests: move replacements generation into Test...
r21298
Gregory Szorc
run-tests: refactor port number declaration...
r21514 def _getenv(self):
Gregory Szorc
run-tests: add docstrings
r21536 """Obtain environment variables to use during test execution."""
Gregory Szorc
run-tests: move createenv() into Test...
r21299 env = os.environ.copy()
Gregory Szorc
run-tests: refactor testtmp...
r21454 env['TESTTMP'] = self._testtmp
env['HOME'] = self._testtmp
Gregory Szorc
run-tests: refactor port number declaration...
r21514 env["HGPORT"] = str(self._startport)
env["HGPORT1"] = str(self._startport + 1)
env["HGPORT2"] = str(self._startport + 2)
Augie Fackler
run-tests: use bytes explicitly for tmpdir and hgrc construction...
r25034 env["HGRCPATH"] = os.path.join(self._threadtmp, b'.hgrc')
env["DAEMON_PIDS"] = os.path.join(self._threadtmp, b'daemon.pids')
Matt Harbison
run-tests: include quotes in the HGEDITOR value when storing sys.executable...
r23347 env["HGEDITOR"] = ('"' + sys.executable + '"'
+ ' -c "import sys; sys.exit(0)"')
Gregory Szorc
run-tests: move createenv() into Test...
r21299 env["HGMERGE"] = "internal:merge"
env["HGUSER"] = "test"
env["HGENCODING"] = "ascii"
env["HGENCODINGMODE"] = "strict"
# Reset some environment variables to well-known values so that
# the tests produce repeatable output.
env['LANG'] = env['LC_ALL'] = env['LANGUAGE'] = 'C'
env['TZ'] = 'GMT'
env["EMAIL"] = "Foo Bar <foo.bar@example.com>"
env['COLUMNS'] = '80'
env['TERM'] = 'xterm'
for k in ('HG HGPROF CDPATH GREP_OPTIONS http_proxy no_proxy ' +
'NO_PROXY').split():
if k in env:
del env[k]
# unset env related to hooks
for k in env.keys():
if k.startswith('HG_'):
del env[k]
return env
Gregory Szorc
run-tests: move createhgrc into Test
r21382 def _createhgrc(self, path):
Gregory Szorc
run-tests: add docstrings
r21536 """Create an hgrc file for this test."""
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 hgrc = open(path, 'wb')
Augie Fackler
run-tests: use bytes explicitly for tmpdir and hgrc construction...
r25034 hgrc.write(b'[ui]\n')
hgrc.write(b'slash = True\n')
hgrc.write(b'interactive = False\n')
hgrc.write(b'mergemarkers = detailed\n')
hgrc.write(b'promptecho = True\n')
hgrc.write(b'[defaults]\n')
hgrc.write(b'backout = -d "0 0"\n')
hgrc.write(b'commit = -d "0 0"\n')
hgrc.write(b'shelve = --date "0 0"\n')
hgrc.write(b'tag = -d "0 0"\n')
hgrc.write(b'[devel]\n')
Pierre-Yves David
devel: rename 'all' to 'all-warnings' (BC)...
r25290 hgrc.write(b'all-warnings = true\n')
Augie Fackler
run-tests: use bytes explicitly for tmpdir and hgrc construction...
r25034 hgrc.write(b'[largefiles]\n')
hgrc.write(b'usercache = %s\n' %
(os.path.join(self._testtmp, b'.cache/largefiles')))
Matt Harbison
run-tests: set a default largefiles usercache in the default hgrc file...
r23388
Gregory Szorc
run-tests: move extra config options to Test.__init__
r21515 for opt in self._extraconfigopts:
section, key = opt.split('.', 1)
assert '=' in key, ('extra config opt %s must '
'have an = for assignment' % opt)
Augie Fackler
run-tests: use bytes explicitly for tmpdir and hgrc construction...
r25034 hgrc.write(b'[%s]\n%s\n' % (section, key))
Gregory Szorc
run-tests: move createhgrc into Test
r21382 hgrc.close()
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 def fail(self, msg):
Gregory Szorc
run-tests: raise WarnTest outside of Test.fail()
r21522 # unittest differentiates between errored and failed.
# Failed is denoted by AssertionError (by default at least).
raise AssertionError(msg)
Gregory Szorc
run-tests: move fail() into Test...
r21323
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 def _runcommand(self, cmd, env, normalizenewlines=False):
Gregory Szorc
run-tests: move run into Test class...
r24508 """Run command in a sub-process, capturing the output (stdout and
stderr).
Return a tuple (exitcode, output). output is None in debug mode.
"""
Gregory Szorc
run-tests: remove arguments from Test._runcommand...
r24509 if self._debug:
proc = subprocess.Popen(cmd, shell=True, cwd=self._testtmp,
env=env)
Gregory Szorc
run-tests: move run into Test class...
r24508 ret = proc.wait()
return (ret, None)
Gregory Szorc
run-tests: remove arguments from Test._runcommand...
r24509 proc = Popen4(cmd, self._testtmp, self._timeout, env)
Gregory Szorc
run-tests: move run into Test class...
r24508 def cleanup():
terminate(proc)
ret = proc.wait()
if ret == 0:
ret = signal.SIGTERM << 8
killdaemons(env['DAEMON_PIDS'])
return ret
output = ''
proc.tochild.close()
try:
output = proc.fromchild.read()
except KeyboardInterrupt:
vlog('# Handling keyboard interrupt')
cleanup()
raise
ret = proc.wait()
Matt Harbison
run-tests: resurrect the wifexited polyfill (backout 6ab5a1c9ea3c)...
r25177 if wifexited(ret):
Gregory Szorc
run-tests: move run into Test class...
r24508 ret = os.WEXITSTATUS(ret)
if proc.timeout:
ret = 'timeout'
if ret:
killdaemons(env['DAEMON_PIDS'])
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 for s, r in self._getreplacements():
Gregory Szorc
run-tests: move run into Test class...
r24508 output = re.sub(s, r, output)
Gregory Szorc
run-tests: separate newline normalization from replacements...
r24510
if normalizenewlines:
output = output.replace('\r\n', '\n')
Gregory Szorc
run-tests: move run into Test class...
r24508 return ret, output.splitlines(True)
Gregory Szorc
run-tests: create classes for representing tests...
r21296 class PythonTest(Test):
"""A Python-based test."""
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501
@property
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 def refpath(self):
Augie Fackler
run-tests: unblock running python tests in python 3...
r25058 return os.path.join(self._testdir, b'%s.out' % self.bname)
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 def _run(self, env):
Augie Fackler
run-tests: unblock running python tests in python 3...
r25058 py3kswitch = self._py3kwarnings and b' -3' or b''
cmd = b'%s%s "%s"' % (PYTHON, py3kswitch, self.path)
Gregory Szorc
run-tests: roll pytest() into PythonTest._run()...
r21311 vlog("# Running", cmd)
Gregory Szorc
run-tests: separate newline normalization from replacements...
r24510 normalizenewlines = os.name == 'nt'
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 result = self._runcommand(cmd, env,
Gregory Szorc
run-tests: separate newline normalization from replacements...
r24510 normalizenewlines=normalizenewlines)
Gregory Szorc
run-tests: remove global abort flag...
r21520 if self._aborted:
raise KeyboardInterrupt()
return result
Gregory Szorc
run-tests: roll pytest() into PythonTest._run()...
r21311
Matt Harbison
run-tests: don't warn on unnecessary globs mandated by check-code.py...
r23352 # This script may want to drop globs from lines matching these patterns on
# Windows, but check-code.py wants a glob on these lines unconditionally. Don't
# warn if that is the case for anything matching these lines.
checkcodeglobpats = [
Augie Fackler
run-tests: fix checking a line to see if it needs globbing
r25059 re.compile(br'^pushing to \$TESTTMP/.*[^)]$'),
re.compile(br'^moving \S+/.*[^)]$'),
re.compile(br'^pulling from \$TESTTMP/.*[^)]$')
Matt Harbison
run-tests: don't warn on unnecessary globs mandated by check-code.py...
r23352 ]
Augie Fackler
run-tests: work around chr() producing unicode in Python 3
r25036 bchr = chr
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: work around chr() producing unicode in Python 3
r25036 bchr = lambda x: bytes([x])
Gregory Szorc
run-tests: create classes for representing tests...
r21296 class TTest(Test):
"""A "t test" is a test backed by a .t file."""
Gregory Szorc
run-tests: move SKIPPED_PREFIX and FAILED_PREFIX into TTest
r21381 SKIPPED_PREFIX = 'skipped: '
FAILED_PREFIX = 'hghave check failed: '
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 NEEDESCAPE = re.compile(br'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
Gregory Szorc
run-tests: move string escaping to TTest...
r21384
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 ESCAPESUB = re.compile(br'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
ESCAPEMAP = dict((bchr(i), br'\x%02x' % i) for i in range(256))
ESCAPEMAP.update({b'\\': b'\\\\', b'\r': br'\r'})
Gregory Szorc
run-tests: move SKIPPED_PREFIX and FAILED_PREFIX into TTest
r21381
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501 @property
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 def refpath(self):
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 return os.path.join(self._testdir, self.bname)
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 def _run(self, env):
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(self.path, 'rb')
Gregory Szorc
run-tests: move t test execution from tsttest() to TTest.run()...
r21313 lines = f.readlines()
f.close()
Gregory Szorc
run-tests: refactor testtmp...
r21454 salt, script, after, expected = self._parsetest(lines)
Gregory Szorc
run-tests: move t test execution from tsttest() to TTest.run()...
r21313
# Write out the generated script.
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 fname = b'%s.sh' % self._testtmp
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(fname, 'wb')
Gregory Szorc
run-tests: move t test execution from tsttest() to TTest.run()...
r21313 for l in script:
f.write(l)
f.close()
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 cmd = b'%s "%s"' % (self._shell, fname)
Gregory Szorc
run-tests: move t test execution from tsttest() to TTest.run()...
r21313 vlog("# Running", cmd)
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 exitcode, output = self._runcommand(cmd, env)
Gregory Szorc
run-tests: remove global abort flag...
r21520
if self._aborted:
raise KeyboardInterrupt()
Gregory Szorc
run-tests: move t test execution from tsttest() to TTest.run()...
r21313 # Do not merge output if skipped. Return hghave message instead.
# Similarly, with --debug, output is None.
Gregory Szorc
run-tests: move SKIPPED_STATUS into Test class
r21380 if exitcode == self.SKIPPED_STATUS or output is None:
Gregory Szorc
run-tests: move t test execution from tsttest() to TTest.run()...
r21313 return exitcode, output
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 return self._processoutput(exitcode, output, salt, after, expected)
Gregory Szorc
run-tests: create classes for representing tests...
r21296
Gregory Szorc
run-tests: refactor testtmp...
r21454 def _hghave(self, reqs):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 # TODO do something smarter when all other uses of hghave are gone.
FUJIWARA Katsunori
run-tests.py: execute hghave by the path relative to run-tests.py...
r25728 runtestdir = os.path.abspath(os.path.dirname(_bytespath(__file__)))
tdir = runtestdir.replace(b'\\', b'/')
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 proc = Popen4(b'%s -c "%s/hghave %s"' %
(self._shell, tdir, b' '.join(reqs)),
FUJIWARA Katsunori
run-tests.py: execute hghave with same env vars as ones for actual tests...
r23933 self._testtmp, 0, self._getenv())
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 stdout, stderr = proc.communicate()
ret = proc.wait()
Matt Harbison
run-tests: resurrect the wifexited polyfill (backout 6ab5a1c9ea3c)...
r25177 if wifexited(ret):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 ret = os.WEXITSTATUS(ret)
if ret == 2:
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print(stdout)
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 sys.exit(1)
return ret == 0
Gregory Szorc
run-tests: refactor testtmp...
r21454 def _parsetest(self, lines):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 # We generate a shell script which outputs unique markers to line
# up script results with our source. These markers include input
# line number and the last return code.
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 salt = b"SALT%d" % time.time()
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 def addsalt(line, inpython):
if inpython:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 script.append(b'%s %d 0\n' % (salt, line))
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 else:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 script.append(b'echo %s %d $?\n' % (salt, line))
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312
script = []
# After we run the shell script, we re-unify the script output
# with non-active parts of the source, with synchronization by our
# SALT line number markers. The after table contains the non-active
# components, ordered by line number.
after = {}
# Expected shell script output.
expected = {}
pos = prepos = -1
# True or False when in a true or false conditional section
skipping = None
# We keep track of whether or not we're in a Python block so we
# can generate the surrounding doctest magic.
inpython = False
Gregory Szorc
run-tests: move debug into an argument to Test.__init__
r21510 if self._debug:
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 script.append(b'set -x\n')
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 if os.getenv('MSYSTEM'):
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 script.append(b'alias pwd="pwd -W"\n')
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312
for n, l in enumerate(lines):
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 if not l.endswith(b'\n'):
l += b'\n'
if l.startswith(b'#require'):
Matt Mackall
run-tests: add #require to abort full test...
r22045 lsplit = l.split()
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 if len(lsplit) < 2 or lsplit[0] != b'#require':
Matt Mackall
run-tests: add #require to abort full test...
r22045 after.setdefault(pos, []).append(' !!! invalid #require\n')
if not self._hghave(lsplit[1:]):
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 script = [b"exit 80\n"]
Matt Mackall
run-tests: add #require to abort full test...
r22045 break
after.setdefault(pos, []).append(l)
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b'#if'):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 lsplit = l.split()
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 if len(lsplit) < 2 or lsplit[0] != b'#if':
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 after.setdefault(pos, []).append(' !!! invalid #if\n')
if skipping is not None:
after.setdefault(pos, []).append(' !!! nested #if\n')
Gregory Szorc
run-tests: refactor testtmp...
r21454 skipping = not self._hghave(lsplit[1:])
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 after.setdefault(pos, []).append(l)
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b'#else'):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 if skipping is None:
after.setdefault(pos, []).append(' !!! missing #if\n')
skipping = not skipping
after.setdefault(pos, []).append(l)
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b'#endif'):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 if skipping is None:
after.setdefault(pos, []).append(' !!! missing #if\n')
skipping = None
after.setdefault(pos, []).append(l)
elif skipping:
after.setdefault(pos, []).append(l)
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b' >>> '): # python inlines
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 after.setdefault(pos, []).append(l)
prepos = pos
pos = n
if not inpython:
# We've just entered a Python block. Add the header.
inpython = True
addsalt(prepos, False) # Make sure we report the exit code.
Augie Fackler
run-tests: unblock running python tests in python 3...
r25058 script.append(b'%s -m heredoctest <<EOF\n' % PYTHON)
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 addsalt(n, True)
script.append(l[2:])
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b' ... '): # python inlines
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 after.setdefault(prepos, []).append(l)
script.append(l[2:])
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b' $ '): # commands
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 if inpython:
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 script.append(b'EOF\n')
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 inpython = False
after.setdefault(pos, []).append(l)
prepos = pos
pos = n
addsalt(n, False)
cmd = l[4:].split()
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 if len(cmd) == 2 and cmd[0] == b'cd':
l = b' $ cd %s || exit 1\n' % cmd[1]
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 script.append(l[4:])
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b' > '): # continuations
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 after.setdefault(prepos, []).append(l)
script.append(l[4:])
Augie Fackler
run-tests: use bytes when constructing shell script
r25035 elif l.startswith(b' '): # results
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 # Queue up a list of expected results.
expected.setdefault(pos, []).append(l[2:])
else:
if inpython:
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 script.append(b'EOF\n')
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 inpython = False
# Non-command/result. Queue up for merged output.
after.setdefault(pos, []).append(l)
if inpython:
Augie Fackler
run-tests: make sure all script lines are bytes
r25060 script.append(b'EOF\n')
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 if skipping is not None:
after.setdefault(pos, []).append(' !!! missing #endif\n')
addsalt(n + 1, False)
return salt, script, after, expected
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 def _processoutput(self, exitcode, output, salt, after, expected):
# Merge the script output back into a unified test.
warnonly = 1 # 1: not yet; 2: yes; 3: for sure not
if exitcode != 0:
warnonly = 3
pos = -1
postout = []
for l in output:
lout, lcmd = l, None
if salt in l:
lout, lcmd = l.split(salt, 1)
Matt Mackall
tests: add (?) flag for optional lines...
r25388 while lout:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if not lout.endswith(b'\n'):
lout += b' (no-eol)\n'
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314
# Find the expected output at the current position.
el = None
if expected.get(pos, None):
el = expected[pos].pop(0)
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 r = TTest.linematch(el, lout)
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 if isinstance(r, str):
if r == '+glob':
lout = el[:-1] + ' (glob)\n'
r = '' # Warn only this line.
elif r == '-glob':
lout = ''.join(el.rsplit(' (glob)', 1))
r = '' # Warn only this line.
Matt Mackall
tests: add (?) flag for optional lines...
r25388 elif r == "retry":
postout.append(b' ' + el)
continue
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 else:
log('\ninfo, unknown linematch result: %r\n' % r)
r = False
if r:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 postout.append(b' ' + el)
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 else:
Gregory Szorc
run-tests: move string escaping to TTest...
r21384 if self.NEEDESCAPE(lout):
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 lout = TTest._stringescape(b'%s (esc)\n' %
lout.rstrip(b'\n'))
postout.append(b' ' + lout) # Let diff deal with it.
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 if r != '': # If line failed.
warnonly = 3 # for sure not
elif warnonly == 1: # Is "not yet" and line is warn only.
warnonly = 2 # Yes do warn.
Matt Mackall
tests: add (?) flag for optional lines...
r25388 break
# clean up any optional leftovers
while expected.get(pos, None):
el = expected[pos].pop(0)
Augie Fackler
run-tests: add b-prefix on two strings to fix python3 support
r26612 if not el.endswith(b" (?)\n"):
Matt Mackall
tests: add (?) flag for optional lines...
r25388 expected[pos].insert(0, el)
break
postout.append(b' ' + el)
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314
if lcmd:
# Add on last return code.
ret = int(lcmd.split()[1])
if ret != 0:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 postout.append(b' [%d]\n' % ret)
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 if pos in after:
# Merge in non-active test bits.
postout += after.pop(pos)
pos = int(lcmd.split()[0])
if pos in after:
postout += after.pop(pos)
if warnonly == 2:
exitcode = False # Set exitcode to warned.
return exitcode, postout
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 @staticmethod
Gregory Szorc
run-tests: make rematch a static method of TTest
r21316 def rematch(el, l):
try:
# use \Z to ensure that the regex matches to the end of the string
if os.name == 'nt':
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 return re.match(el + br'\r?\n\Z', l)
return re.match(el + br'\n\Z', l)
Gregory Szorc
run-tests: make rematch a static method of TTest
r21316 except re.error:
# el is an invalid regex
return False
@staticmethod
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 def globmatch(el, l):
# The only supported special characters are * and ? plus / which also
# matches \ on windows. Escaping of these characters is supported.
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if el + b'\n' == l:
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 if os.altsep:
# matching on "/" is not needed for this line
Matt Harbison
run-tests: don't warn on unnecessary globs mandated by check-code.py...
r23352 for pat in checkcodeglobpats:
if pat.match(el):
return True
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 return b'-glob'
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 return True
i, n = 0, len(el)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 res = b''
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 while i < n:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 c = el[i:i + 1]
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 i += 1
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if c == b'\\' and i < n and el[i:i + 1] in b'*?\\/':
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 res += el[i - 1:i + 1]
i += 1
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 elif c == b'*':
res += b'.*'
elif c == b'?':
res += b'.'
elif c == b'/' and os.altsep:
res += b'[/\\\\]'
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 else:
res += re.escape(c)
return TTest.rematch(res, l)
@staticmethod
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 def linematch(el, l):
Matt Mackall
tests: add (?) flag for optional lines...
r25388 retry = False
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 if el == l: # perfect match (fast)
return True
if el:
Augie Fackler
run-tests: add b-prefix on two strings to fix python3 support
r26612 if el.endswith(b" (?)\n"):
Matt Mackall
tests: add (?) flag for optional lines...
r25388 retry = "retry"
el = el[:-5] + "\n"
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if el.endswith(b" (esc)\n"):
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: string-escape no longer exists in python 3, use unicode_escape
r25047 el = el[:-7].decode('unicode_escape') + '\n'
el = el.encode('utf-8')
else:
el = el[:-7].decode('string-escape') + '\n'
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if el == l or os.name == 'nt' and el[:-1] + b'\r\n' == l:
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 return True
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if el.endswith(b" (re)\n"):
Matt Mackall
tests: add (?) flag for optional lines...
r25388 return TTest.rematch(el[:-6], l) or retry
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if el.endswith(b" (glob)\n"):
Mads Kiilerich
run-tests: automatically add (glob) to "saved backup bundle to" lines...
r23728 # ignore '(glob)' added to l by 'replacements'
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if l.endswith(b" (glob)\n"):
l = l[:-8] + b"\n"
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 return TTest.globmatch(el[:-8], l)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if os.altsep and l.replace(b'\\', b'/') == el:
return b'+glob'
Matt Mackall
tests: add (?) flag for optional lines...
r25388 return retry
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315
Gregory Szorc
run-tests: move parsehghaveoutput() into TTest...
r21379 @staticmethod
def parsehghaveoutput(lines):
'''Parse hghave log lines.
Return tuple of lists (missing, failed):
* the missing/unknown features
* the features for which existence check failed'''
missing = []
failed = []
for line in lines:
Gregory Szorc
run-tests: move SKIPPED_PREFIX and FAILED_PREFIX into TTest
r21381 if line.startswith(TTest.SKIPPED_PREFIX):
Gregory Szorc
run-tests: move parsehghaveoutput() into TTest...
r21379 line = line.splitlines()[0]
Gregory Szorc
run-tests: move SKIPPED_PREFIX and FAILED_PREFIX into TTest
r21381 missing.append(line[len(TTest.SKIPPED_PREFIX):])
elif line.startswith(TTest.FAILED_PREFIX):
Gregory Szorc
run-tests: move parsehghaveoutput() into TTest...
r21379 line = line.splitlines()[0]
Gregory Szorc
run-tests: move SKIPPED_PREFIX and FAILED_PREFIX into TTest
r21381 failed.append(line[len(TTest.FAILED_PREFIX):])
Gregory Szorc
run-tests: move parsehghaveoutput() into TTest...
r21379
return missing, failed
Gregory Szorc
run-tests: move string escaping to TTest...
r21384 @staticmethod
def _escapef(m):
return TTest.ESCAPEMAP[m.group(0)]
@staticmethod
def _stringescape(s):
return TTest.ESCAPESUB(TTest._escapef, s)
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock = threading.RLock()
Matt Mackall
run-tests: add locking on results struct
r14000
Gregory Szorc
run-tests: record skips by raising SkipTest...
r21442 class SkipTest(Exception):
"""Raised to indicate that a test is to be skipped."""
Gregory Szorc
run-tests: record ignored tests by raising IgnoreTest
r21443 class IgnoreTest(Exception):
"""Raised to indicate that a test is to be ignored."""
Gregory Szorc
run-tests: record warnings by raising WarnTest...
r21444 class WarnTest(Exception):
"""Raised to indicate that a test warned."""
Gregory Szorc
run-tests: define custom result and runner classes for unittest...
r21429 class TestResult(unittest._TextTestResult):
"""Holds results when executing via unittest."""
# Don't worry too much about accessing the non-public _TextTestResult.
# It is relatively common in Python testing tools.
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460 def __init__(self, options, *args, **kwargs):
Gregory Szorc
run-tests: define custom result and runner classes for unittest...
r21429 super(TestResult, self).__init__(*args, **kwargs)
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460 self._options = options
Gregory Szorc
run-tests: teach unittest about skipped tests
r21430 # unittest.TestResult didn't have skipped until 2.7. We need to
# polyfill it.
self.skipped = []
Gregory Szorc
run-tests: teach unittest about ignored tests
r21431 # We have a custom "ignored" result that isn't present in any Python
# unittest implementation. It is very similar to skipped. It may make
# sense to map it into skip some day.
self.ignored = []
Gregory Szorc
run-tests: teach unittest about warned results
r21433 # We have a custom "warned" result that isn't present in any Python
# unittest implementation. It is very similar to failed. It may make
# sense to map it into fail some day.
self.warned = []
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 self.times = []
Pierre-Yves David
run-tests: track start and end time of tests...
r25097 self._firststarttime = None
Augie Fackler
run-tests: add support for xunit test reports...
r22044 # Data stored for the benefit of generating xunit reports.
self.successes = []
self.faildata = {}
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495
Gregory Szorc
run-tests: make failure reporting in unittest mode equivalent to default...
r21462 def addFailure(self, test, reason):
self.failures.append((test, reason))
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460
if self._options.first:
self.stop()
anuraggoel
run-tests: produce error on running a failing test...
r21735 else:
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
if not self._options.nodiff:
self.stream.write('\nERROR: %s output changed\n' % test)
anuraggoel
run-tests: fixes the '--interactive' option error...
r21754
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 self.stream.write('!')
self.stream.flush()
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460
Augie Fackler
run-tests: add support for xunit test reports...
r22044 def addSuccess(self, test):
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
super(TestResult, self).addSuccess(test)
Augie Fackler
run-tests: add support for xunit test reports...
r22044 self.successes.append(test)
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460
Augie Fackler
run-tests: add support for xunit test reports...
r22044 def addError(self, test, err):
super(TestResult, self).addError(test, err)
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460 if self._options.first:
self.stop()
Gregory Szorc
run-tests: teach unittest about skipped tests
r21430 # Polyfill.
def addSkip(self, test, reason):
self.skipped.append((test, reason))
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
if self.showAll:
self.stream.writeln('skipped %s' % reason)
else:
self.stream.write('s')
self.stream.flush()
Gregory Szorc
run-tests: teach unittest about skipped tests
r21430
Gregory Szorc
run-tests: teach unittest about ignored tests
r21431 def addIgnore(self, test, reason):
self.ignored.append((test, reason))
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
if self.showAll:
self.stream.writeln('ignored %s' % reason)
Augie Fackler
run-tests: fix test result counts with --keyword specified or skips occurring...
r21997 else:
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 if reason not in ('not retesting', "doesn't match keyword"):
self.stream.write('i')
else:
self.testsRun += 1
self.stream.flush()
Gregory Szorc
run-tests: teach unittest about ignored tests
r21431
Gregory Szorc
run-tests: teach unittest about warned results
r21433 def addWarn(self, test, reason):
self.warned.append((test, reason))
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460 if self._options.first:
self.stop()
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
if self.showAll:
self.stream.writeln('warned %s' % reason)
else:
self.stream.write('~')
self.stream.flush()
Gregory Szorc
run-tests: teach unittest about warned results
r21433
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523 def addOutputMismatch(self, test, ret, got, expected):
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 """Record a mismatch in test output for a particular test."""
Augie Fackler
run-tests: handle --jobs and --first gracefully...
r22838 if self.shouldStop:
# don't print, some other test case already failed and
# printed, we're just stale and probably failed due to our
# temp dir getting cleaned up.
return
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521
Matt Mackall
run-tests: hold iolock across diff/prompt when interactive...
r21763 accepted = False
Augie Fackler
run-tests: add support for xunit test reports...
r22044 failed = False
lines = []
Matt Mackall
run-tests: hold iolock across diff/prompt when interactive...
r21763
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
if self._options.nodiff:
pass
elif self._options.view:
Augie Fackler
run-tests: be more paranoid about os.system using bytes
r25056 v = self._options.view
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 v = _bytespath(v)
Augie Fackler
run-tests: be more paranoid about os.system using bytes
r25056 os.system(b"%s %s %s" %
(v, test.refpath, test.errpath))
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 else:
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 servefail, lines = getdiff(expected, got,
test.refpath, test.errpath)
if servefail:
self.addFailure(
test,
'server failed to start (HGPORT=%s)' % test._startport)
else:
self.stream.write('\n')
for line in lines:
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: write bytes to the binary buffer on sys.{stdout,stderr}
r25053 self.stream.flush()
self.stream.buffer.write(line)
self.stream.buffer.flush()
else:
self.stream.write(line)
self.stream.flush()
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 # handle interactive prompt without releasing iolock
if self._options.interactive:
self.stream.write('Accept this change? [n] ')
answer = sys.stdin.readline().strip()
if answer.lower() in ('y', 'yes'):
if test.name.endswith('.t'):
rename(test.errpath, test.path)
else:
rename(test.errpath, '%s.out' % test.path)
accepted = True
if not accepted and not failed:
Augie Fackler
run-tests: record faildata using bytes instead of str...
r25052 self.faildata[test.name] = b''.join(lines)
Matt Mackall
run-tests: hold iolock across diff/prompt when interactive...
r21763
return accepted
Gregory Szorc
run-tests: move interactive test acceptance into TestResult...
r21523
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 def startTest(self, test):
super(TestResult, self).startTest(test)
anuraggoel
run-tests: '--time' option provide more details to Linux users...
r21977 # os.times module computes the user time and system time spent by
# child's processes along with real elapsed time taken by a process.
# This module has one limitation. It can only work for Linux user
# and not for Windows.
Augie Fackler
run-tests: stop storing start/stop times in a dict by test name...
r24331 test.started = os.times()
Pierre-Yves David
run-tests: track start and end time of tests...
r25097 if self._firststarttime is None: # thread racy but irrelevant
self._firststarttime = test.started[4]
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495
def stopTest(self, test, interrupted=False):
super(TestResult, self).stopTest(test)
Augie Fackler
run-tests: stop storing start/stop times in a dict by test name...
r24331 test.stopped = os.times()
anuraggoel
run-tests: '--time' option provide more details to Linux users...
r21977
Augie Fackler
run-tests: stop storing start/stop times in a dict by test name...
r24331 starttime = test.started
endtime = test.stopped
Pierre-Yves David
run-tests: track start and end time of tests...
r25097 origin = self._firststarttime
Pierre-Yves David
run-tests: spread and document the content of time tuple...
r24984 self.times.append((test.name,
endtime[2] - starttime[2], # user space CPU time
endtime[3] - starttime[3], # sys space CPU time
endtime[4] - starttime[4], # real time
Pierre-Yves David
run-tests: track start and end time of tests...
r25097 starttime[4] - origin, # start date in run context
endtime[4] - origin, # end date in run context
Pierre-Yves David
run-tests: spread and document the content of time tuple...
r24984 ))
anuraggoel
run-tests: '--time' option provide more details to Linux users...
r21977
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 if interrupted:
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
test.name, self.times[-1][3]))
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495
Gregory Szorc
run-tests: define a custom TestSuite that uses _executetests()...
r21439 class TestSuite(unittest.TestSuite):
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23139 """Custom unittest TestSuite that knows how to execute Mercurial tests."""
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528
Gregory Szorc
run-tests: make testdir an argument of TestSuite.__init__...
r21533 def __init__(self, testdir, jobs=1, whitelist=None, blacklist=None,
Augie Fackler
run-tests: add --runs-per-test flag...
r24329 retest=False, keywords=None, loop=False, runs_per_test=1,
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 loadtest=None,
Gregory Szorc
run-tests: move whitelist and blacklist to named arguments of TestSuite
r21529 *args, **kwargs):
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528 """Create a new instance that can run tests with a configuration.
Gregory Szorc
run-tests: define a custom TestSuite that uses _executetests()...
r21439
Gregory Szorc
run-tests: make testdir an argument of TestSuite.__init__...
r21533 testdir specifies the directory where tests are executed from. This
is typically the ``tests`` directory from Mercurial's source
repository.
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528 jobs specifies the number of jobs to run concurrently. Each test
executes on its own thread. Tests actually spawn new processes, so
state mutation should not be an issue.
Gregory Szorc
run-tests: move whitelist and blacklist to named arguments of TestSuite
r21529
whitelist and blacklist denote tests that have been whitelisted and
blacklisted, respectively. These arguments don't belong in TestSuite.
Instead, whitelist and blacklist should be handled by the thing that
populates the TestSuite with tests. They are present to preserve
backwards compatible behavior which reports skipped tests as part
of the results.
Gregory Szorc
run-tests: make retest a named argument of TestSuite.__init__
r21530
retest denotes whether to retest failed tests. This arguably belongs
outside of TestSuite.
Gregory Szorc
run-tests: make keywords a named argument to TestSuite.__init__...
r21531
keywords denotes key words that will be used to filter which tests
to execute. This arguably belongs outside of TestSuite.
Gregory Szorc
run-tests: move loop to a named argument of TestSuite.__init__
r21532
loop denotes whether to loop over tests forever.
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528 """
Gregory Szorc
run-tests: define a custom TestSuite that uses _executetests()...
r21439 super(TestSuite, self).__init__(*args, **kwargs)
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528 self._jobs = jobs
Gregory Szorc
run-tests: move whitelist and blacklist to named arguments of TestSuite
r21529 self._whitelist = whitelist
self._blacklist = blacklist
Gregory Szorc
run-tests: make retest a named argument of TestSuite.__init__
r21530 self._retest = retest
Gregory Szorc
run-tests: make keywords a named argument to TestSuite.__init__...
r21531 self._keywords = keywords
Gregory Szorc
run-tests: move loop to a named argument of TestSuite.__init__
r21532 self._loop = loop
Augie Fackler
run-tests: add --runs-per-test flag...
r24329 self._runs_per_test = runs_per_test
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 self._loadtest = loadtest
Gregory Szorc
run-tests: define a custom TestSuite that uses _executetests()...
r21439
def run(self, result):
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 # We have a number of filters that need to be applied. We do this
# here instead of inside Test because it makes the running logic for
# Test simpler.
tests = []
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 num_tests = [0]
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 for test in self._tests:
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 def get():
num_tests[0] += 1
if getattr(test, 'should_reload', False):
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 return self._loadtest(test.bname, num_tests[0])
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 return test
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 if not os.path.exists(test.path):
result.addSkip(test, "Doesn't exist")
continue
Gregory Szorc
run-tests: move whitelist and blacklist to named arguments of TestSuite
r21529 if not (self._whitelist and test.name in self._whitelist):
Augie Fackler
run-tests: blacklist entries are bytes, use bname to check blacklisting
r25055 if self._blacklist and test.bname in self._blacklist:
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 result.addSkip(test, 'blacklisted')
continue
Gregory Szorc
run-tests: make retest a named argument of TestSuite.__init__
r21530 if self._retest and not os.path.exists(test.errpath):
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 result.addIgnore(test, 'not retesting')
continue
Gregory Szorc
run-tests: make keywords a named argument to TestSuite.__init__...
r21531 if self._keywords:
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(test.path, 'rb')
Augie Fackler
run-tests: refer to test.bname when sniffing for keywords
r25048 t = f.read().lower() + test.bname.lower()
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 f.close()
ignored = False
Gregory Szorc
run-tests: make keywords a named argument to TestSuite.__init__...
r21531 for k in self._keywords.lower().split():
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 if k not in t:
result.addIgnore(test, "doesn't match keyword")
ignored = True
break
if ignored:
continue
Augie Fackler
run-tests: add --runs-per-test flag...
r24329 for _ in xrange(self._runs_per_test):
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 tests.append(get())
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496
Gregory Szorc
run-tests: remove global abort flag...
r21520 runtests = list(tests)
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 done = queue.Queue()
running = 0
def job(test, result):
try:
test(result)
done.put(None)
except KeyboardInterrupt:
pass
except: # re-raises
done.put(('!', test, 'run-test raised an error, see traceback'))
raise
Gregory Szorc
run-tests: wait for test threads after first error...
r24507 stoppedearly = False
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 try:
while tests or running:
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528 if not done.empty() or running == self._jobs or not tests:
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 try:
done.get(True, 1)
Gregory Szorc
run-tests: wait for test threads after first error...
r24507 running -= 1
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 if result and result.shouldStop:
Gregory Szorc
run-tests: wait for test threads after first error...
r24507 stoppedearly = True
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 break
except queue.Empty:
continue
Gregory Szorc
run-tests: pass jobs into TestSuite constructor...
r21528 if tests and not running == self._jobs:
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 test = tests.pop(0)
Gregory Szorc
run-tests: move loop to a named argument of TestSuite.__init__
r21532 if self._loop:
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 if getattr(test, 'should_reload', False):
num_tests[0] += 1
tests.append(
self._loadtest(test.name, num_tests[0]))
else:
tests.append(test)
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 t = threading.Thread(target=job, name=test.name,
args=(test, result))
t.start()
running += 1
Gregory Szorc
run-tests: wait for test threads after first error...
r24507
# If we stop early we still need to wait on started tests to
# finish. Otherwise, there is a race between the test completing
# and the test's cleanup code running. This could result in the
# test reporting incorrect.
if stoppedearly:
while running:
try:
done.get(True, 1)
running -= 1
except queue.Empty:
continue
Gregory Szorc
run-tests: move _executetests into TestSuite
r21496 except KeyboardInterrupt:
Gregory Szorc
run-tests: remove global abort flag...
r21520 for test in runtests:
test.abort()
Gregory Szorc
run-tests: define a custom TestSuite that uses _executetests()...
r21439
return result
Gregory Szorc
run-tests: define custom result and runner classes for unittest...
r21429 class TextTestRunner(unittest.TextTestRunner):
"""Custom unittest test runner that uses appropriate settings."""
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459 def __init__(self, runner, *args, **kwargs):
super(TextTestRunner, self).__init__(*args, **kwargs)
self._runner = runner
def run(self, test):
Gregory Szorc
run-tests: abort tests after first failure in unittest mode...
r21460 result = TestResult(self._runner.options, self.stream,
self.descriptions, self.verbosity)
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459
test(result)
failed = len(result.failures)
warned = len(result.warned)
skipped = len(result.skipped)
ignored = len(result.ignored)
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 with iolock:
self.stream.writeln('')
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 if not self._runner.options.noskips:
for test, msg in result.skipped:
self.stream.writeln('Skipped %s: %s' % (test.name, msg))
for test, msg in result.warned:
self.stream.writeln('Warned %s: %s' % (test.name, msg))
for test, msg in result.failures:
self.stream.writeln('Failed %s: %s' % (test.name, msg))
for test, msg in result.errors:
self.stream.writeln('Errored %s: %s' % (test.name, msg))
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 if self._runner.options.xunit:
xuf = open(self._runner.options.xunit, 'wb')
try:
timesd = dict((t[0], t[3]) for t in result.times)
doc = minidom.Document()
s = doc.createElement('testsuite')
s.setAttribute('name', 'run-tests')
s.setAttribute('tests', str(result.testsRun))
s.setAttribute('errors', "0") # TODO
s.setAttribute('failures', str(failed))
s.setAttribute('skipped', str(skipped + ignored))
doc.appendChild(s)
for tc in result.successes:
t = doc.createElement('testcase')
t.setAttribute('name', tc.name)
t.setAttribute('time', '%.3f' % timesd[tc.name])
s.appendChild(t)
Augie Fackler
run-tests: use items instead of iteritems on dicts...
r25049 for tc, err in sorted(result.faildata.items()):
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 t = doc.createElement('testcase')
t.setAttribute('name', tc)
t.setAttribute('time', '%.3f' % timesd[tc])
# createCDATASection expects a unicode or it will
# convert using default conversion rules, which will
# fail if string isn't ASCII.
err = cdatasafe(err).decode('utf-8', 'replace')
cd = doc.createCDATASection(err)
t.appendChild(cd)
s.appendChild(t)
xuf.write(doc.toprettyxml(indent=' ', encoding='utf-8'))
finally:
xuf.close()
Augie Fackler
run-tests: add support for xunit test reports...
r22044
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 if self._runner.options.json:
if json is None:
raise ImportError("json module not installed")
jsonpath = os.path.join(self._runner._testdir, 'report.json')
fp = open(jsonpath, 'w')
try:
timesd = {}
for tdata in result.times:
test = tdata[0]
timesd[test] = tdata[1:]
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 outcome = {}
groups = [('success', ((tc, None)
for tc in result.successes)),
('failure', result.failures),
('skip', result.skipped)]
for res, testcases in groups:
for tc, __ in testcases:
tres = {'result': res,
'time': ('%0.3f' % timesd[tc.name][2]),
'cuser': ('%0.3f' % timesd[tc.name][0]),
Pierre-Yves David
run-tests: track start and end time of tests...
r25097 'csys': ('%0.3f' % timesd[tc.name][1]),
'start': ('%0.3f' % timesd[tc.name][3]),
'end': ('%0.3f' % timesd[tc.name][4])}
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 outcome[tc.name] = tres
jsonout = json.dumps(outcome, sort_keys=True, indent=4)
fp.writelines(("testreport =", jsonout))
finally:
fp.close()
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 self._runner._checkhglib('Tested')
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459
Augie Fackler
run-tests: switch all uses of iolock.acquire() to a context manager
r25046 self.stream.writeln(
'# Ran %d tests, %d skipped, %d warned, %d failed.'
% (result.testsRun,
skipped + ignored, warned, failed))
if failed:
self.stream.writeln('python hash seed: %s' %
os.environ['PYTHONHASHSEED'])
if self._runner.options.time:
self.printtimes(result.times)
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104
Gregory Szorc
run-tests: exit with non-0 exit code when tests fail or warn...
r21613 return result
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 def printtimes(self, times):
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 # iolock held by run
Gregory Szorc
run-tests: move outputtimes() into unittest runner class...
r21494 self.stream.writeln('# Producing time report')
anuraggoel
run-tests: '--time' option provide more details to Linux users...
r21977 times.sort(key=lambda t: (t[3]))
Pierre-Yves David
run-tests: include 'start' and 'end' in --time output...
r25098 cols = '%7.3f %7.3f %7.3f %7.3f %7.3f %s'
self.stream.writeln('%-7s %-7s %-7s %-7s %-7s %s' %
('start', 'end', 'cuser', 'csys', 'real', 'Test'))
Pierre-Yves David
run-tests: stop explicit expansion of time data...
r24982 for tdata in times:
test = tdata[0]
Pierre-Yves David
run-tests: include 'start' and 'end' in --time output...
r25098 cuser, csys, real, start, end = tdata[1:6]
self.stream.writeln(cols % (start, end, cuser, csys, real, test))
Gregory Szorc
run-tests: define custom result and runner classes for unittest...
r21429
Gregory Szorc
run-tests: establish a class to hold testing state
r21340 class TestRunner(object):
"""Holds context for executing tests.
Tests rely on a lot of state. This object holds it for them.
"""
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357
Gregory Szorc
run-tests: add docstrings
r21536 # Programs required to run tests.
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 REQUIREDTOOLS = [
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 os.path.basename(_bytespath(sys.executable)),
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 b'diff',
b'grep',
b'unzip',
b'gunzip',
b'bunzip2',
b'sed',
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 ]
Gregory Szorc
run-tests: add docstrings
r21536 # Maps file extensions to test class.
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357 TESTTYPES = [
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 (b'.py', PythonTest),
(b'.t', TTest),
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357 ]
Gregory Szorc
run-tests: move TESTDIR out of a global...
r21341 def __init__(self):
Gregory Szorc
run-tests: add options to runner
r21348 self.options = None
Gregory Szorc
run-tests: report code coverage from source directory...
r24506 self._hgroot = None
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._testdir = None
self._hgtmp = None
self._installdir = None
self._bindir = None
self._tmpbinddir = None
self._pythondir = None
self._coveragefile = None
Gregory Szorc
run-tests: move createdfiles out of a global and into TestRunner
r21352 self._createdfiles = []
Gregory Szorc
run-tests: move _gethgpath() into TestRunner
r21385 self._hgpath = None
Pierre-Yves David
run-test: ensure the test ports are available before launching test...
r24967 self._portoffset = 0
self._ports = {}
Gregory Szorc
run-tests: establish a class to hold testing state
r21340
Gregory Szorc
run-tests: move option parser logic to TestRunner.run()
r21376 def run(self, args, parser=None):
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366 """Run the test suite."""
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 oldmask = os.umask(0o22)
Gregory Szorc
run-tests: move umask into TestRunner.run()...
r21375 try:
Gregory Szorc
run-tests: move option parser logic to TestRunner.run()
r21376 parser = parser or getparser()
options, args = parseargs(args, parser)
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 # positional arguments are paths to test files to run, so
# we make sure they're all bytestrings
args = [_bytespath(a) for a in args]
Gregory Szorc
run-tests: move option parser logic to TestRunner.run()
r21376 self.options = options
Gregory Szorc
run-tests: move umask into TestRunner.run()...
r21375 self._checktools()
tests = self.findtests(args)
Augie Fackler
run-tests: add a --profile-runner option...
r25107 if options.profile_runner:
import statprof
statprof.start()
result = self._run(tests)
if options.profile_runner:
statprof.stop()
statprof.display()
return result
Gregory Szorc
run-tests: move umask into TestRunner.run()...
r21375 finally:
os.umask(oldmask)
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366
def _run(self, tests):
Gregory Szorc
run-tests: move test shuffling and sorting into TestRunner
r21372 if self.options.random:
random.shuffle(tests)
else:
# keywords for slow tests
Pierre-Yves David
run-tests: allow different extra weight for slow tests...
r25067 slow = {b'svn': 10,
b'gendoc': 10,
Pierre-Yves David
run-test.py: greatly increase the priority of 'check-code' tests...
r25068 b'check-code-hg': 100,
Pierre-Yves David
run-tests: allow different extra weight for slow tests...
r25067 }
Gregory Szorc
run-tests: move test shuffling and sorting into TestRunner
r21372 def sortkey(f):
# run largest tests first, as they tend to take the longest
try:
val = -os.stat(f).st_size
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as e:
Gregory Szorc
run-tests: move test shuffling and sorting into TestRunner
r21372 if e.errno != errno.ENOENT:
raise
return -1e9 # file does not exist, tell early
Augie Fackler
run-tests: use items() instead of iteritems()...
r25261 for kw, mul in slow.items():
Gregory Szorc
run-tests: move test shuffling and sorting into TestRunner
r21372 if kw in f:
Pierre-Yves David
run-tests: allow different extra weight for slow tests...
r25067 val *= mul
Gregory Szorc
run-tests: move test shuffling and sorting into TestRunner
r21372 return val
tests.sort(key=sortkey)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 self._testdir = osenvironb[b'TESTDIR'] = getattr(
os, 'getcwdb', os.getcwd)()
Gregory Szorc
run-tests: assign testdir in TestRunner
r21371
Gregory Szorc
run-tests: move hash seed logic to TestRunner
r21370 if 'PYTHONHASHSEED' not in os.environ:
# use a random python hash seed all the time
# we do the randomness ourself to know what seed is used
os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
Gregory Szorc
run-tests: move tmpdir calculations into TestRunner
r21369 if self.options.tmpdir:
self.options.keep_tmpdir = True
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 tmpdir = _bytespath(self.options.tmpdir)
Gregory Szorc
run-tests: move tmpdir calculations into TestRunner
r21369 if os.path.exists(tmpdir):
# Meaning of tmpdir has changed since 1.3: we used to create
# HGTMP inside tmpdir; now HGTMP is tmpdir. So fail if
# tmpdir already exists.
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print("error: temp dir %r already exists" % tmpdir)
Gregory Szorc
run-tests: move tmpdir calculations into TestRunner
r21369 return 1
# Automatically removing tmpdir sounds convenient, but could
# really annoy anyone in the habit of using "--tmpdir=/tmp"
# or "--tmpdir=$HOME".
#vlog("# Removing temp dir", tmpdir)
#shutil.rmtree(tmpdir)
os.makedirs(tmpdir)
else:
d = None
if os.name == 'nt':
# without this, we get the default temp dir location, but
# in all lowercase, which causes troubles with paths (issue3490)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 d = osenvironb.get(b'TMP', None)
Augie Fackler
run-tests: python3.5 now supports mkdtemp using bytes for paths...
r25262 tmpdir = tempfile.mkdtemp(b'', b'hgtests.', d)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041
self._hgtmp = osenvironb[b'HGTMP'] = (
os.path.realpath(tmpdir))
Gregory Szorc
run-tests: move tmpdir calculations into TestRunner
r21369
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368 if self.options.with_hg:
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._installdir = None
Augie Fackler
run-tests: work around with_hg being bytes or string depending on provenance
r25042 whg = self.options.with_hg
# If --with-hg is not specified, we have bytes already,
# but if it was specified in python3 we get a str, so we
# have to encode it back into a bytes.
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: work around with_hg being bytes or string depending on provenance
r25042 if not isinstance(whg, bytes):
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 whg = _bytespath(whg)
Augie Fackler
run-tests: work around with_hg being bytes or string depending on provenance
r25042 self._bindir = os.path.dirname(os.path.realpath(whg))
assert isinstance(self._bindir, bytes)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 self._tmpbindir = os.path.join(self._hgtmp, b'install', b'bin')
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 os.makedirs(self._tmpbindir)
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368
# This looks redundant with how Python initializes sys.path from
# the location of the script being executed. Needed because the
# "hg" specified by --with-hg is not the only Python script
# executed in the test suite that needs to import 'mercurial'
# ... which means it's not really redundant at all.
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._pythondir = self._bindir
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368 else:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 self._installdir = os.path.join(self._hgtmp, b"install")
self._bindir = osenvironb[b"BINDIR"] = \
os.path.join(self._installdir, b"bin")
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._tmpbindir = self._bindir
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 self._pythondir = os.path.join(self._installdir, b"lib", b"python")
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 osenvironb[b"BINDIR"] = self._bindir
Augie Fackler
run-tests: unblock running python tests in python 3...
r25058 osenvironb[b"PYTHON"] = PYTHON
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 fileb = _bytespath(__file__)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 runtestdir = os.path.abspath(os.path.dirname(fileb))
FUJIWARA Katsunori
run-tests.py: add RUNTESTDIR to refer `tests` of Mercurial...
r25729 osenvironb[b'RUNTESTDIR'] = runtestdir
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 sepb = _bytespath(os.pathsep)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 else:
sepb = os.pathsep
path = [self._bindir, runtestdir] + osenvironb[b"PATH"].split(sepb)
Pierre-Yves David
run-tests: also follow symlink when update PATH with 'run-tests.py' dir...
r24742 if os.path.islink(__file__):
# test helper will likely be at the end of the symlink
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 realfile = os.path.realpath(fileb)
Pierre-Yves David
run-tests: also follow symlink when update PATH with 'run-tests.py' dir...
r24742 realdir = os.path.abspath(os.path.dirname(realfile))
path.insert(2, realdir)
FUJIWARA Katsunori
run-tests.py: add TESTDIR to PATH if it differs from RUNTESTDIR...
r25730 if self._testdir != runtestdir:
path = [self._testdir] + path
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 if self._tmpbindir != self._bindir:
path = [self._tmpbindir] + path
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 osenvironb[b"PATH"] = sepb.join(path)
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368
Gregory Szorc
run-tests: move pypath manipulation into TestRunner
r21367 # Include TESTDIR in PYTHONPATH so that out-of-tree extensions
# can run .../tests/run-tests.py test-foo where test-foo
# adds an extension to HGRC. Also include run-test.py directory to
# import modules like heredoctest.
Mads Kiilerich
run-tests: include testdir in $PATH so tests easily can use helper tools...
r23859 pypath = [self._pythondir, self._testdir, runtestdir]
Gregory Szorc
run-tests: move pypath manipulation into TestRunner
r21367 # We have to augment PYTHONPATH, rather than simply replacing
# it, in case external libraries are only available via current
# PYTHONPATH. (In particular, the Subversion bindings on OS X
# are in /opt/subversion.)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 oldpypath = osenvironb.get(IMPL_PATH)
Gregory Szorc
run-tests: move pypath manipulation into TestRunner
r21367 if oldpypath:
pypath.append(oldpypath)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 osenvironb[IMPL_PATH] = sepb.join(pypath)
Gregory Szorc
run-tests: move pypath manipulation into TestRunner
r21367
FUJIWARA Katsunori
run-tests.py: inherit --pure option from outer run-tests.py execution...
r23935 if self.options.pure:
os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure"
Augie Fackler
run-tests: add support for marking tests as very slow...
r26109 if self.options.allow_slow_tests:
os.environ["HGTEST_SLOW"] = "slow"
elif 'HGTEST_SLOW' in os.environ:
del os.environ['HGTEST_SLOW']
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 self._coveragefile = os.path.join(self._testdir, b'.coverage')
Gregory Szorc
run-tests: move pypath manipulation into TestRunner
r21367
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 vlog("# Using TESTDIR", self._testdir)
FUJIWARA Katsunori
run-tests.py: add RUNTESTDIR to refer `tests` of Mercurial...
r25729 vlog("# Using RUNTESTDIR", osenvironb[b'RUNTESTDIR'])
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 vlog("# Using HGTMP", self._hgtmp)
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366 vlog("# Using PATH", os.environ["PATH"])
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 vlog("# Using", IMPL_PATH, osenvironb[IMPL_PATH])
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366
try:
return self._runtests(tests) or 0
finally:
time.sleep(.1)
self._cleanup()
Gregory Szorc
run-tests: move test discovery logic into a function...
r21363 def findtests(self, args):
"""Finds possible test files from arguments.
If you wish to inject custom tests into the test harness, this would
be a good function to monkeypatch or override in a derived class.
"""
if not args:
if self.options.changed:
proc = Popen4('hg st --rev "%s" -man0 .' %
self.options.changed, None, 0)
stdout, stderr = proc.communicate()
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 args = stdout.strip(b'\0').split(b'\0')
Gregory Szorc
run-tests: move test discovery logic into a function...
r21363 else:
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 args = os.listdir(b'.')
Gregory Szorc
run-tests: move test discovery logic into a function...
r21363
return [t for t in args
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 if os.path.basename(t).startswith(b'test-')
and (t.endswith(b'.py') or t.endswith(b'.t'))]
Gregory Szorc
run-tests: move test discovery logic into a function...
r21363
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366 def _runtests(self, tests):
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360 try:
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 if self._installdir:
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 self._installhg()
self._checkhglib("Testing")
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360 else:
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 self._usecorrectpython()
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360
if self.options.restart:
orig = list(tests)
while tests:
if os.path.exists(tests[0] + ".err"):
break
tests.pop(0)
if not tests:
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print("running all tests")
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360 tests = orig
Gregory Szorc
run-tests: execute tests via unittest...
r21464 tests = [self._gettest(t, i) for i, t in enumerate(tests)]
Gregory Szorc
run-tests: pass Test instances into TestRunner._executetests()...
r21437
Gregory Szorc
run-tests: don't print results in unittest mode...
r21458 failed = False
warned = False
Augie Fackler
run-tests: make sure keyword(s) are in bytes and not str
r25050 kws = self.options.keywords
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if kws is not None and PYTHON3:
Augie Fackler
run-tests: make sure keyword(s) are in bytes and not str
r25050 kws = kws.encode('utf-8')
Gregory Szorc
run-tests: don't print results in unittest mode...
r21458
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 suite = TestSuite(self._testdir,
Gregory Szorc
run-tests: make testdir an argument of TestSuite.__init__...
r21533 jobs=self.options.jobs,
Gregory Szorc
run-tests: move whitelist and blacklist to named arguments of TestSuite
r21529 whitelist=self.options.whitelisted,
blacklist=self.options.blacklist,
Gregory Szorc
run-tests: make retest a named argument of TestSuite.__init__
r21530 retest=self.options.retest,
Augie Fackler
run-tests: make sure keyword(s) are in bytes and not str
r25050 keywords=kws,
Gregory Szorc
run-tests: move loop to a named argument of TestSuite.__init__
r21532 loop=self.options.loop,
Augie Fackler
run-tests: add --runs-per-test flag...
r24329 runs_per_test=self.options.runs_per_test,
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 tests=tests, loadtest=self._gettest)
Gregory Szorc
run-tests: execute tests via unittest...
r21464 verbosity = 1
if self.options.verbose:
verbosity = 2
runner = TextTestRunner(self, verbosity=verbosity)
Gregory Szorc
run-tests: exit with non-0 exit code when tests fail or warn...
r21613 result = runner.run(suite)
if result.failures:
failed = True
if result.warned:
warned = True
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360
if self.options.anycoverage:
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 self._outputcoverage()
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360 except KeyboardInterrupt:
failed = True
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print("\ninterrupted!")
Gregory Szorc
run-tests: move runtests() into TestRunner
r21360
if failed:
return 1
if warned:
return 80
Pierre-Yves David
run-test: ensure the test ports are available before launching test...
r24967 def _getport(self, count):
port = self._ports.get(count) # do we have a cached entry?
if port is None:
port = self.options.port + self._portoffset
portneeded = 3
# above 100 tries we just give up and let test reports failure
for tries in xrange(100):
allfree = True
for idx in xrange(portneeded):
if not checkportisavailable(port + idx):
allfree = False
break
self._portoffset += portneeded
if allfree:
break
self._ports[count] = port
return port
Gregory Szorc
run-tests: execute tests via unittest...
r21464 def _gettest(self, test, count):
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357 """Obtain a Test by looking at its filename.
Returns a Test instance. The Test may not be runnable if it doesn't
map to a known type.
"""
lctest = test.lower()
testcls = Test
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501 for ext, cls in self.TESTTYPES:
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357 if lctest.endswith(ext):
testcls = cls
break
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 refpath = os.path.join(self._testdir, test)
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 tmpdir = os.path.join(self._hgtmp, b'child%d' % count)
Gregory Szorc
run-tests: pass temp dir into Test.__init__...
r21504
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 t = testcls(refpath, tmpdir,
keeptmpdir=self.options.keep_tmpdir,
debug=self.options.debug,
timeout=self.options.timeout,
Pierre-Yves David
run-test: ensure the test ports are available before launching test...
r24967 startport=self._getport(count),
Augie Fackler
run-tests: avoid running the same test instance concurrently...
r24330 extraconfigopts=self.options.extra_config_opt,
py3kwarnings=self.options.py3k_warnings,
shell=self.options.shell)
t.should_reload = True
return t
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366 def _cleanup(self):
Gregory Szorc
run-tests: move cleanup() into TestRunner
r21350 """Clean up state from this test invocation."""
if self.options.keep_tmpdir:
return
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 vlog("# Cleaning up HGTMP", self._hgtmp)
shutil.rmtree(self._hgtmp, True)
Gregory Szorc
run-tests: move createdfiles out of a global and into TestRunner
r21352 for f in self._createdfiles:
Gregory Szorc
run-tests: move cleanup() into TestRunner
r21350 try:
os.remove(f)
except OSError:
pass
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 def _usecorrectpython(self):
Gregory Szorc
run-tests: add docstrings
r21536 """Configure the environment to use the appropriate Python in tests."""
# Tests must use the same interpreter as us or bad things will happen.
Augie Fackler
run-tests: even more bytestring annotations for Python 3
r25041 pyexename = sys.platform == 'win32' and b'python.exe' or b'python'
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 if getattr(os, 'symlink', None):
vlog("# Making python executable in test path a symlink to '%s'" %
sys.executable)
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 mypython = os.path.join(self._tmpbindir, pyexename)
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 try:
if os.readlink(mypython) == sys.executable:
return
os.unlink(mypython)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as err:
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 if err.errno != errno.ENOENT:
raise
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 if self._findprogram(pyexename) != sys.executable:
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 try:
os.symlink(sys.executable, mypython)
Gregory Szorc
run-tests: move createdfiles out of a global and into TestRunner
r21352 self._createdfiles.append(mypython)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as err:
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 # child processes may race, which is harmless
if err.errno != errno.EEXIST:
raise
else:
exedir, exename = os.path.split(sys.executable)
vlog("# Modifying search path to find %s as %s in '%s'" %
(exename, pyexename, exedir))
path = os.environ['PATH'].split(os.pathsep)
while exedir in path:
path.remove(exedir)
os.environ['PATH'] = os.pathsep.join([exedir] + path)
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 if not self._findprogram(pyexename):
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print("WARNING: Cannot find %s in search path" % pyexename)
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 def _installhg(self):
Gregory Szorc
run-tests: add docstrings
r21536 """Install hg into the test environment.
This will also configure hg with the appropriate testing settings.
"""
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 vlog("# Performing temporary installation of HG")
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 installerrs = os.path.join(b"tests", b"install.err")
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 compiler = ''
if self.options.compiler:
compiler = '--compiler ' + self.options.compiler
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 if self.options.pure:
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 pure = b"--pure"
Jordi Gutiérrez Hermoso
style: kill ersatz if-else ternary operators...
r24306 else:
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 pure = b""
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 py3 = ''
# Run installer in hg root
script = os.path.realpath(sys.argv[0])
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 exe = sys.executable
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 py3 = b'--c2to3'
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 compiler = _bytespath(compiler)
script = _bytespath(script)
exe = _bytespath(exe)
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 hgroot = os.path.dirname(os.path.dirname(script))
Gregory Szorc
run-tests: report code coverage from source directory...
r24506 self._hgroot = hgroot
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 os.chdir(hgroot)
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 nohome = b'--home=""'
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 if os.name == 'nt':
# The --home="" trick works only on OS where os.sep == '/'
# because of a distutils convert_path() fast-path. Avoid it at
# least on Windows for now, deal with .pydistutils.cfg bugs
# when they happen.
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 nohome = b''
cmd = (b'%(exe)s setup.py %(py3)s %(pure)s clean --all'
b' build %(compiler)s --build-base="%(base)s"'
b' install --force --prefix="%(prefix)s"'
b' --install-lib="%(libdir)s"'
b' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
% {b'exe': exe, b'py3': py3, b'pure': pure,
b'compiler': compiler,
b'base': os.path.join(self._hgtmp, b"build"),
b'prefix': self._installdir, b'libdir': self._pythondir,
b'bindir': self._bindir,
b'nohome': nohome, b'logfile': installerrs})
Gregory Szorc
run-tests: ensure install directories exist...
r24075
# setuptools requires install directories to exist.
def makedirs(p):
try:
os.makedirs(p)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as e:
Gregory Szorc
run-tests: ensure install directories exist...
r24075 if e.errno != errno.EEXIST:
raise
makedirs(self._pythondir)
makedirs(self._bindir)
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 vlog("# Running", cmd)
if os.system(cmd) == 0:
if not self.options.verbose:
Augie Fackler
run-tests: ignore failed removal of nonexistent installerrs...
r26087 try:
os.remove(installerrs)
except OSError as e:
if e.errno != errno.ENOENT:
raise
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 else:
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(installerrs, 'rb')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 for line in f:
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: write bytes to sys.stdout.buffer in python 3
r25040 sys.stdout.buffer.write(line)
else:
sys.stdout.write(line)
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 f.close()
sys.exit(1)
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 os.chdir(self._testdir)
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 self._usecorrectpython()
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353
if self.options.py3k_warnings and not self.options.anycoverage:
vlog("# Updating hg command to enable Py3k Warnings switch")
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(os.path.join(self._bindir, 'hg'), 'rb')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 lines = [line.rstrip() for line in f]
lines[0] += ' -3'
f.close()
Augie Fackler
run-tests: write out scripts in binary mode...
r21916 f = open(os.path.join(self._bindir, 'hg'), 'wb')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 for line in lines:
f.write(line + '\n')
f.close()
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 hgbat = os.path.join(self._bindir, b'hg.bat')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 if os.path.isfile(hgbat):
# hg.bat expects to be put in bin/scripts while run-tests.py
# installation layout put it in bin/ directly. Fix it
f = open(hgbat, 'rb')
data = f.read()
f.close()
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 if b'"%~dp0..\python" "%~dp0hg" %*' in data:
data = data.replace(b'"%~dp0..\python" "%~dp0hg" %*',
b'"%~dp0python" "%~dp0hg" %*')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 f = open(hgbat, 'wb')
f.write(data)
f.close()
else:
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print('WARNING: cannot fix hg.bat reference to python.exe')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353
if self.options.anycoverage:
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 custom = os.path.join(self._testdir, 'sitecustomize.py')
target = os.path.join(self._pythondir, 'sitecustomize.py')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 vlog('# Installing coverage trigger to %s' % target)
shutil.copyfile(custom, target)
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 rc = os.path.join(self._testdir, '.coveragerc')
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 vlog('# Installing coverage rc to %s' % rc)
os.environ['COVERAGE_PROCESS_START'] = rc
Gregory Szorc
run-tests: collect aggregate code coverage...
r24505 covdir = os.path.join(self._installdir, '..', 'coverage')
try:
os.mkdir(covdir)
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 except OSError as e:
Gregory Szorc
run-tests: collect aggregate code coverage...
r24505 if e.errno != errno.EEXIST:
raise
os.environ['COVERAGE_DIR'] = covdir
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 def _checkhglib(self, verb):
Gregory Szorc
run-tests: move checkhglib into TestRunner
r21354 """Ensure that the 'mercurial' package imported by python is
the one we expect it to be. If not, print a warning to stderr."""
Pierre-Yves David
run-tests: don't check for the mercurial library used when using --with-hg...
r21733 if ((self._bindir == self._pythondir) and
(self._bindir != self._tmpbindir)):
Mads Kiilerich
spelling: fixes from proofreading of spell checker issues
r23139 # The pythondir has been inferred from --with-hg flag.
# We cannot expect anything sensible here.
Pierre-Yves David
run-tests: don't check for the mercurial library used when using --with-hg...
r21733 return
Augie Fackler
run-tests: fix installation of hg by bytesifying more constants
r25044 expecthg = os.path.join(self._pythondir, b'mercurial')
Gregory Szorc
run-tests: move _gethgpath() into TestRunner
r21385 actualhg = self._gethgpath()
Gregory Szorc
run-tests: move checkhglib into TestRunner
r21354 if os.path.abspath(actualhg) != os.path.abspath(expecthg):
sys.stderr.write('warning: %s with unexpected mercurial lib: %s\n'
' (expected %s)\n'
% (verb, actualhg, expecthg))
Gregory Szorc
run-tests: move _gethgpath() into TestRunner
r21385 def _gethgpath(self):
"""Return the path to the mercurial package that is actually found by
the current Python interpreter."""
if self._hgpath is not None:
return self._hgpath
Augie Fackler
run-tests: unblock running python tests in python 3...
r25058 cmd = b'%s -c "import mercurial; print (mercurial.__path__[0])"'
cmd = cmd % PYTHON
Augie Fackler
run-tests: move all open-coded sys.version_info checks to PYTHON3 (issue4668)...
r25159 if PYTHON3:
Augie Fackler
run-tests: replace open-coded .decode()s on paths with a helper (issue4667)...
r25162 cmd = _strpath(cmd)
Augie Fackler
run-tests: unblock running python tests in python 3...
r25058 pipe = os.popen(cmd)
Gregory Szorc
run-tests: move _gethgpath() into TestRunner
r21385 try:
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 self._hgpath = _bytespath(pipe.read().strip())
Gregory Szorc
run-tests: move _gethgpath() into TestRunner
r21385 finally:
pipe.close()
return self._hgpath
Gregory Szorc
run-tests: move checkhglib into TestRunner
r21354
Gregory Szorc
run-tests: make some methods of TestRunner internal
r21378 def _outputcoverage(self):
Gregory Szorc
run-tests: add docstrings
r21536 """Produce code coverage output."""
Gregory Szorc
run-tests: obtain code coverage via Python API...
r24504 from coverage import coverage
Gregory Szorc
run-tests: move outputcoverage() into TestRunner
r21356
Gregory Szorc
run-tests: obtain code coverage via Python API...
r24504 vlog('# Producing coverage report')
# chdir is the easiest way to get short, relative paths in the
# output.
Gregory Szorc
run-tests: report code coverage from source directory...
r24506 os.chdir(self._hgroot)
Gregory Szorc
run-tests: collect aggregate code coverage...
r24505 covdir = os.path.join(self._installdir, '..', 'coverage')
cov = coverage(data_file=os.path.join(covdir, 'cov'))
Gregory Szorc
run-tests: report code coverage from source directory...
r24506
# Map install directory paths back to source directory.
cov.config.paths['srcdir'] = ['.', self._pythondir]
Gregory Szorc
run-tests: collect aggregate code coverage...
r24505 cov.combine()
Gregory Szorc
run-tests: move outputcoverage() into TestRunner
r21356
Gregory Szorc
run-tests: obtain code coverage via Python API...
r24504 omit = [os.path.join(x, '*') for x in [self._bindir, self._testdir]]
cov.report(ignore_errors=True, omit=omit)
Gregory Szorc
run-tests: move outputcoverage() into TestRunner
r21356 if self.options.htmlcov:
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 htmldir = os.path.join(self._testdir, 'htmlcov')
Gregory Szorc
run-tests: obtain code coverage via Python API...
r24504 cov.html_report(directory=htmldir, omit=omit)
Gregory Szorc
run-tests: move outputcoverage() into TestRunner
r21356 if self.options.annotate:
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 adir = os.path.join(self._testdir, 'annotated')
Gregory Szorc
run-tests: move outputcoverage() into TestRunner
r21356 if not os.path.isdir(adir):
os.mkdir(adir)
Gregory Szorc
run-tests: obtain code coverage via Python API...
r24504 cov.annotate(directory=adir, omit=omit)
Gregory Szorc
run-tests: move outputcoverage() into TestRunner
r21356
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 def _findprogram(self, program):
"""Search PATH for a executable program"""
Augie Fackler
run-tests: move unicode-to-bytes operations on paths to a helper (issue4667)...
r25161 dpb = _bytespath(os.defpath)
sepb = _bytespath(os.pathsep)
Augie Fackler
run-tests: fix _findprogram to reliably return bytes
r25038 for p in osenvironb.get(b'PATH', dpb).split(sepb):
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 name = os.path.join(p, program)
if os.name == 'nt' or os.access(name, os.X_OK):
return name
return None
Gregory Szorc
run-tests: move checktools into TestRunner.run()
r21374 def _checktools(self):
Gregory Szorc
run-tests: add docstrings
r21536 """Ensure tools required to run tests are present."""
Gregory Szorc
run-tests: move program searching into TestRunner
r21365 for p in self.REQUIREDTOOLS:
if os.name == 'nt' and not p.endswith('.exe'):
p += '.exe'
found = self._findprogram(p)
if found:
vlog("# Found prerequisite", p, "at", found)
else:
Augie Fackler
python3: update killdaemons and run-tests print and exception syntax...
r25031 print("WARNING: Did not find prerequisite tool: %s " % p)
Gregory Szorc
run-tests: move program searching into TestRunner
r21365
Simon Heimberg
run-tests: loadable as module
r13347 if __name__ == '__main__':
Gregory Szorc
run-tests: eliminate main()
r21377 runner = TestRunner()
Matt Mackall
run-tests: self-test on Windows needs binary streams
r22120
try:
import msvcrt
msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
except ImportError:
pass
Gregory Szorc
run-tests: eliminate main()
r21377 sys.exit(runner.run(sys.argv[1:]))