[Checkins] SVN: zope.testrunner/trunk/s merge in utf8 support for tracebacks

Benji York benji+zope.org at benjiyork.com
Mon Jul 19 18:06:28 EDT 2010


Log message for revision 114856:
  merge in utf8 support for tracebacks
  
  I added python-subunit to tests_require and the tests extra on the principal
  that tests with non-abhorrent dependencies should be run by default, and
  python-subunit seems reasonable.
  
  see https://code.edge.launchpad.net/~mars/zope.testing/fix-subunit-utf8-traceback-reporting/+merge/27086
  for the details
  

Changed:
  U   zope.testrunner/trunk/setup.py
  U   zope.testrunner/trunk/src/zope/testrunner/formatter.py
  A   zope.testrunner/trunk/src/zope/testrunner/test_subunit.py
  U   zope.testrunner/trunk/src/zope/testrunner/testrunner-subunit.txt
  U   zope.testrunner/trunk/src/zope/testrunner/tests.py

-=-
Modified: zope.testrunner/trunk/setup.py
===================================================================
--- zope.testrunner/trunk/setup.py	2010-07-19 20:32:06 UTC (rev 114855)
+++ zope.testrunner/trunk/setup.py	2010-07-19 22:06:28 UTC (rev 114856)
@@ -26,7 +26,7 @@
 if sys.version_info < (2,4) or sys.version_info[:2] == (3,0):
     raise ValueError("zope.testrunner requires Python 2.4 or higher, "
                      "but not Python 3.0.")
-    
+
 if sys.version_info >= (3,):
     extra = dict(use_2to3 = True,
                  setup_requires = ['zope.fixers'],
@@ -101,7 +101,7 @@
         script = template % (sys.path, os.path.abspath(os.curdir), os.path.abspath('src'))
         scriptfile.write(script)
         scriptfile.close()
- 
+
         import subprocess
         process = subprocess.Popen([sys.executable, filename])
         process.wait()
@@ -171,8 +171,8 @@
     install_requires = ['setuptools',
                         'zope.exceptions',
                         'zope.interface',],
-    tests_require = ['zope.testing',],
-    extras_require = {'test': ['zope.testing',]},
+    tests_require = ['zope.testing', 'python-subunit'],
+    extras_require = {'test': ['zope.testing', 'python-subunit']},
     entry_points = {
         'console_scripts':
             ['zope-testrunner = zope.testrunner:run',]},

Modified: zope.testrunner/trunk/src/zope/testrunner/formatter.py
===================================================================
--- zope.testrunner/trunk/src/zope/testrunner/formatter.py	2010-07-19 20:32:06 UTC (rev 114855)
+++ zope.testrunner/trunk/src/zope/testrunner/formatter.py	2010-07-19 22:06:28 UTC (rev 114856)
@@ -717,14 +717,18 @@
     TAG_THREADS = 'zope:threads'
     TAG_REFCOUNTS = 'zope:refcounts'
 
-    def __init__(self, options):
+    def __init__(self, options, stream=None):
         if subunit is None:
             raise Exception("Requires subunit 0.0.5 or better")
         if content is None:
             raise Exception("Requires testtools 0.9.2 or better")
         self.options = options
-        self._stream = sys.stdout
+
+        if stream is None:
+            stream = sys.stdout
+        self._stream = stream
         self._subunit = subunit.TestProtocolClient(self._stream)
+
         # Used to track the last layer that was set up or torn down. Either
         # None or (layer_name, last_touched_time).
         self._last_layer = None
@@ -912,9 +916,18 @@
         # create an object equivalent to an instance of 'TracebackContent'.
         formatter = OutputFormatter(None)
         traceback = formatter.format_traceback(exc_info)
+
+        # We have no idea if the traceback is a unicode object or a bytestring
+        # with non-ASCII characters.  We had best be careful when handling it.
+        if isinstance(traceback, unicode):
+            unicode_tb = traceback
+        else:
+            # Assume the traceback was utf-8 encoded, but still be careful.
+            unicode_tb = traceback.decode('utf8', 'replace')
+
         return {
             'traceback': content.Content(
-                self.TRACEBACK_CONTENT_TYPE, lambda: [traceback.encode('utf8')])}
+                self.TRACEBACK_CONTENT_TYPE, lambda: [unicode_tb.encode('utf8')])}
 
     def test_error(self, test, seconds, exc_info):
         """Report that an error occurred while running a test.

Added: zope.testrunner/trunk/src/zope/testrunner/test_subunit.py
===================================================================
--- zope.testrunner/trunk/src/zope/testrunner/test_subunit.py	                        (rev 0)
+++ zope.testrunner/trunk/src/zope/testrunner/test_subunit.py	2010-07-19 22:06:28 UTC (rev 114856)
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2010 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Unit tests for the testrunner's subunit integration.
+"""
+
+
+import sys
+import unittest
+import formatter
+import subunit
+from StringIO import StringIO
+
+
+class TestSubunitTracebackPrinting(unittest.TestCase):
+
+    def makeByteStringFailure(self, text, encoding):
+        try:
+            # Note that this deliberately throws a string of bytes instead
+            # of a unicode object.  This simulates errors thrown by
+            # utf8-encoded doctests.
+            bytestr = text.encode(encoding)
+            self.fail(bytestr)
+        except self.failureException:
+            return sys.exc_info()
+
+    def setUp(self):
+        class FormatterOptions:
+            verbose=False
+        options = FormatterOptions()
+
+        self.output = StringIO()
+        self.subunit_formatter = formatter.SubunitOutputFormatter(
+            options, stream=self.output)
+
+    def test_print_failure_containing_utf8_bytestrings(self):
+        exc_info = self.makeByteStringFailure(unichr(6514), 'utf8')
+        self.subunit_formatter.test_failure(self, 0, exc_info)
+        assert "AssertionError: \xe1\xa5\xb2" in self.output.getvalue()
+
+    def test_print_error_containing_utf8_bytestrings(self):
+        exc_info = self.makeByteStringFailure(unichr(6514), 'utf8')
+        self.subunit_formatter.test_error(self, 0, exc_info)
+        assert "AssertionError: \xe1\xa5\xb2" in self.output.getvalue()
+
+    def test_print_failure_containing_latin1_bytestrings(self):
+        exc_info = self.makeByteStringFailure(unichr(241), 'latin1')
+        self.subunit_formatter.test_failure(self, 0, exc_info)
+        assert "AssertionError: \xef\xbf\xbd0" in self.output.getvalue()

