Patchwork [017,of,179,tests-refactor] run-tests: move t test parsing into its own function

login
register
mail settings
Submitter Gregory Szorc
Date May 2, 2014, 6:37 p.m.
Message ID <944c8d8f054267dbdede.1399055854@vm-ubuntu-main.gateway.sonic.net>
Download mbox | patch
Permalink /patch/4512/
State Accepted
Commit 986b8a58a6d3f0f38d0f8582c6d4674525e0becf
Headers show

Comments

Gregory Szorc - May 2, 2014, 6:37 p.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1397945968 25200
#      Sat Apr 19 15:19:28 2014 -0700
# Branch stable
# Node ID 944c8d8f054267dbdedeac9bdff7826ed4b9e8ed
# Parent  b81c0cdec1404e692bf042c67632d65f92fdf9d3
run-tests: move t test parsing into its own function

Test parsing is somewhat complicated. This patch extracts it into its
own function.

The impetus of this patch is folding tsttest() into the TTest class.
Subsequent patches will continue this work until tsttest() no longer
exists.

Patch

diff --git a/tests/run-tests.py b/tests/run-tests.py
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -740,135 +740,22 @@  def linematch(el, l):
         if el.endswith(" (re)\n"):
             return rematch(el[:-6], l)
         if el.endswith(" (glob)\n"):
             return globmatch(el[:-8], l)
         if os.altsep and l.replace('\\', '/') == el:
             return '+glob'
     return False
 
-def tsttest(test, wd, options, replacements, env):
-    # 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))
-
-    # 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 = {}
-    pos = prepos = -1
-
-    # Expected shell script output
-    expected = {}
-
-    # We keep track of whether or not we're in a Python block so we
-    # can generate the surrounding doctest magic
-    inpython = False
-
-    # True or False when in a true or false conditional section
-    skipping = None
-
-    def hghave(reqs):
-        # TODO: do something smarter when all other uses of hghave is gone
-        tdir = TESTDIR.replace('\\', '/')
-        proc = Popen4('%s -c "%s/hghave %s"' %
-                      (options.shell, tdir, ' '.join(reqs)), wd, 0)
-        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
-
+def tsttest(t, test, wd, options, replacements, env):
     f = open(test)
-    t = f.readlines()
+    tlines = f.readlines()
     f.close()
 
-    script = []
-    if options.debug:
-        script.append('set -x\n')
-    if os.getenv('MSYSTEM'):
-        script.append('alias pwd="pwd -W"\n')
-    n = 0
-    for n, l in enumerate(t):
-        if not l.endswith('\n'):
-            l += '\n'
-        if l.startswith('#if'):
-            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')
-            skipping = not hghave(lsplit[1:])
-            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)
+    salt, script, after, expected = t._parsetest(tlines, wd)
 
     # Write out the script and execute it
     name = wd + '.sh'
     f = open(name, 'w')
     for l in script:
         f.write(l)
     f.close()
 
@@ -939,19 +826,142 @@  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, testtmp, replacements, env):
-        return tsttest(self._path, testtmp, self._options, replacements,
+        return tsttest(self, self._path, testtmp, self._options, replacements,
                        env)
 
+    def _hghave(self, reqs, testtmp):
+        # TODO do something smarter when all other uses of hghave are gone.
+        tdir = 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:
+            print stdout
+            sys.exit(1)
+
+        return ret == 0
+
+    def _parsetest(self, lines, testtmp):
+        # 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
+
+        if self._options.debug:
+            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'
+            if l.startswith('#if'):
+                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')
+                skipping = not self._hghave(lsplit[1:], testtmp)
+                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
+
+
 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()