0.0.19
authorRobert Collins <robertc@robertcollins.net>
Sun, 24 Aug 2014 05:54:36 +0000 (17:54 +1200)
committerRobert Collins <robertc@robertcollins.net>
Sun, 24 Aug 2014 05:54:36 +0000 (17:54 +1200)
------

IMPROVEMENTS
~~~~~~~~~~~~

* ``subunit.run`` in Python will now exit 0 as long as the test stream has
  been generated correctly - this has always been the intent but API friction
  with testtools had prevented it working.
  (Robert Collins)

NEWS
configure.ac
python/subunit/__init__.py
python/subunit/run.py
python/subunit/tests/test_run.py

diff --git a/NEWS b/NEWS
index 9d1100c..2023c2e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -5,11 +5,22 @@ subunit release notes
 NEXT (In development)
 ---------------------
 
+0.0.19
+------
+
+IMPROVEMENTS
+~~~~~~~~~~~~
+
+* ``subunit.run`` in Python will now exit 0 as long as the test stream has
+  been generated correctly - this has always been the intent but API friction
+  with testtools had prevented it working.
+  (Robert Collins)
+
 0.0.18
 ------
 
-IMPROVMENTS
-~~~~~~~~~~~
+IMPROVEMENTS
+~~~~~~~~~~~~
 
 * Fix compatibility with testtools 0.9.35 which dropped the 'all' compat
   symbol. This breaks support for Python versions lower than 2.6.
@@ -18,8 +29,8 @@ IMPROVMENTS
 0.0.17
 ------
 
