@@ -572,24 +572,42 @@ def outputcoverage(options):
htmldir = os.path.join(TESTDIR, 'htmlcov')
covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
if options.annotate:
adir = os.path.join(TESTDIR, 'annotated')
if not os.path.isdir(adir):
os.mkdir(adir)
covrun('-i', '-a', '"--directory=%s"' % adir, '"--omit=%s"' % omit)
+class Test(object):
+ """Encapsulates a single, runnable test."""
+
+ def __init__(self, path, options):
+ self._path = path
+ self._options = options
+
+ def run(self, testtmp, replacements, env):
+ return self._run(testtmp, replacements, env)
+
+ def _run(self, testtmp, replacements, env):
+ raise NotImplemented('Subclasses must implement Test.run()')
+
def pytest(test, wd, options, replacements, env):
py3kswitch = options.py3k_warnings and ' -3' or ''
cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
vlog("# Running", cmd)
if os.name == 'nt':
replacements.append((r'\r\n', '\n'))
return run(cmd, wd, options, replacements, env)
+class PythonTest(Test):
+ """A Python-based test."""
+ def _run(self, testtmp, replacements, env):
+ return pytest(self._path, testtmp, self._options, replacements, env)
+
needescape = re.compile(r'[\x00-\x08\x0b-\x1f\x7f-\xff]').search
escapesub = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub
escapemap = dict((chr(i), r'\x%02x' % i) for i in range(256))
escapemap.update({'\\': '\\\\', '\r': r'\r'})
def escapef(m):
return escapemap[m.group(0)]
def stringescape(s):
return escapesub(escapef, s)
@@ -836,16 +854,22 @@ def tsttest(test, wd, options, replaceme
if pos in after:
postout += after.pop(pos)
if warnonly == 2:
exitcode = False # set exitcode to warned
return exitcode, postout
+class TTest(Test):
+ """A "t test" is a test backed by a .t file."""
+
+ def _run(self, testtmp, replacements, env):
+ return tsttest(self._path, testtmp, self._options, replacements, env)
+
wifexited = getattr(os, "WIFEXITED", lambda x: False)
def run(cmd, wd, options, replacements, env):
"""Run command in a sub-process, capturing the output (stdout and stderr).
Return a tuple (exitcode, output). output is None in debug mode."""
# TODO: Use subprocess.Popen if we're running on Python 2.4
if options.debug:
proc = subprocess.Popen(cmd, shell=True, cwd=wd, env=env)
ret = proc.wait()
@@ -945,29 +969,31 @@ def runone(options, test, count):
for k in options.keywords.lower().split():
if k in t:
break
else:
return ignore("doesn't match keyword")
if not os.path.basename(lctest).startswith("test-"):
return skip("not a test file")
- for ext, func, out in testtypes:
+ for ext, cls, out in testtypes:
if lctest.endswith(ext):
- runner = func
+ runner = cls
ref = os.path.join(TESTDIR, test + out)
break
else:
return skip("unknown test type")
vlog("# Test", test)
if os.path.exists(err):
os.remove(err) # Remove any previous output files
+ t = runner(testpath, options)
+
# Make a tmp subdirectory to work in
threadtmp = os.path.join(HGTMP, "child%d" % count)
testtmp = os.path.join(threadtmp, os.path.basename(test))
os.mkdir(threadtmp)
os.mkdir(testtmp)
port = options.port + count * 3
replacements = [
@@ -985,17 +1011,17 @@ def runone(options, test, count):
else:
replacements.append((re.escape(testtmp), '$TESTTMP'))
env = createenv(options, testtmp, threadtmp, port)
createhgrc(env['HGRCPATH'], options)
starttime = time.time()
try:
- ret, out = runner(testpath, testtmp, options, replacements, env)
+ ret, out = t.run(testtmp, replacements, env)
except KeyboardInterrupt:
endtime = time.time()
log('INTERRUPTED: %s (after %d seconds)' % (test, endtime - starttime))
raise
endtime = time.time()
times.append((test, endtime - starttime))
vlog("# Ret was:", ret)
@@ -1185,18 +1211,18 @@ def runtests(options, tests):
failed = True
print "\ninterrupted!"
if failed:
return 1
if warned:
return 80
-testtypes = [('.py', pytest, '.out'),
- ('.t', tsttest, '')]
+testtypes = [('.py', PythonTest, '.out'),
+ ('.t', TTest, '')]
def main(args, parser=None):
parser = parser or getparser()
(options, args) = parseargs(args, parser)
os.umask(022)
checktools()