Add an adapter to adapt StreamResult event streams into ExtendedTestResult.
This permits running existing tools like test runners and reporters with StreamResult generating test runs adapting any unittest or testtools compatible TestResult into a StreamResult.
This commit is contained in:
5
NEWS
5
NEWS
@@ -51,6 +51,11 @@ Improvements
|
||||
separate consumer will be created to support that.
|
||||
(Robert Collins)
|
||||
|
||||
* New support class ``StreamToExtendedDecorator`` which translates
|
||||
``StreamResult`` API calls into ``ExtendedTestResult`` (or any older
|
||||
``TestResult``) calls. This permits using un-migrated result objects with
|
||||
new runners / tests. (Robert Collins)
|
||||
|
||||
* New ``TestCase`` decorator ``DecorateTestCaseResult`` that adapts the
|
||||
``TestResult`` or ``StreamResult`` a case will be run with, for ensuring that
|
||||
a particular result object is used even if the runner running the test doesn't
|
||||
|
||||
@@ -213,6 +213,14 @@ This is useful when a ``StreamResult`` stream is desired, but you cannot
|
||||
be sure that the tests which will run have been updated to the ``StreamResult``
|
||||
API.
|
||||
|
||||
StreamToExtendedDecorator
|
||||
-------------------------
|
||||
|
||||
This is a simple converter that emits the ``ExtendedTestResult`` API in
|
||||
response to events from the ``StreamResult`` API. Useful when outputting
|
||||
``StreamResult`` events from a ``TestCase`` but the supplied ``TestResult``
|
||||
does not support the ``status`` and ``file`` methods.
|
||||
|
||||
ThreadsafeStreamResult
|
||||
----------------------
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ __all__ = [
|
||||
'StreamResult',
|
||||
'StreamSummary',
|
||||
'StreamToDict',
|
||||
'StreamToExtendedDecorator',
|
||||
'TestControl',
|
||||
'ThreadsafeForwardingResult',
|
||||
'try_import',
|
||||
@@ -81,6 +82,7 @@ else:
|
||||
StreamResult,
|
||||
StreamSummary,
|
||||
StreamToDict,
|
||||
StreamToExtendedDecorator,
|
||||
Tagger,
|
||||
TestByTestResult,
|
||||
TestControl,
|
||||
|
||||
@@ -11,6 +11,7 @@ __all__ = [
|
||||
'StreamResult',
|
||||
'StreamSummary',
|
||||
'StreamToDict',
|
||||
'StreamToExtendedDecorator',
|
||||
'Tagger',
|
||||
'TestByTestResult',
|
||||
'TestControl',
|
||||
@@ -29,6 +30,7 @@ from testtools.testresult.real import (
|
||||
StreamResult,
|
||||
StreamSummary,
|
||||
StreamToDict,
|
||||
StreamToExtendedDecorator,
|
||||
Tagger,
|
||||
TestByTestResult,
|
||||
TestControl,
|
||||
|
||||
@@ -11,6 +11,7 @@ __all__ = [
|
||||
'StreamResult',
|
||||
'StreamSummary',
|
||||
'StreamToDict',
|
||||
'StreamToExtendedDecorator',
|
||||
'Tagger',
|
||||
'TestControl',
|
||||
'TestResult',
|
||||
@@ -540,7 +541,8 @@ def test_dict_to_case(test_dict):
|
||||
from testtools.testcase import PlaceHolder
|
||||
outcome = _status_map[test_dict['status']]
|
||||
return PlaceHolder(test_dict['id'], outcome=outcome,
|
||||
details=test_dict['details'], tags=test_dict['tags'])
|
||||
details=test_dict['details'], tags=test_dict['tags'],
|
||||
timestamps=test_dict['timestamps'])
|
||||
|
||||
|
||||
class StreamSummary(StreamToDict):
|
||||
@@ -1260,6 +1262,44 @@ class ExtendedToStreamDecorator(CopyStreamResult, StreamSummary, TestControl):
|
||||
return super(ExtendedToStreamDecorator, self).wasSuccessful()
|
||||
|
||||
|
||||
class StreamToExtendedDecorator(StreamResult):
|
||||
"""Convert StreamResult API calls into ExtendedTestResult calls.
|
||||
|
||||
This will buffer all calls for all concurrently active tests, and
|
||||
then flush each test as they complete.
|
||||
|
||||
Incomplete tests will be flushed as errors when the test run stops.
|
||||
|
||||
Non test file attachments are accumulated into a test called
|
||||
'testtools.extradata' flushed at the end of the run.
|
||||
"""
|
||||
|
||||
def __init__(self, decorated):
|
||||
# ExtendedToOriginalDecorator takes care of thunking details back to
|
||||
# exceptions/reasons etc.
|
||||
self.decorated = ExtendedToOriginalDecorator(decorated)
|
||||
# StreamToDict buffers and gives us individual tests.
|
||||
self.hook = StreamToDict(self._handle_tests)
|
||||
|
||||
def status(self, test_id=None, test_status=None, *args, **kwargs):
|
||||
if test_status == 'exists':
|
||||
return
|
||||
self.hook.status(
|
||||
test_id=test_id, test_status=test_status, *args, **kwargs)
|
||||
|
||||
def startTestRun(self):
|
||||
self.decorated.startTestRun()
|
||||
self.hook.startTestRun()
|
||||
|
||||
def stopTestRun(self):
|
||||
self.hook.stopTestRun()
|
||||
self.decorated.stopTestRun()
|
||||
|
||||
def _handle_tests(self, test_dict):
|
||||
case = test_dict_to_case(test_dict)
|
||||
case.run(self.decorated)
|
||||
|
||||
|
||||
class TestResultDecorator(object):
|
||||
"""General pass-through decorator.
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ from testtools import (
|
||||
StreamResult,
|
||||
StreamSummary,
|
||||
StreamToDict,
|
||||
StreamToExtendedDecorator,
|
||||
Tagger,
|
||||
TestCase,
|
||||
TestControl,
|
||||
@@ -467,6 +468,15 @@ class TestTestResultDecoratorContract(TestCase, StartTestRunContract):
|
||||
return TestResultDecorator(TestResult())
|
||||
|
||||
|
||||
# DetailsContract because ExtendedToStreamDecorator follows Python for
|
||||
# uxsuccess handling.
|
||||
class TestStreamToExtendedContract(TestCase, DetailsContract):
|
||||
|
||||
def makeResult(self):
|
||||
return ExtendedToStreamDecorator(
|
||||
StreamToExtendedDecorator(ExtendedTestResult()))
|
||||
|
||||
|
||||
class TestStreamResultContract(object):
|
||||
|
||||
def _make_result(self):
|
||||
@@ -559,6 +569,12 @@ class TestStreamToDictContract(TestCase, TestStreamResultContract):
|
||||
return StreamToDict(lambda x:None)
|
||||
|
||||
|
||||
class TestStreamToExtendedDecoratorContract(TestCase, TestStreamResultContract):
|
||||
|
||||
def _make_result(self):
|
||||
return StreamToExtendedDecorator(ExtendedTestResult())
|
||||
|
||||
|
||||
class TestStreamFailFastContract(TestCase, TestStreamResultContract):
|
||||
|
||||
def _make_result(self):
|
||||
|
||||
Reference in New Issue
Block a user