testtools-dev team mailing list archive
-
testtools-dev team
-
Mailing list archive
-
Message #00353
[Merge] lp:~lifeless/testtools/bug-683332 into lp:testtools
Robert Collins has proposed merging lp:~lifeless/testtools/bug-683332 into lp:testtools.
Requested reviews:
testtools developers (testtools-dev)
Related bugs:
#683332 ExtendedToOriginalDecorator no longer decorates wasSuccessful
https://bugs.launchpad.net/bugs/683332
Fix bug. Yay.
--
https://code.launchpad.net/~lifeless/testtools/bug-683332/+merge/42751
Your team testtools developers is requested to review the proposed merge of lp:~lifeless/testtools/bug-683332 into lp:testtools.
=== modified file 'NEWS'
--- NEWS 2010-11-29 23:46:10 +0000
+++ NEWS 2010-12-04 21:08:52 +0000
@@ -10,10 +10,18 @@
* addUnexpectedSuccess is translated to addFailure for test results that don't
know about addUnexpectedSuccess. Further, it fails the entire result for
all testtools TestResults (i.e. wasSuccessful() returns False after
- addUnexpectedSuccess has been called). (Jonathan Lange, #654474)
+ addUnexpectedSuccess has been called). Note that when using a delegating
+ result such as ThreadsafeForwardingResult, MultiTestResult or
+ ExtendedToOriginalDecorator then the behaviour of addUnexpectedSuccess is
+ determined by the delegated to result(s).
+ (Jonathan Lange, Robert Collins, #654474, #683332)
* startTestRun will reset any errors on the result. That is, wasSuccessful()
- will always return True immediately after startTestRun() is called.
+ will always return True immediately after startTestRun() is called. This
+ only applies to delegated test results (ThreadsafeForwardingResult,
+ MultiTestResult and ExtendedToOriginalDecorator) if the delegated to result
+ is a testtools test result - we cannot reliably reset the state of unknown
+ test result class instances. (Jonathan Lange, Robert Collins, #683332)
* Responsibility for running test cleanups has been moved to ``RunTest``.
This change does not affect public APIs and can be safely ignored by test
=== modified file 'testtools/testresult/real.py'
--- testtools/testresult/real.py 2010-11-30 12:54:20 +0000
+++ testtools/testresult/real.py 2010-12-04 21:08:52 +0000
@@ -35,13 +35,11 @@
"""
def __init__(self):
- super(TestResult, self).__init__()
- self.skip_reasons = {}
- self.__now = None
- # -- Start: As per python 2.7 --
- self.expectedFailures = []
- self.unexpectedSuccesses = []
- # -- End: As per python 2.7 --
+ # startTestRun resets all attributes, and older clients don't know to
+ # call startTestRun, so it is called once here.
+ # Because subclasses may reasonably not expect this, we call the
+ # specific version we want to run.
+ TestResult.startTestRun(self)
def addExpectedFailure(self, test, err=None, details=None):
"""Called when a test has failed in an expected manner.
@@ -160,9 +158,13 @@
New in python 2.7
"""
+ super(TestResult, self).__init__()
+ self.skip_reasons = {}
+ self.__now = None
+ # -- Start: As per python 2.7 --
+ self.expectedFailures = []
self.unexpectedSuccesses = []
- self.errors = []
- self.failures = []
+ # -- End: As per python 2.7 --
def stopTestRun(self):
"""Called after a test run completes
@@ -406,13 +408,11 @@
def __init__(self, decorated):
self.decorated = decorated
- self._was_successful = True
def __getattr__(self, name):
return getattr(self.decorated, name)
def addError(self, test, err=None, details=None):
- self._was_successful = False
self._check_args(err, details)
if details is not None:
try:
@@ -437,7 +437,6 @@
return addExpectedFailure(test, err)
def addFailure(self, test, err=None, details=None):
- self._was_successful = False
self._check_args(err, details)
if details is not None:
try:
@@ -464,7 +463,6 @@
return addSkip(test, reason)
def addUnexpectedSuccess(self, test, details=None):
- self._was_successful = False
outcome = getattr(self.decorated, 'addUnexpectedSuccess', None)
if outcome is None:
try:
@@ -521,7 +519,6 @@
return self.decorated.startTest(test)
def startTestRun(self):
- self._was_successful = True
try:
return self.decorated.startTestRun()
except AttributeError:
@@ -552,7 +549,7 @@
return method(a_datetime)
def wasSuccessful(self):
- return self._was_successful
+ return self.decorated.wasSuccessful()
class _StringException(Exception):
=== modified file 'testtools/tests/test_testresult.py'
--- testtools/tests/test_testresult.py 2010-11-29 00:32:53 +0000
+++ testtools/tests/test_testresult.py 2010-12-04 21:08:52 +0000
@@ -132,7 +132,7 @@
result.stopTestRun()
-class TestResultContract(Python27Contract):
+class DetailsContract(Python27Contract):
"""Tests for the contract of TestResults."""
def test_addExpectedFailure_details(self):
@@ -171,6 +171,13 @@
result.startTest(self)
result.addSuccess(self, details={})
+
+class FallbackContract(DetailsContract):
+ """When we fallback we take our policy choice to map calls.
+
+ For instance, we map unexpectedSuccess to an error code, not to success.
+ """
+
def test_addUnexpectedSuccess_was_successful(self):
# addUnexpectedSuccess fails test run in testtools.
result = self.makeResult()
@@ -179,6 +186,14 @@
result.stopTest(self)
self.assertFalse(result.wasSuccessful())
+
+class StartTestRunContract(FallbackContract):
+ """Defines the contract for testtools policy choices.
+
+ That is things which are not simply extensions to unittest but choices we
+ have made differently.
+ """
+
def test_startTestRun_resets_unexpected_success(self):
result = self.makeResult()
result.startTest(self)
@@ -203,25 +218,26 @@
result.startTestRun()
self.assertTrue(result.wasSuccessful())
-class TestTestResultContract(TestCase, TestResultContract):
+
+class TestTestResultContract(TestCase, StartTestRunContract):
def makeResult(self):
return TestResult()
-class TestMultiTestResultContract(TestCase, TestResultContract):
+class TestMultiTestResultContract(TestCase, StartTestRunContract):
def makeResult(self):
return MultiTestResult(TestResult(), TestResult())
-class TestTextTestResultContract(TestCase, TestResultContract):
+class TestTextTestResultContract(TestCase, StartTestRunContract):
def makeResult(self):
return TextTestResult(StringIO())
-class TestThreadSafeForwardingResultContract(TestCase, TestResultContract):
+class TestThreadSafeForwardingResultContract(TestCase, StartTestRunContract):
def makeResult(self):
result_semaphore = threading.Semaphore(1)
@@ -229,7 +245,7 @@
return ThreadsafeForwardingResult(target, result_semaphore)
-class TestExtendedTestResultContract(TestCase, TestResultContract):
+class TestExtendedTestResultContract(TestCase, StartTestRunContract):
def makeResult(self):
return ExtendedTestResult()
@@ -241,7 +257,7 @@
return Python26TestResult()
-class TestAdaptedPython26TestResultContract(TestCase, TestResultContract):
+class TestAdaptedPython26TestResultContract(TestCase, FallbackContract):
def makeResult(self):
return ExtendedToOriginalDecorator(Python26TestResult())
@@ -253,7 +269,7 @@
return Python27TestResult()
-class TestAdaptedPython27TestResultContract(TestCase, TestResultContract):
+class TestAdaptedPython27TestResultContract(TestCase, DetailsContract):
def makeResult(self):
return ExtendedToOriginalDecorator(Python27TestResult())
Follow ups