Mercurial > public > mercurial-scm > hg
comparison tests/run-tests.py @ 21521:855f092c96e5
run-tests: move diff generation into TestResult
TestResult is the thing that captures all our test results. It's logical
for diff viewing to be handled there and not inside Test.
While writing this patch, it was discovered that Test.fail() was
printing redundant test result output. This has been removed.
Arguments controlling diffs have been removed from Test.__init__.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Tue, 22 Apr 2014 13:16:34 -0700 |
parents | fa6ba4372678 |
children | eeaec308ad5f |
comparison
equal
deleted
inserted
replaced
21520:fa6ba4372678 | 21521:855f092c96e5 |
---|---|
280 for existing destination support. | 280 for existing destination support. |
281 """ | 281 """ |
282 shutil.copy(src, dst) | 282 shutil.copy(src, dst) |
283 os.remove(src) | 283 os.remove(src) |
284 | 284 |
285 def showdiff(expected, output, ref, err): | 285 def getdiff(expected, output, ref, err): |
286 print | |
287 servefail = False | 286 servefail = False |
287 lines = [] | |
288 for line in difflib.unified_diff(expected, output, ref, err): | 288 for line in difflib.unified_diff(expected, output, ref, err): |
289 sys.stdout.write(line) | 289 lines.append(line) |
290 if not servefail and line.startswith( | 290 if not servefail and line.startswith( |
291 '+ abort: child process failed to start'): | 291 '+ abort: child process failed to start'): |
292 servefail = True | 292 servefail = True |
293 return {'servefail': servefail} | 293 |
294 | 294 return servefail, lines |
295 | 295 |
296 verbose = False | 296 verbose = False |
297 def vlog(*msg): | 297 def vlog(*msg): |
298 if verbose is not False: | 298 if verbose is not False: |
299 iolock.acquire() | 299 iolock.acquire() |
337 | 337 |
338 # Status code reserved for skipped tests (used by hghave). | 338 # Status code reserved for skipped tests (used by hghave). |
339 SKIPPED_STATUS = 80 | 339 SKIPPED_STATUS = 80 |
340 | 340 |
341 def __init__(self, path, tmpdir, keeptmpdir=False, | 341 def __init__(self, path, tmpdir, keeptmpdir=False, |
342 debug=False, nodiff=False, diffviewer=None, | 342 debug=False, |
343 interactive=False, timeout=defaults['timeout'], | 343 interactive=False, timeout=defaults['timeout'], |
344 startport=defaults['port'], extraconfigopts=None, | 344 startport=defaults['port'], extraconfigopts=None, |
345 py3kwarnings=False, shell=None): | 345 py3kwarnings=False, shell=None): |
346 """Create a test from parameters. | 346 """Create a test from parameters. |
347 | 347 |
353 after execution. It defaults to removal (False). | 353 after execution. It defaults to removal (False). |
354 | 354 |
355 debug mode will make the test execute verbosely, with unfiltered | 355 debug mode will make the test execute verbosely, with unfiltered |
356 output. | 356 output. |
357 | 357 |
358 nodiff will suppress the printing of a diff when output changes. | |
359 | |
360 diffviewer is the program that should be used to display diffs. Only | |
361 used when output changes. | |
362 | |
363 interactive controls whether the test will run interactively. | 358 interactive controls whether the test will run interactively. |
364 | 359 |
365 timeout controls the maximum run time of the test. It is ignored when | 360 timeout controls the maximum run time of the test. It is ignored when |
366 debug is True. | 361 debug is True. |
367 | 362 |
385 self.errpath = os.path.join(self._testdir, '%s.err' % self.name) | 380 self.errpath = os.path.join(self._testdir, '%s.err' % self.name) |
386 | 381 |
387 self._threadtmp = tmpdir | 382 self._threadtmp = tmpdir |
388 self._keeptmpdir = keeptmpdir | 383 self._keeptmpdir = keeptmpdir |
389 self._debug = debug | 384 self._debug = debug |
390 self._nodiff = nodiff | |
391 self._diffviewer = diffviewer | |
392 self._interactive = interactive | 385 self._interactive = interactive |
393 self._timeout = timeout | 386 self._timeout = timeout |
394 self._startport = startport | 387 self._startport = startport |
395 self._extraconfigopts = extraconfigopts or [] | 388 self._extraconfigopts = extraconfigopts or [] |
396 self._py3kwarnings = py3kwarnings | 389 self._py3kwarnings = py3kwarnings |
406 | 399 |
407 # If we're not in --debug mode and reference output file exists, | 400 # If we're not in --debug mode and reference output file exists, |
408 # check test output against it. | 401 # check test output against it. |
409 if debug: | 402 if debug: |
410 self._refout = None # to match "out is None" | 403 self._refout = None # to match "out is None" |
411 elif os.path.exists(self._refpath): | 404 elif os.path.exists(self.refpath): |
412 f = open(self._refpath, 'r') | 405 f = open(self.refpath, 'r') |
413 self._refout = f.read().splitlines(True) | 406 self._refout = f.read().splitlines(True) |
414 f.close() | 407 f.close() |
415 else: | 408 else: |
416 self._refout = [] | 409 self._refout = [] |
417 | 410 |
441 # Remove any previous output files. | 434 # Remove any previous output files. |
442 if os.path.exists(self.errpath): | 435 if os.path.exists(self.errpath): |
443 os.remove(self.errpath) | 436 os.remove(self.errpath) |
444 | 437 |
445 def run(self, result): | 438 def run(self, result): |
439 self._result = result | |
446 result.startTest(self) | 440 result.startTest(self) |
447 try: | 441 try: |
448 try: | 442 try: |
449 self.setUp() | 443 self.setUp() |
450 except (KeyboardInterrupt, SystemExit): | 444 except (KeyboardInterrupt, SystemExit): |
531 self._skipped = True | 525 self._skipped = True |
532 raise SkipTest(missing[-1]) | 526 raise SkipTest(missing[-1]) |
533 elif ret == 'timeout': | 527 elif ret == 'timeout': |
534 self.fail('timed out', ret) | 528 self.fail('timed out', ret) |
535 elif out != self._refout: | 529 elif out != self._refout: |
536 info = {} | 530 # The result object handles diff calculation for us. |
537 if not self._nodiff: | 531 self._result.addOutputMismatch(self, out, self._refout) |
538 iolock.acquire() | 532 |
539 if self._diffviewer: | |
540 os.system("%s %s %s" % (self._diffviewer, self._refpath, | |
541 self.errpath)) | |
542 else: | |
543 info = showdiff(self._refout, out, self._refpath, | |
544 self.errpath) | |
545 iolock.release() | |
546 msg = '' | |
547 if info.get('servefail'): | |
548 msg += 'serve failed and ' | |
549 if ret: | 533 if ret: |
550 msg += 'output changed and ' + describe(ret) | 534 msg = 'output changed and ' + describe(ret) |
551 else: | 535 else: |
552 msg += 'output changed' | 536 msg = 'output changed' |
553 | 537 |
554 if (ret != 0 or out != self._refout) and not self._skipped \ | 538 if (ret != 0 or out != self._refout) and not self._skipped \ |
555 and not self._debug: | 539 and not self._debug: |
556 f = open(self.errpath, 'wb') | 540 f = open(self.errpath, 'wb') |
557 for line in out: | 541 for line in out: |
659 hgrc.write('[%s]\n%s\n' % (section, key)) | 643 hgrc.write('[%s]\n%s\n' % (section, key)) |
660 hgrc.close() | 644 hgrc.close() |
661 | 645 |
662 def fail(self, msg, ret): | 646 def fail(self, msg, ret): |
663 warned = ret is False | 647 warned = ret is False |
664 if not self._nodiff: | |
665 log("\n%s: %s %s" % (warned and 'Warning' or 'ERROR', self.name, | |
666 msg)) | |
667 if (not ret and self._interactive and | 648 if (not ret and self._interactive and |
668 os.path.exists(self.errpath)): | 649 os.path.exists(self.errpath)): |
669 iolock.acquire() | 650 iolock.acquire() |
670 print 'Accept this change? [n] ', | 651 print 'Accept this change? [n] ', |
671 answer = sys.stdin.readline().strip() | 652 answer = sys.stdin.readline().strip() |
687 | 668 |
688 class PythonTest(Test): | 669 class PythonTest(Test): |
689 """A Python-based test.""" | 670 """A Python-based test.""" |
690 | 671 |
691 @property | 672 @property |
692 def _refpath(self): | 673 def refpath(self): |
693 return os.path.join(self._testdir, '%s.out' % self.name) | 674 return os.path.join(self._testdir, '%s.out' % self.name) |
694 | 675 |
695 def _run(self, replacements, env): | 676 def _run(self, replacements, env): |
696 py3kswitch = self._py3kwarnings and ' -3' or '' | 677 py3kswitch = self._py3kwarnings and ' -3' or '' |
697 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self.path) | 678 cmd = '%s%s "%s"' % (PYTHON, py3kswitch, self.path) |
715 ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub | 696 ESCAPESUB = re.compile(r'[\x00-\x08\x0b-\x1f\\\x7f-\xff]').sub |
716 ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256)).update( | 697 ESCAPEMAP = dict((chr(i), r'\x%02x' % i) for i in range(256)).update( |
717 {'\\': '\\\\', '\r': r'\r'}) | 698 {'\\': '\\\\', '\r': r'\r'}) |
718 | 699 |
719 @property | 700 @property |
720 def _refpath(self): | 701 def refpath(self): |
721 return os.path.join(self._testdir, self.name) | 702 return os.path.join(self._testdir, self.name) |
722 | 703 |
723 def _run(self, replacements, env): | 704 def _run(self, replacements, env): |
724 f = open(self.path) | 705 f = open(self.path) |
725 lines = f.readlines() | 706 lines = f.readlines() |
1138 self.stream.writeln('warned %s' % reason) | 1119 self.stream.writeln('warned %s' % reason) |
1139 else: | 1120 else: |
1140 self.stream.write('~') | 1121 self.stream.write('~') |
1141 self.stream.flush() | 1122 self.stream.flush() |
1142 | 1123 |
1124 def addOutputMismatch(self, test, got, expected): | |
1125 """Record a mismatch in test output for a particular test.""" | |
1126 | |
1127 if self._options.nodiff: | |
1128 return | |
1129 | |
1130 if self._options.view: | |
1131 os.system("%s %s %s" % (self._view, test.refpath, test.errpath)) | |
1132 else: | |
1133 failed, lines = getdiff(expected, got, | |
1134 test.refpath, test.errpath) | |
1135 if failed: | |
1136 self.addFailure(test, 'diff generation failed') | |
1137 else: | |
1138 self.stream.write('\n') | |
1139 for line in lines: | |
1140 self.stream.write(line) | |
1141 self.stream.flush() | |
1142 | |
1143 def startTest(self, test): | 1143 def startTest(self, test): |
1144 super(TestResult, self).startTest(test) | 1144 super(TestResult, self).startTest(test) |
1145 | 1145 |
1146 self._started[test.name] = time.time() | 1146 self._started[test.name] = time.time() |
1147 | 1147 |
1524 tmpdir = os.path.join(self.hgtmp, 'child%d' % count) | 1524 tmpdir = os.path.join(self.hgtmp, 'child%d' % count) |
1525 | 1525 |
1526 return testcls(refpath, tmpdir, | 1526 return testcls(refpath, tmpdir, |
1527 keeptmpdir=self.options.keep_tmpdir, | 1527 keeptmpdir=self.options.keep_tmpdir, |
1528 debug=self.options.debug, | 1528 debug=self.options.debug, |
1529 nodiff = self.options.nodiff, | |
1530 diffviewer=self.options.view, | |
1531 interactive=self.options.interactive, | 1529 interactive=self.options.interactive, |
1532 timeout=self.options.timeout, | 1530 timeout=self.options.timeout, |
1533 startport=self.options.port + count * 3, | 1531 startport=self.options.port + count * 3, |
1534 extraconfigopts=self.options.extra_config_opt, | 1532 extraconfigopts=self.options.extra_config_opt, |
1535 py3kwarnings=self.options.py3k_warnings, | 1533 py3kwarnings=self.options.py3k_warnings, |