tests/run-tests.py
changeset 1004 b675519e2a5b
parent 1003 59bd7f017103
child 1005 d86b26020b48
equal deleted inserted replaced
997:5c25b505ffc1 1004:b675519e2a5b
   519 def escapef(m):
   519 def escapef(m):
   520     return escapemap[m.group(0)]
   520     return escapemap[m.group(0)]
   521 def stringescape(s):
   521 def stringescape(s):
   522     return escapesub(escapef, s)
   522     return escapesub(escapef, s)
   523 
   523 
   524 def transformtst(lines):
   524 def rematch(el, l):
   525     inblock = False
   525     try:
   526     for l in lines:
   526         # ensure that the regex matches to the end of the string
   527         if inblock:
   527         return re.match(el + r'\Z', l)
   528             if l.startswith('  $ ') or not l.startswith('  '):
   528     except re.error:
   529                 inblock = False
   529         # el is an invalid regex
   530                 yield '  > EOF\n'
   530         return False
   531                 yield l
   531 
   532             else:
   532 def globmatch(el, l):
   533                 yield '  > ' + l[2:]
   533     # The only supported special characters are * and ?. Escaping is
       
   534     # supported.
       
   535     i, n = 0, len(el)
       
   536     res = ''
       
   537     while i < n:
       
   538         c = el[i]
       
   539         i += 1
       
   540         if c == '\\' and el[i] in '*?\\':
       
   541             res += el[i - 1:i + 1]
       
   542             i += 1
       
   543         elif c == '*':
       
   544             res += '.*'
       
   545         elif c == '?':
       
   546             res += '.'
   534         else:
   547         else:
   535             if l.startswith('  >>> '):
   548             res += re.escape(c)
   536                 inblock = True
   549     return rematch(res, l)
   537                 yield '  $ %s -m heredoctest <<EOF\n' % PYTHON
   550 
   538                 yield '  > ' + l[2:]
   551 def linematch(el, l):
   539             else:
   552     if el == l: # perfect match (fast)
   540                 yield l
   553         return True
   541     if inblock:
   554     if (el and
   542         yield '  > EOF\n'
   555         (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', l) or
       
   556          el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', l) or
       
   557          el.endswith(" (esc)\n") and el.decode('string-escape') == l)):
       
   558         return True
       
   559     return False
   543 
   560 
   544 def tsttest(test, wd, options, replacements):
   561 def tsttest(test, wd, options, replacements):
   545     t = open(test)
   562     # We generate a shell script which outputs unique markers to line
   546     out = []
   563     # up script results with our source. These markers include input
       
   564     # line number and the last return code
       
   565     salt = "SALT" + str(time.time())
       
   566     def addsalt(line):
       
   567         script.append('echo %s %s $?\n' % (salt, line))
       
   568 
       
   569     # After we run the shell script, we re-unify the script output
       
   570     # with non-active parts of the source, with synchronization by our
       
   571     # SALT line number markers. The after table contains the
       
   572     # non-active components, ordered by line number
       
   573     after = {}
       
   574     pos = prepos = -1
       
   575 
       
   576     # Expected shellscript output
       
   577     expected = {}
       
   578 
       
   579     # We keep track of whether or not we're in a Python block so we
       
   580     # can generate the surrounding doctest magic
       
   581     inpython = False
       
   582 
       
   583     f = open(test)
       
   584     t = f.readlines()
       
   585     f.close()
       
   586 
   547     script = []
   587     script = []
   548     salt = "SALT" + str(time.time())
   588     for n, l in enumerate(t):
   549 
       
   550     pos = prepos = -1
       
   551     after = {}
       
   552     expected = {}
       
   553     for n, l in enumerate(transformtst(t)):
       
   554         if not l.endswith('\n'):
   589         if not l.endswith('\n'):
   555             l += '\n'
   590             l += '\n'
   556         if l.startswith('  $ '): # commands
   591         if l.startswith('  >>> '): # python inlines
       
   592             if not inpython:
       
   593                 # we've just entered a Python block, add the header
       
   594                 inpython = True
       
   595                 addsalt(n)
       
   596                 script.append('%s -m heredoctest <<EOF\n' % PYTHON)
       
   597                 prepos = pos
       
   598                 pos = n
       
   599             after.setdefault(prepos, []).append(l)
       
   600             script.append(l[2:])
       
   601         elif l.startswith('  $ '): # commands
       
   602             if inpython:
       
   603                 script.append("EOF\n")
       
   604                 inpython = False
   557             after.setdefault(pos, []).append(l)
   605             after.setdefault(pos, []).append(l)
   558             prepos = pos
   606             prepos = pos
   559             pos = n
   607             pos = n
   560             script.append('echo %s %s $?\n' % (salt, n))
   608             addsalt(n)
   561             script.append(l[4:])
   609             script.append(l[4:])
   562         elif l.startswith('  > '): # continuations
   610         elif l.startswith('  > '): # continuations
   563             after.setdefault(prepos, []).append(l)
   611             after.setdefault(prepos, []).append(l)
   564             script.append(l[4:])
   612             script.append(l[4:])
   565         elif l.startswith('  '): # results
   613         elif l.startswith('  '): # results
   566             # queue up a list of expected results
   614             if inpython:
   567             expected.setdefault(pos, []).append(l[2:])
   615                 script.append(l[2:])
       
   616                 after.setdefault(prepos, []).append(l)
       
   617             else:
       
   618                 # queue up a list of expected results
       
   619                 expected.setdefault(pos, []).append(l[2:])
   568         else:
   620         else:
       
   621             if inpython:
       
   622                 script.append("EOF\n")
       
   623                 inpython = False
   569             # non-command/result - queue up for merged output
   624             # non-command/result - queue up for merged output
   570             after.setdefault(pos, []).append(l)
   625             after.setdefault(pos, []).append(l)
   571 
   626 
   572     t.close()
   627     if inpython:
   573 
   628         script.append("EOF\n")
   574     script.append('echo %s %s $?\n' % (salt, n + 1))
   629     addsalt(n + 1)
   575 
   630 
       
   631     # Write out the script and execute it
   576     fd, name = tempfile.mkstemp(suffix='hg-tst')
   632     fd, name = tempfile.mkstemp(suffix='hg-tst')
   577 
       
   578     try:
   633     try:
   579         for l in script:
   634         for l in script:
   580             os.write(fd, l)
   635             os.write(fd, l)
   581         os.close(fd)
   636         os.close(fd)
   582 
   637 
   588         if exitcode == SKIPPED_STATUS or output is None:
   643         if exitcode == SKIPPED_STATUS or output is None:
   589             return exitcode, output
   644             return exitcode, output
   590     finally:
   645     finally:
   591         os.remove(name)
   646         os.remove(name)
   592 
   647 
   593     def rematch(el, l):
   648     # Merge the script output back into a unified test
   594         try:
       
   595             # ensure that the regex matches to the end of the string
       
   596             return re.match(el + r'\Z', l)
       
   597         except re.error:
       
   598             # el is an invalid regex
       
   599             return False
       
   600 
       
   601     def globmatch(el, l):
       
   602         # The only supported special characters are * and ?. Escaping is
       
   603         # supported.
       
   604         i, n = 0, len(el)
       
   605         res = ''
       
   606         while i < n:
       
   607             c = el[i]
       
   608             i += 1
       
   609             if c == '\\' and el[i] in '*?\\':
       
   610                 res += el[i - 1:i + 1]
       
   611                 i += 1
       
   612             elif c == '*':
       
   613                 res += '.*'
       
   614             elif c == '?':
       
   615                 res += '.'
       
   616             else:
       
   617                 res += re.escape(c)
       
   618         return rematch(res, l)
       
   619 
   649 
   620     pos = -1
   650     pos = -1
   621     postout = []
   651     postout = []
   622     ret = 0
   652     ret = 0
   623     for n, l in enumerate(output):
   653     for n, l in enumerate(output):
   625         if salt in l:
   655         if salt in l:
   626             lout, lcmd = l.split(salt, 1)
   656             lout, lcmd = l.split(salt, 1)
   627 
   657 
   628         if lout:
   658         if lout:
   629             if lcmd:
   659             if lcmd:
       
   660                 # output block had no trailing newline, clean up
   630                 lout += ' (no-eol)\n'
   661                 lout += ' (no-eol)\n'
   631 
   662 
       
   663             # find the expected output at the current position
   632             el = None
   664             el = None
   633             if pos in expected and expected[pos]:
   665             if pos in expected and expected[pos]:
   634                 el = expected[pos].pop(0)
   666                 el = expected[pos].pop(0)
   635 
   667 
   636             if el == lout: # perfect match (fast)
   668             if linematch(el, lout):
   637                 postout.append("  " + lout)
   669                 postout.append("  " + el)
   638             elif (el and
       
   639                   (el.endswith(" (re)\n") and rematch(el[:-6] + '\n', lout) or
       
   640                    el.endswith(" (glob)\n") and globmatch(el[:-8] + '\n', lout)
       
   641                    or el.endswith(" (esc)\n") and
       
   642                       el.decode('string-escape') == l)):
       
   643                 postout.append("  " + el) # fallback regex/glob/esc match
       
   644             else:
   670             else:
   645                 if needescape(lout):
   671                 if needescape(lout):
   646                     lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
   672                     lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
   647                 postout.append("  " + lout) # let diff deal with it
   673                 postout.append("  " + lout) # let diff deal with it
   648 
   674 
   650             # add on last return code
   676             # add on last return code
   651             ret = int(lcmd.split()[1])
   677             ret = int(lcmd.split()[1])
   652             if ret != 0:
   678             if ret != 0:
   653                 postout.append("  [%s]\n" % ret)
   679                 postout.append("  [%s]\n" % ret)
   654             if pos in after:
   680             if pos in after:
       
   681                 # merge in non-active test bits
   655                 postout += after.pop(pos)
   682                 postout += after.pop(pos)
   656             pos = int(lcmd.split()[0])
   683             pos = int(lcmd.split()[0])
   657 
   684 
   658     if pos in after:
   685     if pos in after:
   659         postout += after.pop(pos)
   686         postout += after.pop(pos)
   851     # check test output against it.
   878     # check test output against it.
   852     if options.debug:
   879     if options.debug:
   853         refout = None                   # to match "out is None"
   880         refout = None                   # to match "out is None"
   854     elif os.path.exists(ref):
   881     elif os.path.exists(ref):
   855         f = open(ref, "r")
   882         f = open(ref, "r")
   856         refout = list(transformtst(splitnewlines(f.read())))
   883         refout = list(splitnewlines(f.read()))
   857         f.close()
   884         f.close()
   858     else:
   885     else:
   859         refout = []
   886         refout = []
   860 
   887 
   861     if (ret != 0 or out != refout) and not skipped and not options.debug:
   888     if (ret != 0 or out != refout) and not skipped and not options.debug: