##// END OF EJS Templates
run-test: ensure the test ports are available before launching test...
run-test: ensure the test ports are available before launching test I'm running into a systematic issue because there is always some port taken in the 1500-wide range of ports used by the test (3 for each test file).

File last commit:

r24967:00790cc2 default
r24967:00790cc2 default
Show More
run-tests.py
2121 lines | 75.9 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
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.)
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
Bryan O'Sullivan
run-tests: support running tests in parallel on windows...
r18057 import Queue as 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
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
Brendan Cully
run-tests: replace popen locking with a noop _cleanup handler on py24...
r19415 # subprocess._cleanup can race with any Popen.wait or Popen.poll on py24
# http://bugs.python.org/issue1731717 for details. We shouldn't be producing
# zombies but it's pretty harmless even if we do.
Simon Heimberg
run-tests: better check for python version...
r20219 if sys.version_info < (2, 5):
Brendan Cully
run-tests: replace popen locking with a noop _cleanup handler on py24...
r19415 subprocess._cleanup = lambda: None
Matt Mackall
run-tests: do chdir for tests under a lock for thread safety
r14019
Gregory Szorc
run-tests: move run into Test class...
r24508 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
except socket.error, exc:
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
Mads Kiilerich
run-tests: convert windows paths to unix
r15448 PYTHON = sys.executable.replace('\\', '/')
Ronny Pfannschmidt
tests: adapt the test runner to work with jython
r10758 IMPL_PATH = 'PYTHONPATH'
if 'java' in sys.platform:
IMPL_PATH = '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
run-tests: allow whitelisting tests that should always run...
r14493 except IOError, err:
if err.errno != errno.ENOENT:
raise
if warn:
print "warning: no such %s file: %s" % (listtype, filename)
continue
for line in f.readlines():
line = line.split('#', 1)[0].strip()
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')
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:
testdir = os.path.dirname(os.path.realpath(sys.argv[0]))
hgbin = os.path.join(os.path.dirname(testdir), '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:
if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
Martin Geisler
run-tests: standardize on --foo instead of -f/--foo...
r9408 parser.error('--py3k-warnings can only be used on Python 2.6+')
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)
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 = []
Mads Kiilerich
run-tests.py: Show paths to failing tests, .err and .out...
r10088 for line in difflib.unified_diff(expected, output, ref, err):
Matt Mackall
run-tests: filter whitespace at end of error diffs...
r21737 if line.startswith('+++') or line.startswith('---'):
Matt Mackall
run-tests: output diffs with only forward slashes...
r23077 line = line.replace('\\', '/')
Matt Mackall
run-tests: filter whitespace at end of error diffs...
r21737 if line.endswith(' \n'):
line = line[:-2] + '\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(
'+ abort: child process failed to start'):
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
CDATA_EVIL = re.compile(r"[\000-\010\013\014\016-\037]")
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.
"""
return CDATA_EVIL.sub('?', data).replace(']]>', '] ]>')
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.
"""
Matt Mackall
run-tests: add a log function
r19251 iolock.acquire()
if verbose:
print verbose,
for m in msg:
print m,
print
sys.stdout.flush()
iolock.release()
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 """
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 self.path = path
Gregory Szorc
run-tests: pass a full test path into Test.__init__...
r21502 self.name = os.path.basename(path)
self._testdir = os.path.dirname(path)
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 self.errpath = os.path.join(self._testdir, '%s.err' % self.name)
Gregory Szorc
run-tests: rename Test._test to Test.name...
r21435
Gregory Szorc
run-tests: pass temp dir into Test.__init__...
r21504 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
Gregory Szorc
run-tests: move shell to Test.__init__
r21517 self._shell = 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)
except OSError, e:
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)
except OSError, e:
# 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
except SkipTest, e:
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
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 except IgnoreTest, e:
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
Gregory Szorc
run-tests: merge MercurialTest into Test...
r21488 except WarnTest, e:
result.addWarn(self, str(e))
except self.failureException, e:
# 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 = []
Gregory Szorc
run-tests: factor options.keep_tmpdir into an argument to Test.__init__
r21509 if not self._keeptmpdir:
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 = [
Gregory Szorc
run-tests: refactor port number declaration...
r21514 (r':%s\b' % self._startport, ':$HGPORT'),
(r':%s\b' % (self._startport + 1), ':$HGPORT1'),
(r':%s\b' % (self._startport + 2), ':$HGPORT2'),
Mads Kiilerich
run-tests: automatically add (glob) to "saved backup bundle to" lines...
r23728 (r'(?m)^(saved backup bundle to .*\.hg)( \(glob\))?$',
r'\1 (glob)'),
Gregory Szorc
run-tests: move replacements generation into Test...
r21298 ]
if os.name == 'nt':
r.append(
(''.join(c.isalpha() and '[%s%s]' % (c.lower(), c.upper()) or
c in '/\\' and r'[/\\]' or c.isdigit() and c or '\\' + c
Gregory Szorc
run-tests: refactor testtmp...
r21454 for c in self._testtmp), '$TESTTMP'))
Gregory Szorc
run-tests: move replacements generation into Test...
r21298 else:
Gregory Szorc
run-tests: refactor testtmp...
r21454 r.append((re.escape(self._testtmp), '$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)
Gregory Szorc
run-tests: remove threadtmp in Test.cleanup()...
r21310 env["HGRCPATH"] = os.path.join(self._threadtmp, '.hgrc')
env["DAEMON_PIDS"] = os.path.join(self._threadtmp, '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')
Gregory Szorc
run-tests: move createhgrc into Test
r21382 hgrc.write('[ui]\n')
hgrc.write('slash = True\n')
hgrc.write('interactive = False\n')
FUJIWARA Katsunori
filemerge: use 'basic' as the default of '[ui] mergemarkers' for safety...
r21918 hgrc.write('mergemarkers = detailed\n')
Yuya Nishihara
ui: separate option to show prompt echo, enabled only in tests (issue4417)...
r23053 hgrc.write('promptecho = True\n')
Gregory Szorc
run-tests: move createhgrc into Test
r21382 hgrc.write('[defaults]\n')
hgrc.write('backout = -d "0 0"\n')
hgrc.write('commit = -d "0 0"\n')
hgrc.write('shelve = --date "0 0"\n')
hgrc.write('tag = -d "0 0"\n')
Pierre-Yves David
run-test: enable the devel warning during tests...
r24751 hgrc.write('[devel]\n')
hgrc.write('all = true\n')
Matt Harbison
run-tests: set a default largefiles usercache in the default hgrc file...
r23388 hgrc.write('[largefiles]\n')
hgrc.write('usercache = %s\n' %
(os.path.join(self._testtmp, '.cache/largefiles')))
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)
hgrc.write('[%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()
if wifexited(ret):
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):
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501 return os.path.join(self._testdir, '%s.out' % self.name)
Gregory Szorc
run-tests: obtain replacements inside Test._runcommand...
r24516 def _run(self, env):
Gregory Szorc
run-tests: move py3kwarnings to Test.__init__
r21516 py3kswitch = self._py3kwarnings and ' -3' or ''
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 cmd = '%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 = [
re.compile(r'^pushing to \$TESTTMP/.*[^)]$'),
re.compile(r'^moving \S+/.*[^)]$'),
re.compile(r'^pulling from \$TESTTMP/.*[^)]$')
]
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: '
Gregory Szorc
run-tests: move string escaping to TTest...
r21384 NEEDESCAPE = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
Mads Kiilerich
run-tests: assign value to ESCAPEMAP - dict.update do not return self...
r21539 ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256))
ESCAPEMAP.update({'\\': '\\\\', '\r': r'\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):
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501 return os.path.join(self._testdir, self.name)
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.
Gregory Szorc
run-tests: refactor testtmp...
r21454 fname = '%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()
Gregory Szorc
run-tests: move shell to Test.__init__
r21517 cmd = '%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.
Gregory Szorc
run-tests: move TESTDIR out of a global...
r21341 tdir = self._testdir.replace('\\', '/')
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 proc = Popen4('%s -c "%s/hghave %s"' %
Gregory Szorc
run-tests: move shell to Test.__init__
r21517 (self._shell, tdir, ' '.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()
if wifexited(ret):
ret = os.WEXITSTATUS(ret)
if ret == 2:
print stdout
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.
salt = "SALT" + str(time.time())
def addsalt(line, inpython):
if inpython:
script.append('%s %d 0\n' % (salt, line))
else:
script.append('echo %s %s $?\n' % (salt, line))
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:
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 script.append('set -x\n')
if os.getenv('MSYSTEM'):
script.append('alias pwd="pwd -W"\n')
for n, l in enumerate(lines):
if not l.endswith('\n'):
l += '\n'
Matt Mackall
run-tests: add #require to abort full test...
r22045 if l.startswith('#require'):
lsplit = l.split()
if len(lsplit) < 2 or lsplit[0] != '#require':
after.setdefault(pos, []).append(' !!! invalid #require\n')
if not self._hghave(lsplit[1:]):
script = ["exit 80\n"]
break
after.setdefault(pos, []).append(l)
elif l.startswith('#if'):
Gregory Szorc
run-tests: move t test parsing into its own function...
r21312 lsplit = l.split()
if len(lsplit) < 2 or lsplit[0] != '#if':
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)
elif l.startswith('#else'):
if skipping is None:
after.setdefault(pos, []).append(' !!! missing #if\n')
skipping = not skipping
after.setdefault(pos, []).append(l)
elif l.startswith('#endif'):
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)
elif l.startswith(' >>> '): # python inlines
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.
script.append('%s -m heredoctest <<EOF\n' % PYTHON)
addsalt(n, True)
script.append(l[2:])
elif l.startswith(' ... '): # python inlines
after.setdefault(prepos, []).append(l)
script.append(l[2:])
elif l.startswith(' $ '): # commands
if inpython:
script.append('EOF\n')
inpython = False
after.setdefault(pos, []).append(l)
prepos = pos
pos = n
addsalt(n, False)
cmd = l[4:].split()
if len(cmd) == 2 and cmd[0] == 'cd':
l = ' $ cd %s || exit 1\n' % cmd[1]
script.append(l[4:])
elif l.startswith(' > '): # continuations
after.setdefault(prepos, []).append(l)
script.append(l[4:])
elif l.startswith(' '): # results
# Queue up a list of expected results.
expected.setdefault(pos, []).append(l[2:])
else:
if inpython:
script.append('EOF\n')
inpython = False
# Non-command/result. Queue up for merged output.
after.setdefault(pos, []).append(l)
if inpython:
script.append('EOF\n')
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)
if lout:
if not lout.endswith('\n'):
lout += ' (no-eol)\n'
# 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.
else:
log('\ninfo, unknown linematch result: %r\n' % r)
r = False
if r:
postout.append(' ' + el)
else:
Gregory Szorc
run-tests: move string escaping to TTest...
r21384 if self.NEEDESCAPE(lout):
Mads Kiilerich
run-tests: fix invalid reference to stringescape after a36cc85a5b7b
r21538 lout = TTest._stringescape('%s (esc)\n' %
lout.rstrip('\n'))
Gregory Szorc
run-tests: finish moving tsttest() into TTest
r21314 postout.append(' ' + lout) # Let diff deal with it.
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.
if lcmd:
# Add on last return code.
ret = int(lcmd.split()[1])
if ret != 0:
postout.append(' [%s]\n' % ret)
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':
return re.match(el + r'\r?\n\Z', l)
return re.match(el + r'\n\Z', l)
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.
if el + '\n' == l:
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
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 return '-glob'
return True
i, n = 0, len(el)
res = ''
while i < n:
c = el[i]
i += 1
Gregory Szorc
run-tests: don't error when glob matched line ends with backslash...
r24811 if c == '\\' and i < n and el[i] in '*?\\/':
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 res += el[i - 1:i + 1]
i += 1
elif c == '*':
res += '.*'
elif c == '?':
res += '.'
elif c == '/' and os.altsep:
res += '[/\\\\]'
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):
if el == l: # perfect match (fast)
return True
if el:
if el.endswith(" (esc)\n"):
el = el[:-7].decode('string-escape') + '\n'
if el == l or os.name == 'nt' and el[:-1] + '\r\n' == l:
return True
if el.endswith(" (re)\n"):
Gregory Szorc
run-tests: make rematch a static method of TTest
r21316 return TTest.rematch(el[:-6], l)
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 if el.endswith(" (glob)\n"):
Mads Kiilerich
run-tests: automatically add (glob) to "saved backup bundle to" lines...
r23728 # ignore '(glob)' added to l by 'replacements'
if l.endswith(" (glob)\n"):
l = l[:-8] + "\n"
Gregory Szorc
run-tests: make globmatch a static method of TTest
r21317 return TTest.globmatch(el[:-8], l)
Gregory Szorc
run-tests: make linematch a static method of TTest...
r21315 if os.altsep and l.replace('\\', '/') == el:
return '+glob'
return False
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 = []
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:
Matt Mackall
run-tests: add iolock to failure output
r21993 iolock.acquire()
anuraggoel
run-tests: produce error on running a failing test...
r21735 if not self._options.nodiff:
self.stream.write('\nERROR: %s output changed\n' % test)
anuraggoel
run-tests: fixes the '--interactive' option error...
r21754
anuraggoel
run-tests: produce '!' mark after running a failing test...
r21736 self.stream.write('!')
Matt Mackall
run-tests: fix some io ordering...
r22146 self.stream.flush()
Matt Mackall
run-tests: add iolock to failure output
r21993 iolock.release()
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):
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.acquire()
Augie Fackler
run-tests: add support for xunit test reports...
r22044 super(TestResult, self).addSuccess(test)
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.release()
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))
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.acquire()
Gregory Szorc
run-tests: teach unittest about skipped tests
r21430 if self.showAll:
self.stream.writeln('skipped %s' % reason)
else:
self.stream.write('s')
self.stream.flush()
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.release()
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))
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.acquire()
Gregory Szorc
run-tests: teach unittest about ignored tests
r21431 if self.showAll:
self.stream.writeln('ignored %s' % reason)
else:
Matt Mackall
run-tests: don't show 'i' for tests that don't match a keyword
r22107 if reason != 'not retesting' and reason != "doesn't match keyword":
anuraggoel
run-tests: skipped test should not produce 'i' mark while retesting...
r21739 self.stream.write('i')
Augie Fackler
run-tests: fix test result counts with --keyword specified or skips occurring...
r21997 else:
self.testsRun += 1
Gregory Szorc
run-tests: teach unittest about ignored tests
r21431 self.stream.flush()
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.release()
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()
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.acquire()
Gregory Szorc
run-tests: teach unittest about warned results
r21433 if self.showAll:
self.stream.writeln('warned %s' % reason)
else:
self.stream.write('~')
self.stream.flush()
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.release()
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
iolock.acquire()
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 if self._options.nodiff:
Matt Mackall
run-tests: hold iolock across diff/prompt when interactive...
r21763 pass
elif self._options.view:
Matt Mackall
run-tests: make --view work again
r21919 os.system("%s %s %s" %
(self._options.view, test.refpath, test.errpath))
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 else:
Kyle Lippincott
run-tests: more accurate/helpful message than "diff generation failed"...
r22839 servefail, lines = getdiff(expected, got,
test.refpath, test.errpath)
if servefail:
self.addFailure(
test,
'server failed to start (HGPORT=%s)' % test._startport)
Gregory Szorc
run-tests: move diff generation into TestResult...
r21521 else:
self.stream.write('\n')
for line in lines:
self.stream.write(line)
self.stream.flush()
Gregory Szorc
run-tests: make --interactive work with --view
r22361 # 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:
self.faildata[test.name] = ''.join(lines)
Matt Mackall
run-tests: hold iolock across diff/prompt when interactive...
r21763 iolock.release()
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()
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
anuraggoel
run-tests: '--time' option provide more details to Linux users...
r21977 self.times.append((test.name, endtime[2] - starttime[2],
endtime[3] - starttime[3], endtime[4] - starttime[4]))
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 if interrupted:
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.acquire()
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 self.stream.writeln('INTERRUPTED: %s (after %d seconds)' % (
anuraggoel
run-tests: '--time' option provide more details to Linux users...
r21977 test.name, self.times[-1][3]))
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.release()
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):
return self._loadtest(test.name, num_tests[0])
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):
if self._blacklist and test.name 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')
Gregory Szorc
run-tests: move test filtering into TestSuite.run()...
r21507 t = f.read().lower() + test.name.lower()
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)
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.acquire()
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459 self.stream.writeln('')
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))
Augie Fackler
run-tests: add support for xunit test reports...
r22044 if self._runner.options.xunit:
xuf = open(self._runner.options.xunit, 'wb')
try:
timesd = dict(
(test, real) for test, cuser, csys, real 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)
for tc, err in sorted(result.faildata.iteritems()):
t = doc.createElement('testcase')
t.setAttribute('name', tc)
t.setAttribute('time', '%.3f' % timesd[tc])
Gregory Szorc
run-tests: explicitly handle unicode when writing xunit file...
r24500 # 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)
Augie Fackler
run-tests: add support for xunit test reports...
r22044 t.appendChild(cd)
s.appendChild(t)
xuf.write(doc.toprettyxml(indent=' ', encoding='utf-8'))
finally:
xuf.close()
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 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 test, cuser, csys, real in result.times:
anuraggoel
run-tests: added 'cuser', 'csys' time info in report.json file...
r22486 timesd[test] = (real, cuser, csys)
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391
outcome = {}
for tc in result.successes:
testresult = {'result': 'success',
anuraggoel
run-tests: added 'cuser', 'csys' time info in report.json file...
r22486 'time': ('%0.3f' % timesd[tc.name][0]),
'cuser': ('%0.3f' % timesd[tc.name][1]),
'csys': ('%0.3f' % timesd[tc.name][2])}
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 outcome[tc.name] = testresult
for tc, err in sorted(result.faildata.iteritems()):
testresult = {'result': 'failure',
anuraggoel
run-tests: added 'cuser', 'csys' time info in report.json file...
r22486 'time': ('%0.3f' % timesd[tc][0]),
'cuser': ('%0.3f' % timesd[tc][1]),
'csys': ('%0.3f' % timesd[tc][2])}
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 outcome[tc] = testresult
for tc, reason in result.skipped:
testresult = {'result': 'skip',
anuraggoel
run-tests: added 'cuser', 'csys' time info in report.json file...
r22486 'time': ('%0.3f' % timesd[tc.name][0]),
'cuser': ('%0.3f' % timesd[tc.name][1]),
'csys': ('%0.3f' % timesd[tc.name][2])}
anuraggoel
run-tests: added '--json' functionality to store test result in json file...
r22391 outcome[tc.name] = testresult
jsonout = json.dumps(outcome, sort_keys=True, indent=4)
fp.writelines(("testreport =", jsonout))
finally:
fp.close()
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459 self._runner._checkhglib('Tested')
self.stream.writeln('# Ran %d tests, %d skipped, %d warned, %d failed.'
Augie Fackler
run-tests: fix test result counts with --keyword specified or skips occurring...
r21997 % (result.testsRun,
Gregory Szorc
run-tests: print compatible output from TextTestRunner...
r21459 skipped + ignored, warned, failed))
if failed:
self.stream.writeln('python hash seed: %s' %
os.environ['PYTHONHASHSEED'])
if self._runner.options.time:
Gregory Szorc
run-tests: capture execution times in TestResult...
r21495 self.printtimes(result.times)
Gregory Szorc
run-tests: move outputtimes() into unittest runner class...
r21494
Matt Mackall
run-tests: attempt to fix iolock handling...
r22104 iolock.release()
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]))
cols = '%7.3f %7.3f %7.3f %s'
self.stream.writeln('%-7s %-7s %-7s %s' % ('cuser', 'csys', 'real',
'Test'))
for test, cuser, csys, real in times:
self.stream.writeln(cols % (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 = [
os.path.basename(sys.executable),
'diff',
'grep',
'unzip',
'gunzip',
'bunzip2',
'sed',
]
Gregory Szorc
run-tests: add docstrings
r21536 # Maps file extensions to test class.
Gregory Szorc
run-tests: move gettest() into TestRunner
r21357 TESTTYPES = [
Gregory Szorc
run-tests: factor refpath into Test classes...
r21501 ('.py', PythonTest),
('.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."""
Gregory Szorc
run-tests: move umask into TestRunner.run()...
r21375 oldmask = os.umask(022)
try:
Gregory Szorc
run-tests: move option parser logic to TestRunner.run()
r21376 parser = parser or getparser()
options, args = parseargs(args, parser)
self.options = options
Gregory Szorc
run-tests: move umask into TestRunner.run()...
r21375 self._checktools()
tests = self.findtests(args)
return self._run(tests)
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
slow = 'svn gendoc check-code-hg'.split()
def sortkey(f):
# run largest tests first, as they tend to take the longest
try:
val = -os.stat(f).st_size
except OSError, e:
if e.errno != errno.ENOENT:
raise
return -1e9 # file does not exist, tell early
for kw in slow:
if kw in f:
val *= 10
return val
tests.sort(key=sortkey)
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._testdir = os.environ['TESTDIR'] = 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
tmpdir = self.options.tmpdir
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.
print "error: temp dir %r already exists" % tmpdir
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)
d = os.getenv('TMP')
tmpdir = tempfile.mkdtemp('', 'hgtests.', d)
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._hgtmp = os.environ['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
self._bindir = os.path.dirname(os.path.realpath(
self.options.with_hg))
self._tmpbindir = os.path.join(self._hgtmp, 'install', 'bin')
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:
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._installdir = os.path.join(self._hgtmp, "install")
self._bindir = os.environ["BINDIR"] = \
os.path.join(self._installdir, "bin")
self._tmpbindir = self._bindir
self._pythondir = os.path.join(self._installdir, "lib", "python")
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 os.environ["BINDIR"] = self._bindir
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368 os.environ["PYTHON"] = PYTHON
Mads Kiilerich
run-tests: include testdir in $PATH so tests easily can use helper tools...
r23859 runtestdir = os.path.abspath(os.path.dirname(__file__))
path = [self._bindir, runtestdir] + os.environ["PATH"].split(os.pathsep)
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
realfile = os.path.realpath(__file__)
realdir = os.path.abspath(os.path.dirname(realfile))
path.insert(2, realdir)
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 if self._tmpbindir != self._bindir:
path = [self._tmpbindir] + path
Gregory Szorc
run-tests: move more path calculations into TestRunner
r21368 os.environ["PATH"] = os.pathsep.join(path)
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.)
oldpypath = os.environ.get(IMPL_PATH)
if oldpypath:
pypath.append(oldpypath)
os.environ[IMPL_PATH] = os.pathsep.join(pypath)
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"
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 self._coveragefile = os.path.join(self._testdir, '.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)
vlog("# Using HGTMP", self._hgtmp)
Gregory Szorc
run-tests: establish TestRunner.run()...
r21366 vlog("# Using PATH", os.environ["PATH"])
vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
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()
args = stdout.strip('\0').split('\0')
else:
args = os.listdir('.')
return [t for t in args
if os.path.basename(t).startswith('test-')
and (t.endswith('.py') or t.endswith('.t'))]
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:
print "running all tests"
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
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,
Gregory Szorc
run-tests: make keywords a named argument to TestSuite.__init__...
r21531 keywords=self.options.keywords,
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
print "\ninterrupted!"
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)
tmpdir = os.path.join(self._hgtmp, '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.
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 pyexename = sys.platform == 'win32' and 'python.exe' or 'python'
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)
except OSError, err:
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)
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 except OSError, err:
# 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):
Gregory Szorc
run-tests: move usecorrectpython() into TestRunner
r21351 print "WARNING: Cannot find %s in search path" % pyexename
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")
installerrs = os.path.join("tests", "install.err")
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:
pure = "--pure"
else:
pure = ""
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 py3 = ''
if sys.version_info[0] == 3:
py3 = '--c2to3'
# Run installer in hg root
script = os.path.realpath(sys.argv[0])
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)
nohome = '--home=""'
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.
nohome = ''
cmd = ('%(exe)s setup.py %(py3)s %(pure)s clean --all'
' build %(compiler)s --build-base="%(base)s"'
' install --force --prefix="%(prefix)s"'
' --install-lib="%(libdir)s"'
' --install-scripts="%(bindir)s" %(nohome)s >%(logfile)s 2>&1'
% {'exe': sys.executable, 'py3': py3, 'pure': pure,
'compiler': compiler,
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 'base': os.path.join(self._hgtmp, "build"),
'prefix': self._installdir, 'libdir': self._pythondir,
'bindir': self._bindir,
Gregory Szorc
run-tests: move installhg() into TestRunner
r21353 'nohome': nohome, 'logfile': installerrs})
Gregory Szorc
run-tests: ensure install directories exist...
r24075
# setuptools requires install directories to exist.
def makedirs(p):
try:
os.makedirs(p)
except OSError, e:
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:
os.remove(installerrs)
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:
Gregory Szorc
run-tests: avoid printing extra newlines from install log...
r24074 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()
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 hgbat = os.path.join(self._bindir, '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()
if '"%~dp0..\python" "%~dp0hg" %*' in data:
data = data.replace('"%~dp0..\python" "%~dp0hg" %*',
'"%~dp0python" "%~dp0hg" %*')
f = open(hgbat, 'wb')
f.write(data)
f.close()
else:
print 'WARNING: cannot fix hg.bat reference to python.exe'
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)
except OSError, e:
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
Gregory Szorc
run-tests: make attributes of TestRunner internal...
r21534 expecthg = os.path.join(self._pythondir, '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
cmd = '%s -c "import mercurial; print (mercurial.__path__[0])"'
pipe = os.popen(cmd % PYTHON)
try:
self._hgpath = pipe.read().strip()
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"""
for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
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:
print "WARNING: Did not find prerequisite tool: %s " % p
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:]))