Modified: zope.testrunner/trunk/src/zope/testrunner/testrunner-subunit.txt
===================================================================
--- zope.testrunner/trunk/src/zope/testrunner/testrunner-subunit.txt	2010-07-19 20:32:06 UTC (rev 114855)
+++ zope.testrunner/trunk/src/zope/testrunner/testrunner-subunit.txt	2010-07-19 22:06:28 UTC (rev 114856)
@@ -164,34 +164,34 @@
     ...     'test', '--subunit' , '--tests-pattern', '^sampletests_e$',
     ...     ]
     >>> testrunner.run_internal(defaults)
-    time: 2010-02-05 15:27:05.113541Z
+    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     test: zope.testrunner.layer.UnitTests:setUp
     tags: zope:layer
-    time: 2010-02-05 15:27:05.113545Z
+    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     successful: zope.testrunner.layer.UnitTests:setUp
     tags: zope:layer:zope.testrunner.layer.UnitTests
+    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     test: sample2.sampletests_e.eek
+    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     failure: sample2.sampletests_e.eek [ multipart
     Content-Type: text/x-traceback;charset=utf8,language=python
     traceback
-    4B6\r
+    NNN\r
     <BLANKLINE>
     Failed doctest test for sample2.sampletests_e.eek
-      File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 29, in eek
+     testrunner-ex/sample2/sampletests_e.py", Line NNN, in eek
     <BLANKLINE>
     ----------------------------------------------------------------------
-    File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 31, in sample2.sampletests_e.eek
+    File testrunner-ex/sample2/sampletests_e.py", Line NNN, in sample2.sampletests_e.eek
     Failed example:
         f()
     Exception raised:
         Traceback (most recent call last):
-          File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/doctest/__init__.py", line 1355, in __run
-            compileflags, 1) in test.globs
-          File "<doctest sample2.sampletests_e.eek[0]>", line 1, in <module>
+          File "<doctest sample2.sampletests_e.eek[0]>", Line NNN, in ?
             f()
-          File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 19, in f
+     testrunner-ex/sample2/sampletests_e.py", Line NNN, in f
             g()
-          File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 25, in g
+     testrunner-ex/sample2/sampletests_e.py", Line NNN, in g
             x = y + 1
            - __traceback_info__: I don't know what Y should be.
         NameError: global name 'y' is not defined
