RegressTests.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. #!/usr/bin/env python
  2. import sys
  3. import os
  4. import re
  5. import tempfile
  6. multiproc = False
  7. try:
  8. from multiprocessing import Pool
  9. multiproc = True
  10. except:
  11. pass
  12. from string import join
  13. from difflib import unified_diff
  14. from LedgerHarness import LedgerHarness
  15. args = sys.argv
  16. jobs = 1
  17. match = re.match('-j([0-9]+)?', args[1])
  18. if match:
  19. args = [args[0]] + args[2:]
  20. if match.group(1):
  21. jobs = int(match.group(1))
  22. if jobs == 1:
  23. multiproc = False
  24. harness = LedgerHarness(args)
  25. tests = args[3]
  26. if not os.path.isdir(tests) and not os.path.isfile(tests):
  27. sys.stderr.write("'%s' is not a directory or file (cwd %s)" %
  28. (tests, os.getcwd()))
  29. sys.exit(1)
  30. class RegressFile(object):
  31. def __init__(self, filename):
  32. self.filename = filename
  33. self.fd = open(self.filename)
  34. def transform_line(self, line):
  35. line = re.sub('\$sourcepath', harness.sourcepath, line)
  36. line = re.sub('\$FILE', os.path.abspath(self.filename), line)
  37. return line
  38. def read_test(self):
  39. test = {
  40. 'command': None,
  41. 'output': None,
  42. 'error': None,
  43. 'exitcode': 0
  44. }
  45. in_output = False
  46. in_error = False
  47. line = self.fd.readline()
  48. #print "line =", line
  49. while line:
  50. if line.startswith("test "):
  51. command = line[5:]
  52. match = re.match('(.*) -> ([0-9]+)', command)
  53. if match:
  54. test['command'] = self.transform_line(match.group(1))
  55. test['exitcode'] = int(match.group(2))
  56. else:
  57. test['command'] = command
  58. in_output = True
  59. elif in_output:
  60. if line.startswith("end test"):
  61. in_output = in_error = False
  62. break
  63. elif in_error:
  64. if test['error'] is None:
  65. test['error'] = []
  66. test['error'].append(self.transform_line(line))
  67. else:
  68. if line.startswith("__ERROR__"):
  69. in_error = True
  70. else:
  71. if test['output'] is None:
  72. test['output'] = []
  73. test['output'].append(self.transform_line(line))
  74. line = self.fd.readline()
  75. #print "line =", line
  76. return test['command'] and test
  77. def notify_user(self, msg, test):
  78. print msg
  79. print "--"
  80. print self.transform_line(test['command']),
  81. print "--"
  82. def run_test(self, test):
  83. use_stdin = False
  84. if test['command'].find("-f ") != -1:
  85. test['command'] = '$ledger ' + test['command']
  86. if re.search("-f (-|/dev/stdin)(\s|$)", test['command']):
  87. use_stdin = True
  88. else:
  89. test['command'] = (('$ledger -f "%s" ' %
  90. os.path.abspath(self.filename)) +
  91. test['command'])
  92. p = harness.run(test['command'],
  93. columns=(not re.search('--columns', test['command'])))
  94. if use_stdin:
  95. fd = open(self.filename)
  96. try:
  97. p.stdin.write(fd.read())
  98. finally:
  99. fd.close()
  100. p.stdin.close()
  101. success = True
  102. printed = False
  103. index = 0
  104. if test['output'] is not None:
  105. for line in unified_diff(test['output'], harness.readlines(p.stdout)):
  106. index += 1
  107. if index < 3:
  108. continue
  109. if not printed:
  110. if success: print
  111. self.notify_user("FAILURE in output from %s:" % self.filename, test)
  112. success = False
  113. printed = True
  114. print " ", line,
  115. printed = False
  116. index = 0
  117. if test['error'] is not None:
  118. for line in unified_diff(test['error'], harness.readlines(p.stderr)):
  119. index += 1
  120. if index < 3:
  121. continue
  122. if not printed:
  123. if success: print
  124. self.notify_user("FAILURE in error output from %s:"
  125. % self.filename, test)
  126. success = False
  127. printed = True
  128. print " ", line,
  129. if test['exitcode'] is None or test['exitcode'] == p.wait():
  130. if success:
  131. harness.success()
  132. else:
  133. harness.failure(os.path.basename(self.filename))
  134. print "STDERR:"
  135. print p.stderr.read()
  136. else:
  137. if success: print
  138. if test['exitcode']:
  139. self.notify_user("FAILURE in exit code (%d != %d) from %s:"
  140. % (test['exitcode'], p.returncode, self.filename),
  141. test)
  142. harness.failure(os.path.basename(self.filename))
  143. def run_tests(self):
  144. if os.path.getsize(self.filename) == 0:
  145. print >>sys.stderr, "WARNING: Empty testfile detected: %s" % (self.filename)
  146. harness.failure(os.path.basename(self.filename))
  147. return False
  148. test = self.read_test()
  149. while test:
  150. self.run_test(test)
  151. test = self.read_test()
  152. def close(self):
  153. self.fd.close()
  154. def do_test(path):
  155. entry = RegressFile(path)
  156. entry.run_tests()
  157. entry.close()
  158. if __name__ == '__main__':
  159. if multiproc:
  160. pool = Pool(jobs*2)
  161. else:
  162. pool = None
  163. if os.path.isdir(tests):
  164. tests = [os.path.join(tests, x)
  165. for x in os.listdir(tests)
  166. if (x.endswith('.test') and
  167. (not '_py.test' in x or (harness.python and
  168. not harness.verify)))]
  169. if pool:
  170. pool.map(do_test, tests, 1)
  171. else:
  172. map(do_test, tests)
  173. else:
  174. entry = RegressFile(tests)
  175. entry.run_tests()
  176. entry.close()
  177. if pool:
  178. pool.close()
  179. pool.join()
  180. harness.exit()