Patchwork [046,of,179,tests-refactor] run-tests: move TESTDIR out of a global

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

Comments

Gregory Szorc - May 2, 2014, 6:38 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1397974524 25200
#      Sat Apr 19 23:15:24 2014 -0700
# Branch stable
# Node ID e45a84e5b9eb089060cfbe21b9470fd72fdaff95
# Parent  5717b806b4e8d500931b0143ce4c598c341e6a88
run-tests: move TESTDIR out of a global

This patch starts the process of removing global variables from
run-tests.py. The goal of this is to make it easier to run tests
differently without having to concern yourself with global state.

Patch

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -425,17 +425,17 @@  def usecorrectpython():
              (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)
         if not findprogram(pyexename):
             print "WARNING: Cannot find %s in search path" % pyexename
 
-def installhg(options):
+def installhg(runner, options):
     vlog("# Performing temporary installation of HG")
     installerrs = os.path.join("tests", "install.err")
     compiler = ''
     if options.compiler:
         compiler = '--compiler ' + options.compiler
     pure = options.pure and "--pure" or ""
     py3 = ''
     if sys.version_info[0] == 3:
@@ -465,17 +465,17 @@  def installhg(options):
         if not options.verbose:
             os.remove(installerrs)
     else:
         f = open(installerrs)
         for line in f:
             print line,
         f.close()
         sys.exit(1)
-    os.chdir(TESTDIR)
+    os.chdir(runner.testdir)
 
     usecorrectpython()
 
     if options.py3k_warnings and not options.anycoverage:
         vlog("# Updating hg command to enable Py3k Warnings switch")
         f = open(os.path.join(BINDIR, 'hg'), 'r')
         lines = [line.rstrip() for line in f]
         lines[0] += ' -3'
@@ -497,67 +497,68 @@  def installhg(options):
                                 '"%~dp0python" "%~dp0hg" %*')
             f = open(hgbat, 'wb')
             f.write(data)
             f.close()
         else:
             print 'WARNING: cannot fix hg.bat reference to python.exe'
 
     if options.anycoverage:
-        custom = os.path.join(TESTDIR, 'sitecustomize.py')
+        custom = os.path.join(runner.testdir, 'sitecustomize.py')
         target = os.path.join(PYTHONDIR, 'sitecustomize.py')
         vlog('# Installing coverage trigger to %s' % target)
         shutil.copyfile(custom, target)
-        rc = os.path.join(TESTDIR, '.coveragerc')
+        rc = os.path.join(runner.testdir, '.coveragerc')
         vlog('# Installing coverage rc to %s' % rc)
         os.environ['COVERAGE_PROCESS_START'] = rc
         fn = os.path.join(INST, '..', '.coverage')
         os.environ['COVERAGE_FILE'] = fn
 
 def outputtimes(options):
     vlog('# Producing time report')
     times.sort(key=lambda t: (t[1], t[0]), reverse=True)
     cols = '%7.3f   %s'
     print '\n%-7s   %s' % ('Time', 'Test')
     for test, timetaken in times:
         print cols % (timetaken, test)
 
-def outputcoverage(options):
+def outputcoverage(runner, options):
 
     vlog('# Producing coverage report')
     os.chdir(PYTHONDIR)
 
     def covrun(*args):
         cmd = 'coverage %s' % ' '.join(args)
         vlog('# Running: %s' % cmd)
         os.system(cmd)
 
     covrun('-c')
-    omit = ','.join(os.path.join(x, '*') for x in [BINDIR, TESTDIR])
+    omit = ','.join(os.path.join(x, '*') for x in [BINDIR, runner.testdir])
     covrun('-i', '-r', '"--omit=%s"' % omit) # report
     if options.htmlcov:
-        htmldir = os.path.join(TESTDIR, 'htmlcov')
+        htmldir = os.path.join(runner.testdir, 'htmlcov')
         covrun('-i', '-b', '"--directory=%s"' % htmldir, '"--omit=%s"' % omit)
     if options.annotate:
-        adir = os.path.join(TESTDIR, 'annotated')
+        adir = os.path.join(runner.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.
 
     Test instances can be run multiple times via run(). However, multiple
     runs cannot be run concurrently.
     """
 
     def __init__(self, testdir, test, options, count, refpath):
         path = os.path.join(testdir, test)
         errpath = os.path.join(testdir, '%s.err' % test)
 
+        self._testdir = testdir
         self._test = test
         self._path = path
         self._options = options
         self._count = count
         self._daemonpids = []
         self._refpath = refpath
         self._errpath = errpath
 
@@ -840,17 +841,17 @@  class TTest(Test):
         # Similarly, with --debug, output is None.
         if exitcode == SKIPPED_STATUS or output is None:
             return exitcode, output
 
         return self._processoutput(exitcode, output, salt, after, expected)
 
     def _hghave(self, reqs, testtmp):
         # TODO do something smarter when all other uses of hghave are gone.
-        tdir = TESTDIR.replace('\\', '/')
+        tdir = self._testdir.replace('\\', '/')
         proc = Popen4('%s -c "%s/hghave %s"' %
                       (self._options.shell, tdir, ' '.join(reqs)),
                       testtmp, 0)
         stdout, stderr = proc.communicate()
         ret = proc.wait()
         if wifexited(ret):
             ret = os.WEXITSTATUS(ret)
         if ret == 2:
@@ -1077,35 +1078,35 @@  class TTest(Test):
             if el.endswith(" (re)\n"):
                 return TTest.rematch(el[:-6], l)
             if el.endswith(" (glob)\n"):
                 return TTest.globmatch(el[:-8], l)
             if os.altsep and l.replace('\\', '/') == el:
                 return '+glob'
         return False
 
-def gettest(testdir, test, options, count):
+def gettest(runner, test, options, count):
     """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()
-    refpath = os.path.join(testdir, test)
+    refpath = os.path.join(runner.testdir, test)
 
-    runner = Test
+    testcls = Test
 
     for ext, cls, out in testtypes:
         if lctest.endswith(ext):
-            runner = cls
-            refpath = os.path.join(testdir, test + out)
+            testcls = cls
+            refpath = os.path.join(runner.testdir, test + out)
             break
 
-    return runner(testdir, test, options, count, refpath)
+    return testcls(runner.testdir, test, options, count, refpath)
 
 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)
@@ -1184,17 +1185,17 @@  def scheduletests(runner, options, tests
     jobs = options.jobs
     done = queue.Queue()
     running = 0
     count = 0
     global abort
 
     def job(test, count):
         try:
-            t = gettest(TESTDIR, test, options, count)
+            t = gettest(runner, test, options, count)
             done.put(t.run())
             del t # For side-effects.
         except KeyboardInterrupt:
             pass
         except: # re-raises
             done.put(('!', test, 'run-test raised an error, see traceback'))
             raise
 
@@ -1218,17 +1219,17 @@  def scheduletests(runner, options, tests
                 running += 1
                 count += 1
     except KeyboardInterrupt:
         abort = True
 
 def runtests(runner, options, tests):
     try:
         if INST:
-            installhg(options)
+            installhg(runner, options)
             _checkhglib("Testing")
         else:
             usecorrectpython()
 
         if options.restart:
             orig = list(tests)
             while tests:
                 if os.path.exists(tests[0] + ".err"):
@@ -1258,17 +1259,17 @@  def runtests(runner, options, tests):
         print "# Ran %d tests, %d skipped, %d warned, %d failed." % (
             tested, skipped + ignored, warned, failed)
         if results['!']:
             print 'python hash seed:', os.environ['PYTHONHASHSEED']
         if options.time:
             outputtimes(options)
 
         if options.anycoverage:
-            outputcoverage(options)
+            outputcoverage(runner, options)
     except KeyboardInterrupt:
         failed = True
         print "\ninterrupted!"
 
     if failed:
         return 1
     if warned:
         return 80
@@ -1276,16 +1277,18 @@  def runtests(runner, options, tests):
 testtypes = [('.py', PythonTest, '.out'),
              ('.t', TTest, '')]
 
 class TestRunner(object):
     """Holds context for executing tests.
 
     Tests rely on a lot of state. This object holds it for them.
     """
+    def __init__(self):
+        self.testdir = None
 
 def main(args, parser=None):
     runner = TestRunner()
 
     parser = parser or getparser()
     (options, args) = parseargs(args, parser)
     os.umask(022)
 
@@ -1323,18 +1326,18 @@  def main(args, parser=None):
             return val
         tests.sort(key=sortkey)
 
     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))
 
-    global TESTDIR, HGTMP, INST, BINDIR, TMPBINDIR, PYTHONDIR, COVERAGE_FILE
-    TESTDIR = os.environ["TESTDIR"] = os.getcwd()
+    global HGTMP, INST, BINDIR, TMPBINDIR, PYTHONDIR, COVERAGE_FILE
+    runner.testdir = os.environ['TESTDIR'] = os.getcwd()
     if options.tmpdir:
         options.keep_tmpdir = True
         tmpdir = 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
@@ -1380,29 +1383,30 @@  def main(args, parser=None):
     if TMPBINDIR != BINDIR:
         path = [TMPBINDIR] + path
     os.environ["PATH"] = os.pathsep.join(path)
 
     # 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.
-    pypath = [PYTHONDIR, TESTDIR, os.path.abspath(os.path.dirname(__file__))]
+    pypath = [PYTHONDIR, runner.testdir,
+              os.path.abspath(os.path.dirname(__file__))]
     # 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)
 
-    COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
+    COVERAGE_FILE = os.path.join(runner.testdir, ".coverage")
 
-    vlog("# Using TESTDIR", TESTDIR)
+    vlog("# Using TESTDIR", runner.testdir)
     vlog("# Using HGTMP", HGTMP)
     vlog("# Using PATH", os.environ["PATH"])
     vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
 
     try:
         return runtests(runner, options, tests) or 0
     finally:
         time.sleep(.1)