--- a/tests/run-tests.py Mon Oct 24 17:02:10 2011 +0100
+++ b/tests/run-tests.py Mon Nov 07 13:52:03 2011 +0000
@@ -521,60 +521,115 @@
def stringescape(s):
return escapesub(escapef, s)
-def transformtst(lines):
- inblock = False
- for l in lines:
- if inblock:
- if l.startswith(' $ ') or not l.startswith(' '):
- inblock = False
- yield ' > EOF\n'
- yield l
- else:
- yield ' > ' + l[2:]
+def rematch(el, l):
+ try:
+ # ensure that the regex matches to the end of the string
+ return re.match(el + r'\Z', l)
+ except re.error:
+ # el is an invalid regex
+ return False
+
+def globmatch(el, l):
+ # The only supported special characters are * and ?. Escaping is
+ # supported.
+ i, n = 0, len(el)
+ res = ''
+ while i < n:
+ c = el[i]
+ i += 1
+ if c == '\\' and el[i] in '*?\\':
+ res += el[i - 1:i + 1]
+ i += 1
+ elif c == '*':
+ res += '.*'
+ elif c == '?':
+ res += '.'
else:
- if l.startswith(' >>> '):
- inblock = True
- yield ' $ %s -m heredoctest <<EOF\n' % PYTHON
- yield ' > ' + l[2:]
- else:
- yield l
- if inblock:
- yield ' > EOF\n'
+ res += re.escape(c)
+ return rematch(res, l)
+
+def linematch(el, l):
+ if el == l: # perfect match (fast)
+ return True
+ if (el and
+ (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', l) or
+ el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', l) or
+ el.endswith(" (esc)\n") and el.decode('string-escape') == l)):
+ return True
+ return False
def tsttest(test, wd, options, replacements):
- t = open(test)
- out = []
- script = []
+ # 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):
+ 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
- pos = prepos = -1
- after = {}
+ # Expected shellscript output
expected = {}
- for n, l in enumerate(transformtst(t)):
+
+ # We keep track of whether or not we're in a Python block so we
+ # can generate the surrounding doctest magic
+ inpython = False
+
+ f = open(test)
+ t = f.readlines()
+ f.close()
+
+ script = []
+ for n, l in enumerate(t):
if not l.endswith('\n'):
l += '\n'
- if l.startswith(' $ '): # commands
+ if l.startswith(' >>> '): # python inlines
+ if not inpython:
+ # we've just entered a Python block, add the header
+ inpython = True
+ addsalt(n)
+ script.append('%s -m heredoctest <<EOF\n' % PYTHON)
+ prepos = pos
+ pos = n
+ 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
- script.append('echo %s %s $?\n' % (salt, n))
+ addsalt(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:])
+ if inpython:
+ script.append(l[2:])
+ after.setdefault(prepos, []).append(l)
+ else:
+ # 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)
- t.close()
+ if inpython:
+ script.append("EOF\n")
+ addsalt(n + 1)
- script.append('echo %s %s $?\n' % (salt, n + 1))
-
+ # Write out the script and execute it
fd, name = tempfile.mkstemp(suffix='hg-tst')
-
try:
for l in script:
os.write(fd, l)
@@ -590,32 +645,7 @@
finally:
os.remove(name)
- def rematch(el, l):
- try:
- # ensure that the regex matches to the end of the string
- return re.match(el + r'\Z', l)
- except re.error:
- # el is an invalid regex
- return False
-
- def globmatch(el, l):
- # The only supported special characters are * and ?. Escaping is
- # supported.
- i, n = 0, len(el)
- res = ''
- while i < n:
- c = el[i]
- i += 1
- if c == '\\' and el[i] in '*?\\':
- res += el[i - 1:i + 1]
- i += 1
- elif c == '*':
- res += '.*'
- elif c == '?':
- res += '.'
- else:
- res += re.escape(c)
- return rematch(res, l)
+ # Merge the script output back into a unified test
pos = -1
postout = []
@@ -627,20 +657,16 @@
if lout:
if lcmd:
+ # output block had no trailing newline, clean up
lout += ' (no-eol)\n'
+ # find the expected output at the current position
el = None
if pos in expected and expected[pos]:
el = expected[pos].pop(0)
- if el == lout: # perfect match (fast)
- postout.append(" " + lout)
- elif (el and
- (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', lout) or
- el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', lout)
- or el.endswith(" (esc)\n") and
- el.decode('string-escape') == l)):
- postout.append(" " + el) # fallback regex/glob/esc match
+ if linematch(el, lout):
+ postout.append(" " + el)
else:
if needescape(lout):
lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
@@ -652,6 +678,7 @@
if ret != 0:
postout.append(" [%s]\n" % ret)
if pos in after:
+ # merge in non-active test bits
postout += after.pop(pos)
pos = int(lcmd.split()[0])
@@ -853,7 +880,7 @@
refout = None # to match "out is None"
elif os.path.exists(ref):
f = open(ref, "r")
- refout = list(transformtst(splitnewlines(f.read())))
+ refout = list(splitnewlines(f.read()))
f.close()
else:
refout = []