=== modified file 'NEWS' --- NEWS 2009-08-26 07:02:45 +0000 +++ NEWS 2009-08-27 03:51:02 +0000 @@ -79,6 +79,9 @@ classes changed to manage lock lifetime of the trees they open in a way consistent with reader-exclusive locks. (Robert Collins, #305006) +* ``bzrlib.tests`` now uses ``stopTestRun`` for its ``TestResult`` + subclasses - the same as python's unittest module. (Robert Collins) + Internals ********* === modified file 'bzrlib/tests/__init__.py' --- bzrlib/tests/__init__.py 2009-08-26 07:02:45 +0000 +++ bzrlib/tests/__init__.py 2009-08-27 03:51:02 +0000 @@ -175,17 +175,47 @@ self._overall_start_time = time.time() self._strict = strict - def done(self): - # nb: called stopTestRun in the version of this that Python merged - # upstream, according to lifeless 20090803 + def stopTestRun(self): + run = self.testsRun + actionTaken = "Ran" + stopTime = time.time() + timeTaken = stopTime - self.startTime + self.printErrors() + self.stream.writeln(self.separator2) + self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken, + run, run != 1 and "s" or "", timeTaken)) + self.stream.writeln() + if not self.wasSuccessful(): + self.stream.write("FAILED (") + failed, errored = map(len, (self.failures, self.errors)) + if failed: + self.stream.write("failures=%d" % failed) + if errored: + if failed: self.stream.write(", ") + self.stream.write("errors=%d" % errored) + if self.known_failure_count: + if failed or errored: self.stream.write(", ") + self.stream.write("known_failure_count=%d" % + self.known_failure_count) + self.stream.writeln(")") + else: + if self.known_failure_count: + self.stream.writeln("OK (known_failures=%d)" % + self.known_failure_count) + else: + self.stream.writeln("OK") + if self.skip_count > 0: + skipped = self.skip_count + self.stream.writeln('%d test%s skipped' % + (skipped, skipped != 1 and "s" or "")) + if self.unsupported: + for feature, count in sorted(self.unsupported.items()): + self.stream.writeln("Missing feature '%s' skipped %d tests." % + (feature, count)) if self._strict: ok = self.wasStrictlySuccessful() else: ok = self.wasSuccessful() - if ok: - self.stream.write('tests passed\n') - else: - self.stream.write('tests failed\n') if TestCase._first_thread_leaker_id: self.stream.write( '%s is leaking threads among %d leaking tests.\n' % ( @@ -383,12 +413,12 @@ else: raise errors.BzrError("Unknown whence %r" % whence) - def finished(self): - pass - def report_cleaning_up(self): pass + def startTestRun(self): + self.startTime = time.time() + def report_success(self, test): pass @@ -421,15 +451,14 @@ self.pb.update_latency = 0 self.pb.show_transport_activity = False - def done(self): + def stopTestRun(self): # called when the tests that are going to run have run self.pb.clear() - super(TextTestResult, self).done() - - def finished(self): self.pb.finished() + super(TextTestResult, self).stopTestRun() - def report_starting(self): + def startTestRun(self): + super(TextTestResult, self).startTestRun() self.pb.update('[test 0/%d] Starting' % (self.num_tests)) def printErrors(self): @@ -514,7 +543,8 @@ result = a_string return result.ljust(final_width) - def report_starting(self): + def startTestRun(self): + super(VerboseTestResult, self).startTestRun() self.stream.write('running %d tests...\n' % self.num_tests) def report_test_start(self, test): @@ -578,7 +608,6 @@ descriptions=0, verbosity=1, bench_history=None, - list_only=False, strict=False, result_decorators=None, ): @@ -593,85 +622,43 @@ self.descriptions = descriptions self.verbosity = verbosity self._bench_history = bench_history - self.list_only = list_only self._strict = strict self._result_decorators = result_decorators or [] def run(self, test): "Run the given test case or test suite." - startTime = time.time() if self.verbosity == 1: result_class = TextTestResult elif self.verbosity >= 2: result_class = VerboseTestResult - result = result_class(self.stream, + original_result = result_class(self.stream, self.descriptions, self.verbosity, bench_history=self._bench_history, strict=self._strict, ) - run_result = result + # Signal to result objects that look at stop early policy to stop, + original_result.stop_early = self.stop_on_failure + result = original_result for decorator in self._result_decorators: - run_result = decorator(run_result) - result.stop_early = self.stop_on_failure - result.report_starting() - if self.list_only: - if self.verbosity >= 2: - self.stream.writeln("Listing tests only ...\n") - run = 0 - for t in iter_suite_tests(test): - self.stream.writeln("%s" % (t.id())) - run += 1 - return None - else: - try: - import testtools - except ImportError: - test.run(run_result) - else: - if isinstance(test, testtools.ConcurrentTestSuite): - # We need to catch bzr specific behaviors - test.run(BZRTransformingResult(run_result)) - else: - test.run(run_result) - run = result.testsRun - actionTaken = "Ran" - stopTime = time.time() - timeTaken = stopTime - startTime - result.printErrors() - self.stream.writeln(result.separator2) - self.stream.writeln("%s %d test%s in %.3fs" % (actionTaken, - run, run != 1 and "s" or "", timeTaken)) - self.stream.writeln() - if not result.wasSuccessful(): - self.stream.write("FAILED (") - failed, errored = map(len, (result.failures, result.errors)) - if failed: - self.stream.write("failures=%d" % failed) - if errored: - if failed: self.stream.write(", ") - self.stream.write("errors=%d" % errored) - if result.known_failure_count: - if failed or errored: self.stream.write(", ") - self.stream.write("known_failure_count=%d" % - result.known_failure_count) - self.stream.writeln(")") - else: - if result.known_failure_count: - self.stream.writeln("OK (known_failures=%d)" % - result.known_failure_count) - else: - self.stream.writeln("OK") - if result.skip_count > 0: - skipped = result.skip_count - self.stream.writeln('%d test%s skipped' % - (skipped, skipped != 1 and "s" or "")) - if result.unsupported: - for feature, count in sorted(result.unsupported.items()): - self.stream.writeln("Missing feature '%s' skipped %d tests." % - (feature, count)) - result.finished() - return result + result = decorator(result) + result.stop_early = self.stop_on_failure + try: + import testtools + except ImportError: + pass + else: + if isinstance(test, testtools.ConcurrentTestSuite): + # We need to catch bzr specific behaviors + result = BZRTransformingResult(result) + result.startTestRun() + try: + test.run(result) + finally: + result.stopTestRun() + # higher level code uses our extended protocol to determine + # what exit code to give. + return original_result def iter_suite_tests(suite): @@ -2807,7 +2794,6 @@ descriptions=0, verbosity=verbosity, bench_history=bench_history, - list_only=list_only, strict=strict, result_decorators=result_decorators, ) @@ -2830,10 +2816,15 @@ decorators.append(CountingDecorator) for decorator in decorators: suite = decorator(suite) - result = runner.run(suite) if list_only: + # Done after test suite decoration to allow randomisation etc + # to take effect, though that is of marginal benefit. + if verbosity >= 2: + stream.write("Listing tests only ...\n") + for t in iter_suite_tests(suite): + stream.write("%s\n" % (t.id())) return True - result.done() + result = runner.run(suite) if strict: return result.wasStrictlySuccessful() else: @@ -3168,6 +3159,12 @@ def stopTest(self, test): self.result.stopTest(test) + def startTestRun(self): + self.result.startTestRun() + + def stopTestRun(self): + self.result.stopTestRun() + def addSkip(self, test, reason): self.result.addSkip(test, reason) === modified file 'bzrlib/tests/test_selftest.py' --- bzrlib/tests/test_selftest.py 2009-08-26 06:33:13 +0000 +++ bzrlib/tests/test_selftest.py 2009-08-27 03:51:02 +0000 @@ -820,7 +820,7 @@ def test_known_failure(self): """A KnownFailure being raised should trigger several result actions.""" class InstrumentedTestResult(tests.ExtendedTestResult): - def done(self): pass + def stopTestRun(self): pass def startTests(self): pass def report_test_start(self, test): pass def report_known_failure(self, test, err): @@ -874,7 +874,7 @@ def test_add_not_supported(self): """Test the behaviour of invoking addNotSupported.""" class InstrumentedTestResult(tests.ExtendedTestResult): - def done(self): pass + def stopTestRun(self): pass def startTests(self): pass def report_test_start(self, test): pass def report_unsupported(self, test, feature): @@ -918,7 +918,7 @@ def test_unavailable_exception(self): """An UnavailableFeature being raised should invoke addNotSupported.""" class InstrumentedTestResult(tests.ExtendedTestResult): - def done(self): pass + def stopTestRun(self): pass def startTests(self): pass def report_test_start(self, test): pass def addNotSupported(self, test, feature): @@ -1001,11 +1001,14 @@ because of our use of global state. """ old_root = tests.TestCaseInTempDir.TEST_ROOT + old_leak = tests.TestCase._first_thread_leaker_id try: tests.TestCaseInTempDir.TEST_ROOT = None + tests.TestCase._first_thread_leaker_id = None return testrunner.run(test) finally: tests.TestCaseInTempDir.TEST_ROOT = old_root + tests.TestCase._first_thread_leaker_id = old_leak def test_known_failure_failed_run(self): # run a test that generates a known failure which should be printed in @@ -1291,6 +1294,34 @@ self.assertContainsRe(log, 'this will be kept') self.assertEqual(log, test._log_contents) + def test_startTestRun(self): + """run should call result.startTestRun()""" + calls = [] + class LoggingDecorator(tests.ForwardingResult): + def startTestRun(self): + tests.ForwardingResult.startTestRun(self) + calls.append('startTestRun') + test = unittest.FunctionTestCase(lambda:None) + stream = StringIO() + runner = tests.TextTestRunner(stream=stream, + result_decorators=[LoggingDecorator]) + result = self.run_test_runner(runner, test) + self.assertLength(1, calls) + + def test_stopTestRun(self): + """run should call result.stopTestRun()""" + calls = [] + class LoggingDecorator(tests.ForwardingResult): + def stopTestRun(self): + tests.ForwardingResult.stopTestRun(self) + calls.append('stopTestRun') + test = unittest.FunctionTestCase(lambda:None) + stream = StringIO() + runner = tests.TextTestRunner(stream=stream, + result_decorators=[LoggingDecorator]) + result = self.run_test_runner(runner, test) + self.assertLength(1, calls) + class SampleTestCase(tests.TestCase): @@ -2934,19 +2965,3 @@ self.verbosity) tests.run_suite(suite, runner_class=MyRunner, stream=StringIO()) self.assertLength(1, calls) - - def test_done(self): - """run_suite should call result.done()""" - self.calls = 0 - def one_more_call(): self.calls += 1 - def test_function(): - pass - test = unittest.FunctionTestCase(test_function) - class InstrumentedTestResult(tests.ExtendedTestResult): - def done(self): one_more_call() - class MyRunner(tests.TextTestRunner): - def run(self, test): - return InstrumentedTestResult(self.stream, self.descriptions, - self.verbosity) - tests.run_suite(test, runner_class=MyRunner, stream=StringIO()) - self.assertEquals(1, self.calls)