diff options
author | Free Ekanayaka <free@ekanayaka.io> | 2016-11-26 19:36:49 +0000 |
---|---|---|
committer | Free Ekanayaka <free@ekanayaka.io> | 2016-11-26 20:10:25 +0000 |
commit | 2632169bf97511e366a82ec190ad2e7427d2f879 (patch) | |
tree | 36a5841e68738ccaee5df0388e1f9f706dcc18f3 | |
parent | bfc88a45a3d94b4b030f9210471b2f700d294f15 (diff) | |
download | testtools-resourced-to-stream-decorator.tar.gz |
Add ResourcedToStreamDecorator test result decorator for testresources integrationresourced-to-stream-decorator
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | setup.cfg | 1 | ||||
-rw-r--r-- | testtools/__init__.py | 2 | ||||
-rw-r--r-- | testtools/testresult/__init__.py | 2 | ||||
-rw-r--r-- | testtools/testresult/doubles.py | 21 | ||||
-rw-r--r-- | testtools/testresult/real.py | 53 | ||||
-rw-r--r-- | testtools/tests/test_testresult.py | 77 |
7 files changed, 154 insertions, 8 deletions
@@ -6,6 +6,12 @@ Changes and improvements to testtools_, grouped by release. NEXT ~~~~ +Improvements +------------ + +* New ``ResourcedToStreamDecorator`` for tracking lifecycle events of + test resources, and possibly integrate with subunit. + 2.2.0 ~~~~~ @@ -12,6 +12,7 @@ classifier = [extras] test = testscenarios + testresources unittest2>=1.1.0 [files] diff --git a/testtools/__init__.py b/testtools/__init__.py index d6c15d2..5d6193a 100644 --- a/testtools/__init__.py +++ b/testtools/__init__.py @@ -18,6 +18,7 @@ __all__ = [ 'MultiTestResult', 'PlaceHolder', 'run_test_with', + 'ResourcedToStreamDecorator', 'Tagger', 'TestCase', 'TestCommand', @@ -85,6 +86,7 @@ else: ExtendedToOriginalDecorator, ExtendedToStreamDecorator, MultiTestResult, + ResourcedToStreamDecorator, StreamFailFast, StreamResult, StreamResultRouter, diff --git a/testtools/testresult/__init__.py b/testtools/testresult/__init__.py index 5bf8f9c..83d8cc7 100644 --- a/testtools/testresult/__init__.py +++ b/testtools/testresult/__init__.py @@ -7,6 +7,7 @@ __all__ = [ 'ExtendedToOriginalDecorator', 'ExtendedToStreamDecorator', 'MultiTestResult', + 'ResourcedToStreamDecorator', 'StreamFailFast', 'StreamResult', 'StreamResultRouter', @@ -30,6 +31,7 @@ from testtools.testresult.real import ( ExtendedToOriginalDecorator, ExtendedToStreamDecorator, MultiTestResult, + ResourcedToStreamDecorator, StreamFailFast, StreamResult, StreamResultRouter, diff --git a/testtools/testresult/doubles.py b/testtools/testresult/doubles.py index 3f1bf53..e37a5d4 100644 --- a/testtools/testresult/doubles.py +++ b/testtools/testresult/doubles.py @@ -2,6 +2,10 @@ """Doubles of test result objects, useful for testing unittest code.""" +from collections import namedtuple + +from testtools.tags import TagContext + __all__ = [ 'Python26TestResult', 'Python27TestResult', @@ -11,9 +15,6 @@ __all__ = [ ] -from testtools.tags import TagContext - - class LoggingBase(object): """Basic support for logging of results.""" @@ -219,6 +220,14 @@ class StreamResult(LoggingBase): runnable=True, file_name=None, file_bytes=None, eof=False, mime_type=None, route_code=None, timestamp=None): self._events.append( - ('status', test_id, test_status, test_tags, - runnable, file_name, file_bytes, eof, mime_type, route_code, - timestamp)) + _StatusEvent( + 'status', test_id, test_status, test_tags, runnable, + file_name, file_bytes, eof, mime_type, route_code, + timestamp)) + + +# Convenience for easier access to status fields +_StatusEvent = namedtuple( + "_Event", [ + "name", "test_id", "test_status", "test_tags", "runnable", "file_name", + "file_bytes", "eof", "mime_type", "route_code", "timestamp"]) diff --git a/testtools/testresult/real.py b/testtools/testresult/real.py index 9330bd8..b74a163 100644 --- a/testtools/testresult/real.py +++ b/testtools/testresult/real.py @@ -6,6 +6,7 @@ __all__ = [ 'ExtendedToOriginalDecorator', 'ExtendedToStreamDecorator', 'MultiTestResult', + 'ResourcedToStreamDecorator', 'StreamFailFast', 'StreamResult', 'StreamSummary', @@ -1688,6 +1689,58 @@ class ExtendedToStreamDecorator(CopyStreamResult, StreamSummary, TestControl): return super(ExtendedToStreamDecorator, self).wasSuccessful() +class ResourcedToStreamDecorator(ExtendedToStreamDecorator): + """Report ``testresources``-related activity to StreamResult objects. + + Implement the resource lifecycle TestResult protocol extension supported + by the ``testresources.TestResourceManager`` class. At each stage of a + resource's lifecycle, a stream event with relevant details will be + emitted. + + Each stream event will have its test_id field set to the resource manager's + identifier (see ``testresources.TestResourceManager.id()``) plus the method + being executed (either 'make' or 'clean'). + + The test_status will be either 'inprogress' or 'success'. + + The runnable flag will be set to False. + """ + + def startMakeResource(self, resource): + self._convertResourceLifecycle(resource, 'make', 'start') + + def stopMakeResource(self, resource): + self._convertResourceLifecycle(resource, 'make', 'stop') + + def startCleanResource(self, resource): + self._convertResourceLifecycle(resource, 'clean', 'start') + + def stopCleanResource(self, resource): + self._convertResourceLifecycle(resource, 'clean', 'stop') + + def _convertResourceLifecycle(self, resource, method, phase): + """Convert a resource lifecycle report to a stream event.""" + + # If the resource implements the TestResourceManager.id() API, let's + # use it, otherwise fallback to the class name. + if safe_hasattr(resource, "id"): + resource_id = resource.id() + else: + resource_id = "%s.%s" % ( + resource.__class__.__module__, resource.__class__.__name__) + + test_id = '%s.%s' % (resource_id, method) + + if phase == 'start': + test_status = 'inprogress' + else: + test_status = 'success' + + self.status( + test_id=test_id, test_status=test_status, runnable=False, + timestamp=self._now()) + + class StreamToExtendedDecorator(StreamResult): """Convert StreamResult API calls into ExtendedTestResult calls. diff --git a/testtools/tests/test_testresult.py b/testtools/tests/test_testresult.py index 2126c49..bea18c4 100644 --- a/testtools/tests/test_testresult.py +++ b/testtools/tests/test_testresult.py @@ -15,17 +15,19 @@ import sys import tempfile import threading from unittest import TestSuite - -from extras import safe_hasattr, try_imports +from extras import safe_hasattr, try_imports, try_import Queue = try_imports(['Queue.Queue', 'queue.Queue']) +testresources = try_import('testresources') + from testtools import ( CopyStreamResult, ExtendedToOriginalDecorator, ExtendedToStreamDecorator, MultiTestResult, PlaceHolder, + ResourcedToStreamDecorator, StreamFailFast, StreamResult, StreamResultRouter, @@ -590,6 +592,12 @@ class TestExtendedToStreamDecoratorContract(TestCase, TestStreamResultContract): return ExtendedToStreamDecorator(StreamResult()) +class TestResourcedToStreamDecoratorContract(TestCase, TestStreamResultContract): + + def _make_result(self): + return ResourcedToStreamDecorator(StreamResult()) + + class TestStreamSummaryResultContract(TestCase, TestStreamResultContract): def _make_result(self): @@ -920,6 +928,71 @@ class TestExtendedToStreamDecorator(TestCase): ('stopTestRun',)], log._events) +class TestResourcedToStreamDecorator(TestCase): + + def setUp(self): + super(TestResourcedToStreamDecorator, self).setUp() + if testresources is None: + self.skipTest('Need testresources') + + def test_startMakeResource(self): + log = LoggingStreamResult() + result = ResourcedToStreamDecorator(log) + timestamp = datetime.datetime.utcfromtimestamp(3.476) + result.startTestRun() + result.time(timestamp) + resource = testresources.TestResourceManager() + result.startMakeResource(resource) + [_, event] = log._events + self.assertEqual( + 'testresources.TestResourceManager.make', event.test_id) + self.assertEqual('inprogress', event.test_status) + self.assertFalse(event.runnable) + self.assertEqual(timestamp, event.timestamp) + + def test_startMakeResource_with_custom_id_method(self): + log = LoggingStreamResult() + result = ResourcedToStreamDecorator(log) + resource = testresources.TestResourceManager() + resource.id = lambda: 'nice.resource' + result.startTestRun() + result.startMakeResource(resource) + self.assertEqual('nice.resource.make', log._events[1].test_id) + + def test_stopMakeResource(self): + log = LoggingStreamResult() + result = ResourcedToStreamDecorator(log) + resource = testresources.TestResourceManager() + result.startTestRun() + result.stopMakeResource(resource) + [_, event] = log._events + self.assertEqual( + 'testresources.TestResourceManager.make', event.test_id) + self.assertEqual('success', event.test_status) + + def test_startCleanResource(self): + log = LoggingStreamResult() + result = ResourcedToStreamDecorator(log) + resource = testresources.TestResourceManager() + result.startTestRun() + result.startCleanResource(resource) + [_, event] = log._events + self.assertEqual( + 'testresources.TestResourceManager.clean', event.test_id) + self.assertEqual('inprogress', event.test_status) + + def test_stopCleanResource(self): + log = LoggingStreamResult() + result = ResourcedToStreamDecorator(log) + resource = testresources.TestResourceManager() + result.startTestRun() + result.stopCleanResource(resource) + [_, event] = log._events + self.assertEqual( + 'testresources.TestResourceManager.clean', event.test_id) + self.assertEqual('success', event.test_status) + + class TestStreamFailFast(TestCase): def test_inprogress(self): |