Support TestResults without stop/shouldStop
Twisted's IReporter contract doesn't specify `stop` or `shouldStop`. With this change, we can still meaningfully call `stop()` after adapting with `ExtendedToOriginalDecorator`.
This commit is contained in:
parent
b3394b2a5c
commit
ce009d3e86
9
NEWS
9
NEWS
@ -3,6 +3,15 @@ testtools NEWS
|
||||
|
||||
Changes and improvements to testtools_, grouped by release.
|
||||
|
||||
Next
|
||||
~~~~
|
||||
|
||||
Improvements
|
||||
------------
|
||||
|
||||
* ``TestResult`` objects that don't implement ``stop``/``shouldStop`` are now
|
||||
handled sanely. (Jonathan Lange)
|
||||
|
||||
2.0.0
|
||||
~~~~~
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2009-2015 testtools developers. See LICENSE for details.
|
||||
# Copyright (c) 2009-2016 testtools developers. See LICENSE for details.
|
||||
|
||||
"""Doubles of test result objects, useful for testing unittest code."""
|
||||
|
||||
@ -6,6 +6,7 @@ __all__ = [
|
||||
'Python26TestResult',
|
||||
'Python27TestResult',
|
||||
'ExtendedTestResult',
|
||||
'TwistedTestResult',
|
||||
'StreamResult',
|
||||
]
|
||||
|
||||
@ -20,14 +21,17 @@ class LoggingBase(object):
|
||||
if event_log is None:
|
||||
event_log = []
|
||||
self._events = event_log
|
||||
self.shouldStop = False
|
||||
self._was_successful = True
|
||||
self.testsRun = 0
|
||||
|
||||
|
||||
class Python26TestResult(LoggingBase):
|
||||
"""A precisely python 2.6 like test result, that logs."""
|
||||
|
||||
def __init__(self, event_log=None):
|
||||
super(Python26TestResult, self).__init__(event_log=event_log)
|
||||
self.shouldStop = False
|
||||
self._was_successful = True
|
||||
self.testsRun = 0
|
||||
|
||||
def addError(self, test, err):
|
||||
self._was_successful = False
|
||||
self._events.append(('addError', test, err))
|
||||
@ -153,17 +157,58 @@ class ExtendedTestResult(Python27TestResult):
|
||||
return self._was_successful
|
||||
|
||||
|
||||
class StreamResult(object):
|
||||
class TwistedTestResult(LoggingBase):
|
||||
"""
|
||||
Emulate the relevant bits of :py:class:`twisted.trial.itrial.IReporter`.
|
||||
|
||||
Used to ensure that we can use ``trial`` as a test runner.
|
||||
"""
|
||||
|
||||
def __init__(self, event_log=None):
|
||||
super(TwistedTestResult, self).__init__(event_log=event_log)
|
||||
self._was_successful = True
|
||||
self.testsRun = 0
|
||||
|
||||
def startTest(self, test):
|
||||
self.testsRun += 1
|
||||
self._events.append(('startTest', test))
|
||||
|
||||
def stopTest(self, test):
|
||||
self._events.append(('stopTest', test))
|
||||
|
||||
def addSuccess(self, test):
|
||||
self._events.append(('addSuccess', test))
|
||||
|
||||
def addError(self, test, error):
|
||||
self._was_successful = False
|
||||
self._events.append(('addError', test, error))
|
||||
|
||||
def addFailure(self, test, error):
|
||||
self._was_successful = False
|
||||
self._events.append(('addFailure', test, error))
|
||||
|
||||
def addExpectedFailure(self, test, failure, todo=None):
|
||||
self._events.append(('addExpectedFailure', test, failure))
|
||||
|
||||
def addUnexpectedSuccess(self, test, todo=None):
|
||||
self._events.append(('addUnexpectedSuccess', test))
|
||||
|
||||
def addSkip(self, test, reason):
|
||||
self._events.append(('addSkip', test, reason))
|
||||
|
||||
def wasSuccessful(self):
|
||||
return self._was_successful
|
||||
|
||||
def done(self):
|
||||
pass
|
||||
|
||||
|
||||
class StreamResult(LoggingBase):
|
||||
"""A StreamResult implementation for testing.
|
||||
|
||||
All events are logged to _events.
|
||||
"""
|
||||
|
||||
def __init__(self, event_log=None):
|
||||
if event_log is None:
|
||||
event_log = []
|
||||
self._events = event_log
|
||||
|
||||
def startTestRun(self):
|
||||
self._events.append(('startTestRun',))
|
||||
|
||||
|
@ -1349,6 +1349,8 @@ class ExtendedToOriginalDecorator(object):
|
||||
self._tags = TagContext()
|
||||
# Only used for old TestResults that do not have failfast.
|
||||
self._failfast = False
|
||||
# Used for old TestResults that do not have stop.
|
||||
self._shouldStop = False
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r>' % (self.__class__.__name__, self.decorated)
|
||||
@ -1485,9 +1487,15 @@ class ExtendedToOriginalDecorator(object):
|
||||
return
|
||||
return method(offset, whence)
|
||||
|
||||
@property
|
||||
def shouldStop(self):
|
||||
return self.decorated.shouldStop
|
||||
def _get_shouldStop(self):
|
||||
return getattr(self.decorated, 'shouldStop', self._shouldStop)
|
||||
|
||||
def _set_shouldStop(self, value):
|
||||
if safe_hasattr(self.decorated, 'shouldStop'):
|
||||
self.decorated.shouldStop = value
|
||||
else:
|
||||
self._shouldStop = value
|
||||
shouldStop = property(_get_shouldStop, _set_shouldStop)
|
||||
|
||||
def startTest(self, test):
|
||||
self._tags = TagContext(self._tags)
|
||||
@ -1501,7 +1509,10 @@ class ExtendedToOriginalDecorator(object):
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
return self.decorated.stop()
|
||||
method = getattr(self.decorated, 'stop', None)
|
||||
if method:
|
||||
return method()
|
||||
self.shouldStop = True
|
||||
|
||||
def stopTest(self, test):
|
||||
self._tags = self._tags.parent
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2008-2015 testtools developers. See LICENSE for details.
|
||||
# Copyright (c) 2008-2016 testtools developers. See LICENSE for details.
|
||||
|
||||
"""Test TestResults and related things."""
|
||||
|
||||
@ -83,6 +83,7 @@ from testtools.testresult.doubles import (
|
||||
Python27TestResult,
|
||||
ExtendedTestResult,
|
||||
StreamResult as LoggingStreamResult,
|
||||
TwistedTestResult,
|
||||
)
|
||||
from testtools.testresult.real import (
|
||||
_details_to_str,
|
||||
@ -134,7 +135,22 @@ def make_exception_info(exceptionFactory, *args, **kwargs):
|
||||
return sys.exc_info()
|
||||
|
||||
|
||||
class Python26Contract(object):
|
||||
class TestControlContract(object):
|
||||
"""Stopping test runs."""
|
||||
|
||||
def test_initially_not_shouldStop(self):
|
||||
# A result is not set to stop initially.
|
||||
result = self.makeResult()
|
||||
self.assertFalse(result.shouldStop)
|
||||
|
||||
def test_stop_sets_shouldStop(self):
|
||||
# Calling 'stop()' sets 'shouldStop' to True.
|
||||
result = self.makeResult()
|
||||
result.stop()
|
||||
self.assertTrue(result.shouldStop)
|
||||
|
||||
|
||||
class Python26Contract(TestControlContract):
|
||||
|
||||
def test_fresh_result_is_successful(self):
|
||||
# A result is considered successful before any tests are run.
|
||||
@ -165,11 +181,6 @@ class Python26Contract(object):
|
||||
result.stopTest(self)
|
||||
self.assertTrue(result.wasSuccessful())
|
||||
|
||||
def test_stop_sets_shouldStop(self):
|
||||
result = self.makeResult()
|
||||
result.stop()
|
||||
self.assertTrue(result.shouldStop)
|
||||
|
||||
|
||||
class Python27Contract(Python26Contract):
|
||||
|
||||
@ -462,6 +473,12 @@ class TestAdaptedPython27TestResultContract(TestCase, DetailsContract):
|
||||
return ExtendedToOriginalDecorator(Python27TestResult())
|
||||
|
||||
|
||||
class TestAdaptedTwistedTestResultContract(TestCase, DetailsContract):
|
||||
|
||||
def makeResult(self):
|
||||
return ExtendedToOriginalDecorator(TwistedTestResult())
|
||||
|
||||
|
||||
class TestAdaptedStreamResult(TestCase, DetailsContract):
|
||||
|
||||
def makeResult(self):
|
||||
@ -1095,15 +1112,10 @@ testtools.matchers._impl.MismatchError: Differences: [
|
||||
self.assertEqual("foo.bar", result.unexpectedSuccesses[0].id())
|
||||
|
||||
|
||||
class TestTestControl(TestCase):
|
||||
class TestTestControl(TestCase, TestControlContract):
|
||||
|
||||
def test_default(self):
|
||||
self.assertEqual(False, TestControl().shouldStop)
|
||||
|
||||
def test_stop(self):
|
||||
control = TestControl()
|
||||
control.stop()
|
||||
self.assertEqual(True, control.shouldStop)
|
||||
def makeResult(self):
|
||||
return TestControl()
|
||||
|
||||
|
||||
class TestTestResult(TestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user