-IMPROVMENTS
-~~~~~~~~~~~
+IMPROVEMENTS
+~~~~~~~~~~~~
 
 * Add ``subunit-output`` tool that can generate a Subunit v2 bytestream from
   arguments passed on the command line. (Thomi Richards, #1252084)
index 4c94519..07c96ac 100644 (file)
@@ -1,6 +1,6 @@
 m4_define([SUBUNIT_MAJOR_VERSION], [0])
 m4_define([SUBUNIT_MINOR_VERSION], [0])
-m4_define([SUBUNIT_MICRO_VERSION], [18])
+m4_define([SUBUNIT_MICRO_VERSION], [19])
 m4_define([SUBUNIT_VERSION],
 m4_defn([SUBUNIT_MAJOR_VERSION]).m4_defn([SUBUNIT_MINOR_VERSION]).m4_defn([SUBUNIT_MICRO_VERSION]))
 AC_PREREQ([2.59])
index 8764d45..ff952c3 100644 (file)
@@ -153,7 +153,7 @@ from subunit.v2 import ByteStreamToStreamResult, StreamResultToBytes
 # If the releaselevel is 'final', then the tarball will be major.minor.micro.
 # Otherwise it is major.minor.micro~$(revno).
 
-__version__ = (0, 0, 18, 'final', 0)
+__version__ = (0, 0, 19, 'final', 0)
 
 PROGRESS_SET = 0
 PROGRESS_CUR = 1
index 7e4d783..cf9cc01 100755 (executable)
@@ -40,15 +40,21 @@ from testtools.run import (
 
 
 class SubunitTestRunner(object):
-    def __init__(self, verbosity=None, failfast=None, buffer=None, stream=None):
+    def __init__(self, verbosity=None, failfast=None, buffer=None, stream=None,
+        stdout=None):
         """Create a TestToolsTestRunner.
 
         :param verbosity: Ignored.
         :param failfast: Stop running tests at the first failure.
         :param buffer: Ignored.
+        :param stream: Upstream unittest stream parameter.
+        :param stdout: Testtools stream parameter.
+
+        Either stream or stdout can be supplied, and stream will take
+        precedence.
         """
         self.failfast = failfast
-        self.stream = stream or sys.stdout
+        self.stream = stream or stdout or sys.stdout
 
     def run(self, test):
         "Run the given test case or test suite."
@@ -112,19 +118,27 @@ class SubunitTestProgram(TestProgram):
         sys.exit(2)
 
 
-def main():
-    # Disable the default buffering, for Python 2.x where pdb doesn't do it
-    # on non-ttys.
-    stream = get_default_formatter()
+def main(argv=None, stdout=None):
+    if argv is None:
+        argv = sys.argv
     runner = SubunitTestRunner
-    # Patch stdout to be unbuffered, so that pdb works well on 2.6/2.7.
-    binstdout = io.open(sys.stdout.fileno(), 'wb', 0)
-    if sys.version_info[0] > 2:
-        sys.stdout = io.TextIOWrapper(binstdout, encoding=sys.stdout.encoding)
-    else:
-        sys.stdout = binstdout
-    SubunitTestProgram(module=None, argv=sys.argv, testRunner=runner,
-        stdout=sys.stdout)
+    # stdout is None except in unit tests.
+    if stdout is None:
+        stdout = sys.stdout
+        # XXX: This is broken code- SUBUNIT_FORMATTER is not being honoured.
+        stream = get_default_formatter()
+        # Disable the default buffering, for Python 2.x where pdb doesn't do it
+        # on non-ttys.
+        if hasattr(stdout, 'fileno'):
+            # Patch stdout to be unbuffered, so that pdb works well on 2.6/2.7.
+            binstdout = io.open(stdout.fileno(), 'wb', 0)
+            if sys.version_info[0] > 2:
+                sys.stdout = io.TextIOWrapper(binstdout, encoding=sys.stdout.encoding)
+            else:
+                sys.stdout = binstdout
+            stdout = sys.stdout
+    SubunitTestProgram(module=None, argv=argv, testRunner=runner,
+        stdout=stdout, exit=False)
 
 
 if __name__ == '__main__':
index 6ac84e1..d92ed04 100644 (file)
 #  limitations under that license.
 #
 
-from testtools.compat import BytesIO
+import io
 import unittest
 
 from testtools import PlaceHolder, TestCase
+from testtools.compat import _b
+from testtools.matchers import StartsWith
 from testtools.testresult.doubles import StreamResult
 
 import subunit
@@ -28,37 +30,59 @@ from subunit.run import SubunitTestRunner
 class TestSubunitTestRunner(TestCase):
 
     def test_includes_timing_output(self):
-        io = BytesIO()
-        runner = SubunitTestRunner(stream=io)
+        bytestream = io.BytesIO()
+        runner = SubunitTestRunner(stream=bytestream)
         test = PlaceHolder('name')
         runner.run(test)
-        io.seek(0)
+        bytestream.seek(0)
         eventstream = StreamResult()
-        subunit.ByteStreamToStreamResult(io).run(eventstream)
+        subunit.ByteStreamToStreamResult(bytestream).run(eventstream)
         timestamps = [event[-1] for event in eventstream._events
             if event is not None]
         self.assertNotEqual([], timestamps)
 
     def test_enumerates_tests_before_run(self):
-        io = BytesIO()
-        runner = SubunitTestRunner(stream=io)
+        bytestream = io.BytesIO()
+        runner = SubunitTestRunner(stream=bytestream)
         test1 = PlaceHolder('name1')
         test2 = PlaceHolder('name2')
         case = unittest.TestSuite([test1, test2])
         runner.run(case)
-        io.seek(0)
+        bytestream.seek(0)
         eventstream = StreamResult()
-        subunit.ByteStreamToStreamResult(io).run(eventstream)
+        subunit.ByteStreamToStreamResult(bytestream).run(eventstream)
         self.assertEqual([
             ('status', 'name1', 'exists'),
             ('status', 'name2', 'exists'),
             ], [event[:3] for event in eventstream._events[:2]])
 
     def test_list_errors_if_errors_from_list_test(self):
-        io = BytesIO()
-        runner = SubunitTestRunner(stream=io)
+        bytestream = io.BytesIO()
+        runner = SubunitTestRunner(stream=bytestream)
         def list_test(test):
             return [], ['failed import']
         self.patch(run, 'list_test', list_test)
         exc = self.assertRaises(SystemExit, runner.list, None)
         self.assertEqual((2,), exc.args)
+
+    class FailingTest(TestCase):
+        def test_fail(self):
+            1/0
+
+    def test_exits_zero_when_tests_fail(self):
+        bytestream = io.BytesIO()
+        stream = io.TextIOWrapper(bytestream, encoding="utf8")
+        try:
+            self.assertEqual(None, run.main(
+                argv=["progName", "subunit.tests.test_run.TestSubunitTestRunner.FailingTest"],
+                stdout=stream))
+        except SystemExit:
+            self.fail("SystemExit raised")
+        self.assertThat(bytestream.getvalue(), StartsWith(_b('\xb3')))
+
+    def test_exits_nonzero_when_execution_errors(self):
+        bytestream = io.BytesIO()
+        stream = io.TextIOWrapper(bytestream, encoding="utf8")
+        exc = self.assertRaises(Exception, run.main,
+                argv=["progName", "subunit.tests.test_run.TestSubunitTestRunner.MissingTest"],
+                stdout=stream)