@@ -212,16 +212,14 @@
     error: sample2.sampletests_e.Test.test3 [ multipart
     Content-Type: text/x-traceback;charset=utf8,language=python
     traceback
-    29F\r
+    NNN\r
     <BLANKLINE>
     Traceback (most recent call last):
-      File "/usr/lib/python2.6/unittest.py", line 279, in run
-        testMethod()
-      File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 44, in test3
+     testrunner-ex/sample2/sampletests_e.py", Line NNN, in test3
         f()
-      File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 19, in f
+     testrunner-ex/sample2/sampletests_e.py", Line NNN, in f
         g()
-      File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/sampletests_e.py", line 25, in g
+     testrunner-ex/sample2/sampletests_e.py", Line NNN, in g
         x = y + 1
        - __traceback_info__: I don't know what Y should be.
     NameError: global name 'y' is not defined
@@ -239,36 +237,33 @@
     time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     test: e_txt
     time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
-    failure: e_txt [
-    multipart
+    failure: e_txt [ multipart
     Content-Type: text/x-traceback;charset=utf8,language=python
     traceback
-    329\r
+    NNN\r
     <BLANKLINE>
     Failed doctest test for e.txt
-      File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/e.txt", line 0
+     testrunner-ex/sample2/e.txt", line 0
     <BLANKLINE>
     ----------------------------------------------------------------------
-    File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/testrunner/testrunner-ex/sample2/e.txt", line 4, in e.txt
+    File testrunner-ex/sample2/e.txt", Line NNN, in e.txt
     Failed example:
         f()
     Exception raised:
         Traceback (most recent call last):
-          File "/home/jml/src/zope.testrunner/subunit-output-formatter/src/zope/testing/doctest/__init__.py", line 1355, in __run
-            compileflags, 1) in test.globs
-          File "<doctest e.txt[1]>", line 1, in <module>
+          File "<doctest e.txt[1]>", Line NNN, in ?
             f()
-          File "<doctest e.txt[0]>", line 2, in f
+          File "<doctest e.txt[0]>", Line NNN, in f
             return x
         NameError: global name 'x' is not defined
     0\r
     <BLANKLINE>
     ]
     tags: -zope:layer:zope.testrunner.layer.UnitTests
-    time: 2010-02-05 15:27:05.147082Z
+    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     test: zope.testrunner.layer.UnitTests:tearDown
     tags: zope:layer
-    time: 2010-02-05 15:27:05.147088Z
+    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
     successful: zope.testrunner.layer.UnitTests:tearDown
     True
 
@@ -337,8 +332,7 @@
     test: sample2.sampletests_i
     tags: zope:import_error
     error: sample2.sampletests_i [
-    Traceback (most recent call last):
-     testrunner-ex/sample2/sampletests_i.py", line 1
+      File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sampletests_i.py", line 1
         importx unittest
                        ^
     SyntaxError: invalid syntax
@@ -347,7 +341,7 @@
     tags: zope:import_error
     error: sample2.sample21.sampletests_i [
     Traceback (most recent call last):
-     testrunner-ex/sample2/sample21/sampletests_i.py", Line NNN, in ?
+      File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample21/sampletests_i.py", line 16, in <module>
         import zope.testrunner.huh
     ImportError: No module named huh
     ]
@@ -355,13 +349,13 @@
     tags: zope:import_error
     error: sample2.sample23.sampletests_i [
     Traceback (most recent call last):
-     testrunner-ex/sample2/sample23/sampletests_i.py", Line NNN, in ?
+      File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample23/sampletests_i.py", line 17, in <module>
         class Test(unittest.TestCase):
-     testrunner-ex/sample2/sample23/sampletests_i.py", Line NNN, in Test
+      File "/home/benji/workspace/all-the-trunks/zope.testrunner/src/zope/testrunner/testrunner-ex/sample2/sample23/sampletests_i.py", line 22, in Test
         raise TypeError('eek')
     TypeError: eek
     ]
-    time: YYYY-MM-DD HH:MM:SS.mmmmmmZ
+    time: 2010-07-19 21:27:16.708260Z
     test: samplelayers.Layer1:setUp
     tags: zope:layer
     ...

Modified: zope.testrunner/trunk/src/zope/testrunner/tests.py
===================================================================
--- zope.testrunner/trunk/src/zope/testrunner/tests.py	2010-07-19 20:32:06 UTC (rev 114855)
+++ zope.testrunner/trunk/src/zope/testrunner/tests.py	2010-07-19 22:06:28 UTC (rev 114856)
@@ -296,4 +296,8 @@
                     optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE,
                     checker=checker))
 
+        suites.append(
+            unittest.defaultTestLoader.loadTestsFromName(
+                'zope.testrunner.test_subunit'))
+
     return unittest.TestSuite(suites)



More information about the checkins mailing list