Patchwork [014,of,179,tests-refactor] run-tests: allow Test.run() to run multiple times

login
register
mail settings
Submitter Gregory Szorc
Date May 2, 2014, 6:37 p.m.
Message ID <38b41ccded330d28d224.1399055851@vm-ubuntu-main.gateway.sonic.net>
Download mbox | patch
Permalink /patch/4509/
State Accepted
Commit 0b123e6a318c3cc2d3ee64076420fb80eb38660d
Headers show

Comments

Gregory Szorc - May 2, 2014, 6:37 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1397944054 25200
#      Sat Apr 19 14:47:34 2014 -0700
# Branch stable
# Node ID 38b41ccded330d28d22458596a9bbfec48177ae5
# Parent  020837217a5004ee99186e2e0002f8a7480a4b26
run-tests: allow Test.run() to run multiple times

Test.run() can now be executed multiple times on the same Test instance.
This feature is currently unused and there are no plans to implement it.
The main reason for this work was to refactor testtmp, replacements, and
env to be run-time specific as opposed to Test instance specific.

Patch

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -538,41 +538,44 @@  def outputcoverage(options):
         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."""
+    """Encapsulates a single, runnable test.
+
+    Test instances can be run multiple times via run(). However, multiple
+    runs cannot be run concurrently.
+    """
 
     def __init__(self, path, options, count):
         self._path = path
         self._options = options
+        self._count = count
 
         self.threadtmp = os.path.join(HGTMP, 'child%d' % count)
         os.mkdir(self.threadtmp)
 
-        self._testtmp = os.path.join(self.threadtmp, os.path.basename(path))
-        os.mkdir(self._testtmp)
-
-        self._setreplacements(count)
-
     def run(self, result, refpath):
-        env = self._getenv()
+        testtmp = os.path.join(self.threadtmp, os.path.basename(self._path))
+        os.mkdir(testtmp)
+        replacements, port = self._getreplacements(testtmp)
+        env = self._getenv(testtmp, port)
         createhgrc(env['HGRCPATH'], self._options)
 
         starttime = time.time()
 
         def updateduration():
             result.duration = time.time() - starttime
 
         try:
-            ret, out = self._run(self._replacements, env)
+            ret, out = self._run(testtmp, replacements, env)
             updateduration()
             result.ret = ret
             result.out = out
         except KeyboardInterrupt:
             updateduration()
             result.interrupted = True
         except Exception, e:
             updateduration()
@@ -586,45 +589,47 @@  class Test(object):
             result.refout = None # to match "out is None"
         elif os.path.exists(refpath):
             f = open(refpath, 'r')
             result.refout = f.read().splitlines(True)
             f.close()
         else:
             result.refout = []
 
-    def _run(self, replacements, env):
+        if not self._options.keep_tmpdir:
+            shutil.rmtree(testtmp)
+
+    def _run(self, testtmp, replacements, env):
         raise NotImplemented('Subclasses must implement Test.run()')
 
-    def _setreplacements(self, count):
-        port = self._options.port + count * 3
+    def _getreplacements(self, testtmp):
+        port = self._options.port + self._count * 3
         r = [
             (r':%s\b' % port, ':$HGPORT'),
             (r':%s\b' % (port + 1), ':$HGPORT1'),
             (r':%s\b' % (port + 2), ':$HGPORT2'),
             ]
 
         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
-                    for c in self._testtmp), '$TESTTMP'))
+                    for c in testtmp), '$TESTTMP'))
         else:
-            r.append((re.escape(self._testtmp), '$TESTTMP'))
+            r.append((re.escape(testtmp), '$TESTTMP'))
 
-        self._replacements = r
-        self._port = port
+        return r, port
 
-    def _getenv(self):
+    def _getenv(self, testtmp, port):
         env = os.environ.copy()
-        env['TESTTMP'] = self._testtmp
-        env['HOME'] = self._testtmp
-        env["HGPORT"] = str(self._port)
-        env["HGPORT1"] = str(self._port + 1)
-        env["HGPORT2"] = str(self._port + 2)
+        env['TESTTMP'] = testtmp
+        env['HOME'] = testtmp
+        env["HGPORT"] = str(port)
+        env["HGPORT1"] = str(port + 1)
+        env["HGPORT2"] = str(port + 2)
         env["HGRCPATH"] = os.path.join(self.threadtmp, '.hgrc')
         env["DAEMON_PIDS"] = os.path.join(self.threadtmp, 'daemon.pids')
         env["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
         env["HGMERGE"] = "internal:merge"
         env["HGUSER"]   = "test"
         env["HGENCODING"] = "ascii"
         env["HGENCODINGMODE"] = "strict"
 
@@ -669,18 +674,18 @@  def pytest(test, wd, options, replacemen
     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, replacements, env):
-        return pytest(self._path, self._testtmp, self._options, replacements,
+    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)]
@@ -932,18 +937,18 @@  def tsttest(test, wd, options, replaceme
 
     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, replacements, env):
-        return tsttest(self._path, self._testtmp, self._options, replacements,
+    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: