tests/run-tests.py
changeset 781 c7a471b49819
parent 777 cd294ce45931
child 793 9cc90e2c826f
--- a/tests/run-tests.py	Mon Jul 26 12:32:45 2010 +0200
+++ b/tests/run-tests.py	Sat Aug 14 09:47:41 2010 +0100
@@ -52,6 +52,7 @@
 import sys
 import tempfile
 import time
+import re
 
 closefds = os.name == 'posix'
 def Popen4(cmd, bufsize=-1):
@@ -441,6 +442,94 @@
 def alarmed(signum, frame):
     raise Timeout
 
+def pytest(test, options):
+    py3kswitch = options.py3k_warnings and ' -3' or ''
+    cmd = '%s%s "%s"' % (PYTHON, py3kswitch, test)
+    vlog("# Running", cmd)
+    return run(cmd, options)
+
+def shtest(test, options):
+    cmd = '"%s"' % test
+    vlog("# Running", cmd)
+    return run(cmd, options)
+
+def battest(test, options):
+    # To reliably get the error code from batch files on WinXP,
+    # the "cmd /c call" prefix is needed. Grrr
+    cmd = 'cmd /c call "%s"' % testpath
+    vlog("# Running", cmd)
+    return run(cmd, options)
+
+def tsttest(test, options):
+    t = open(test)
+    out = []
+    script = []
+    salt = "SALT" + str(time.time())
+
+    pos = prepos = -1
+    after = {}
+    expected = {}
+    for n, l in enumerate(t):
+        if l.startswith('  $ '): # commands
+            after.setdefault(pos, []).append(l)
+            prepos = pos
+            pos = n
+            script.append('echo %s %s\n' % (salt, n))
+            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:
+            # non-command/result - queue up for merged output
+            after.setdefault(pos, []).append(l)
+
+    fd, name = tempfile.mkstemp(suffix='hg-tst')
+
+    try:
+        for l in script:
+            os.write(fd, l)
+        os.close(fd)
+
+        cmd = '/bin/sh "%s"' % name
+        vlog("# Running", cmd)
+        exitcode, output = run(cmd, options)
+    finally:
+        os.remove(name)
+
+    def rematch(el, l):
+        try:
+            return re.match(el, l)
+        except re.error:
+            # el is an invalid regex
+            return False
+
+    pos = -1
+    postout = []
+    for n, l in enumerate(output):
+        if l.startswith(salt):
+            if pos in after:
+                postout += after.pop(pos)
+            pos = int(l.split()[1])
+        else:
+            el = None
+            if pos in expected and expected[pos]:
+                el = expected[pos].pop(0)
+
+            if el == l: # perfect match (fast)
+                postout.append("  " + l)
+            elif el and rematch(el, l): # fallback regex match
+                postout.append("  " + el)
+            else: # mismatch - let diff deal with it
+                postout.append("  " + l)
+
+    if pos in after:
+        postout += after.pop(pos)
+
+    return exitcode, postout
+
 def run(cmd, options):
     """Run command in a sub-process, capturing the output (stdout and stderr).
     Return a tuple (exitcode, output).  output is None in debug mode."""
@@ -537,15 +626,15 @@
     lctest = test.lower()
 
     if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
-        py3kswitch = options.py3k_warnings and ' -3' or ''
-        cmd = '%s%s "%s"' % (PYTHON, py3kswitch, testpath)
+        runner = pytest
     elif lctest.endswith('.bat'):
         # do not run batch scripts on non-windows
         if os.name != 'nt':
             return skip("batch script")
-        # To reliably get the error code from batch files on WinXP,
-        # the "cmd /c call" prefix is needed. Grrr
-        cmd = 'cmd /c call "%s"' % testpath
+        runner = battest
+    elif lctest.endswith('.t'):
+        runner = tsttest
+        ref = testpath
     else:
         # do not run shell scripts on windows
         if os.name == 'nt':
@@ -555,7 +644,7 @@
             return fail("does not exist")
         elif not os.access(testpath, os.X_OK):
             return skip("not executable")
-        cmd = '"%s"' % testpath
+        runner = shtest
 
     # Make a tmp subdirectory to work in
     tmpd = os.path.join(HGTMP, test)
@@ -565,8 +654,7 @@
     if options.timeout > 0:
         signal.alarm(options.timeout)
 
-    vlog("# Running", cmd)
-    ret, out = run(cmd, options)
+    ret, out = runner(testpath, options)
     vlog("# Ret was:", ret)
 
     if options.timeout > 0:
@@ -807,7 +895,10 @@
                     print "Accept this change? [n] ",
                     answer = sys.stdin.readline().strip()
                     if answer.lower() in "y yes".split():
-                        rename(test + ".err", test + ".out")
+                        if test.endswith(".t"):
+                            rename(test + ".err", test)
+                        else:
+                            rename(test + ".err", test + ".out")
                         tested += 1
                         fails.pop()
                         continue
@@ -944,7 +1035,7 @@
     for test in args:
         if (test.startswith("test-") and '~' not in test and
             ('.' not in test or test.endswith('.py') or
-             test.endswith('.bat'))):
+             test.endswith('.bat') or test.endswith('.t'))):
             tests.append(test)
     if not tests:
         print "# Ran 0 tests, 0 skipped, 0 failed."