* The ``failfast`` option to ``subunit.run`` will now work. The dependency on
authorRobert Collins <robertc@robertcollins.net>
Mon, 17 Dec 2012 07:24:28 +0000 (20:24 +1300)
committerRobert Collins <robertc@robertcollins.net>
Mon, 17 Dec 2012 07:24:28 +0000 (20:24 +1300)
  testtools has been raised to 0.9.23 to permit this.
  (Robert Collins, #1090582)

NEWS
python/subunit/__init__.py
python/subunit/run.py
python/subunit/test_results.py
python/subunit/tests/test_test_results.py
runtests.py
setup.py

diff --git a/NEWS b/NEWS
index f28ec5a6df9811b698d255b67bc7b988c4411570..f515c6549d321fbb47e2f91a7eadb98cb11e7a91 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ BUG FIXES
 * ``python/subunit/tests/test_run.py`` and ``python/subunit/filters.py`` were
   not included in the 0.0.8 tarball. (Robert Collins)
 
+* The ``failfast`` option to ``subunit.run`` will now work. The dependency on
+  testtools has been raised to 0.9.23 to permit this.
+  (Robert Collins, #1090582)
+
 0.0.8
 -----
 
index 6015c0e68ca050237178c42efa0b8094668091ea..ebe6e8c82cbdf70e0a5045b430c3a109cfee0b1c 100644 (file)
@@ -636,6 +636,8 @@ class TestProtocolClient(testresult.TestResult):
             to subunit.Content objects.
         """
         self._addOutcome("error", test, error=error, details=details)
+        if self.failfast:
+            self.stop()
 
     def addExpectedFailure(self, test, error=None, details=None):
         """Report an expected failure in test test.
@@ -666,6 +668,8 @@ class TestProtocolClient(testresult.TestResult):
             to subunit.Content objects.
         """
         self._addOutcome("failure", test, error=error, details=details)
+        if self.failfast:
+            self.stop()
 
     def _addOutcome(self, outcome, test, error=None, details=None,
         error_permitted=True):
@@ -730,6 +734,8 @@ class TestProtocolClient(testresult.TestResult):
         """
         self._addOutcome("uxsuccess", test, details=details,
             error_permitted=False)
+        if self.failfast:
+            self.stop()
 
     def startTest(self, test):
         """Mark a test as starting its test run."""
index ca5fe5c17e0e4ac1506de39a3bae2a1fffe9af88..b5ccea449d1ff11059276396515ba5dd148def9e 100755 (executable)
@@ -34,13 +34,22 @@ from testtools.run import (
 
 
 class SubunitTestRunner(object):
-    def __init__(self, stream=sys.stdout):
-        self.stream = stream
+    def __init__(self, verbosity=None, failfast=None, buffer=None, stream=None):
+        """Create a TestToolsTestRunner.
+
+        :param verbosity: Ignored.
+        :param failfast: Stop running tests at the first failure.
+        :param buffer: Ignored.
+        """
+        self.failfast = failfast
+        self.stream = stream or sys.stdout
 
     def run(self, test):
         "Run the given test case or test suite."
         result = TestProtocolClient(self.stream)
         result = AutoTimingTestResultDecorator(result)
+        if self.failfast is not None:
+            result.failfast = self.failfast
         test(result)
         return result
 
@@ -70,6 +79,6 @@ class SubunitTestProgram(TestProgram):
 
 if __name__ == '__main__':
     stream = get_default_formatter()
-    runner = SubunitTestRunner(stream)
+    runner = SubunitTestRunner
     SubunitTestProgram(module=None, argv=sys.argv, testRunner=runner,
         stdout=sys.stdout)
index c00a2d3e9706cf3c2011b1c35856e63ff08402b7..91c9bbdc1e4219e21957bd39e16a6c0e06043925 100644 (file)
@@ -78,6 +78,13 @@ class TestResultDecorator(object):
     def addUnexpectedSuccess(self, test, details=None):
         return self.decorated.addUnexpectedSuccess(test, details=details)
 
+    def _get_failfast(self):
+        return getattr(self.decorated, 'failfast', False)
+
+    def _set_failfast(self, value):
+        self.decorated.failfast = value
+    failfast = property(_get_failfast, _set_failfast)
+
     def progress(self, offset, whence):
         return self.decorated.progress(offset, whence)
 
index 236dfa22e51f0b15474ff5aae236b7feef86c48a..ff74b9a818f64681c6fa24e4b6480a4b456ceeb6 100644 (file)
@@ -61,6 +61,7 @@ class TimeCapturingResult(unittest.TestResult):
     def __init__(self):
         super(TimeCapturingResult, self).__init__()
         self._calls = []
+        self.failfast = False
 
     def time(self, a_datetime):
         self._calls.append(a_datetime)
@@ -198,6 +199,11 @@ class TestAutoTimingTestResultDecorator(unittest.TestCase):
         self.assertEqual(3, len(self.decorated._calls))
         self.assertNotEqual(None, self.decorated._calls[2])
 
+    def test_set_failfast_True(self):
+        self.assertFalse(self.decorated.failfast)
+        self.result.failfast = True
+        self.assertTrue(self.decorated.failfast)
+
 
 class TestTagCollapsingDecorator(TestCase):
 
index 691e3b34da81ac240cc544a7bd17ad9563212e91..8ecc6cd3fb4c47f1d4237bf8a8392cb6d08bba62 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # -*- Mode: python -*-
 #
 # Copyright (C) 2004 Canonical.com
index a78eb999d7cf5262b125d26532a721ca5933e7fa..ed94d90c98ebc53d172ba758b7029e1ec0c7c38f 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,7 @@ except ImportError:
 else:
     extra = {
         'install_requires': [
-            'testtools>=0.9.11',
+            'testtools>=0.9.23',
         ]
     }
 
@@ -38,6 +38,7 @@ setup(
     long_description=open('README').read(),
     classifiers=[
         'Intended Audience :: Developers',
+        'Programming Language :: Python :: 3',
         'Programming Language :: Python',
         'Topic :: Software Development :: Testing',
     ],