summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/subunit/MANIFEST.in21
-rw-r--r--lib/subunit/NEWS33
-rw-r--r--lib/subunit/README2
-rw-r--r--lib/subunit/configure.ac2
-rwxr-xr-xlib/subunit/filters/subunit-ls51
-rw-r--r--lib/subunit/perl/lib/Subunit.pm21
-rw-r--r--lib/subunit/python/subunit/__init__.py48
-rw-r--r--lib/subunit/python/subunit/details.py8
-rwxr-xr-xlib/subunit/python/subunit/run.py39
-rw-r--r--lib/subunit/python/subunit/test_results.py76
-rw-r--r--lib/subunit/python/subunit/tests/test_details.py3
-rw-r--r--lib/subunit/python/subunit/tests/test_test_protocol.py55
-rwxr-xr-xlib/subunit/setup.py59
13 files changed, 300 insertions, 118 deletions
diff --git a/lib/subunit/MANIFEST.in b/lib/subunit/MANIFEST.in
new file mode 100644
index 0000000000..7c449cf7e7
--- /dev/null
+++ b/lib/subunit/MANIFEST.in
@@ -0,0 +1,21 @@
+exclude .bzrignore
+exclude aclocal.m4
+prune autom4te.cache
+prune c
+prune c++
+prune compile
+exclude configure*
+exclude depcomp
+exclude INSTALL
+exclude install-sh
+exclude lib*
+exclude ltmain.sh
+prune m4
+exclude Makefile*
+exclude missing
+prune perl
+exclude py-compile
+prune shell
+prune python/iso8601
+exclude stamp-h1
+include NEWS
diff --git a/lib/subunit/NEWS b/lib/subunit/NEWS
index 7c933c8f6e..1af8ef5708 100644
--- a/lib/subunit/NEWS
+++ b/lib/subunit/NEWS
@@ -5,9 +5,20 @@ subunit release notes
NEXT (In development)
---------------------
+0.0.6
+-----
+
+This release of subunit fixes a number of unicode related bugs. This depends on
+testtools 0.9.4 and will not function without it. Thanks to Tres Seaver there
+is also an optional native setup.py file for use with easy_install and the
+like.
+
BUG FIXES
~~~~~~~~~
+* Be consistent about delivering unicode content to testtools StringException
+ class which has become (appropriately) conservative. (Robert Collins)
+
* Fix incorrect reference to subunit_test_failf in c/README.
(Brad Hards, #524341)
@@ -15,6 +26,28 @@ BUG FIXES
is purely cosmetic as the parameters are passed down with no interpretation.
(Robert Collins, #537611)
+* Old style tracebacks with no encoding info are now treated as UTF8 rather
+ than some-random-codec-like-ascii. (Robert Collins)
+
+* On windows, ProtocolTestCase and TestProtocolClient will set their streams to
+ binary mode by calling into msvcrt; this avoids having their input or output
+ mangled by the default line ending translation on that platform.
+ (Robert Collins, Martin [gz], #579296)
+
+IMPROVEMENTS
+~~~~~~~~~~~~
+
+* Subunit now has a setup.py for python deployments that are not using
+ distribution packages. (Tres Seaver, #538181)
+
+* Subunit now supports test discovery by building on the testtools support for
+ it. You can take advantage of it with "python -m subunit.run discover [path]"
+ and see "python -m subunit.run discover --help" for more options.
+
+* Subunit now uses the improved unicode support in testtools when outputting
+ non-details based test information; this should consistently UTF8 encode such
+ strings.
+
0.0.5
-----
diff --git a/lib/subunit/README b/lib/subunit/README
index 9740d013a5..6ac258485f 100644
--- a/lib/subunit/README
+++ b/lib/subunit/README
@@ -160,7 +160,7 @@ tags: [-]TAG ...
time: YYYY-MM-DD HH:MM:SSZ
DETAILS ::= BRACKETED | MULTIPART
-BRACKETED ::= '[' CR lines ']' CR
+BRACKETED ::= '[' CR UTF8-lines ']' CR
MULTIPART ::= '[ multipart' CR PART* ']' CR
PART ::= PART_TYPE CR NAME CR PART_BYTES CR
PART_TYPE ::= Content-Type: type/sub-type(;parameter=value,parameter=value)
diff --git a/lib/subunit/configure.ac b/lib/subunit/configure.ac
index 496aea5719..5696573464 100644
--- a/lib/subunit/configure.ac
+++ b/lib/subunit/configure.ac
@@ -1,6 +1,6 @@
m4_define([SUBUNIT_MAJOR_VERSION], [0])
m4_define([SUBUNIT_MINOR_VERSION], [0])
-m4_define([SUBUNIT_MICRO_VERSION], [5])
+m4_define([SUBUNIT_MICRO_VERSION], [6])
m4_define([SUBUNIT_VERSION],
m4_defn([SUBUNIT_MAJOR_VERSION]).m4_defn([SUBUNIT_MINOR_VERSION]).m4_defn([SUBUNIT_MICRO_VERSION]))
AC_PREREQ([2.59])
diff --git a/lib/subunit/filters/subunit-ls b/lib/subunit/filters/subunit-ls
index 15ec4b01e6..86461347d3 100755
--- a/lib/subunit/filters/subunit-ls
+++ b/lib/subunit/filters/subunit-ls
@@ -18,58 +18,9 @@
from optparse import OptionParser
import sys
-import unittest
from subunit import DiscardStream, ProtocolTestCase
-
-class TestIdPrintingResult(unittest.TestResult):
-
- def __init__(self, stream, show_times=False):
- """Create a FilterResult object outputting to stream."""
- unittest.TestResult.__init__(self)
- self._stream = stream
- self.failed_tests = 0
- self.__time = 0
- self.show_times = show_times
- self._test = None
- self._test_duration = 0
-
- def addError(self, test, err):
- self.failed_tests += 1
- self._test = test
-
- def addFailure(self, test, err):
- self.failed_tests += 1
- self._test = test
-
- def addSuccess(self, test):
- self._test = test
-
- def reportTest(self, test, duration):
- if self.show_times:
- seconds = duration.seconds
- seconds += duration.days * 3600 * 24
- seconds += duration.microseconds / 1000000.0
- self._stream.write(test.id() + ' %0.3f\n' % seconds)
- else:
- self._stream.write(test.id() + '\n')
-
- def startTest(self, test):
- self._start_time = self._time()
-
- def stopTest(self, test):
- test_duration = self._time() - self._start_time
- self.reportTest(self._test, test_duration)
-
- def time(self, time):
- self.__time = time
-
- def _time(self):
- return self.__time
-
- def wasSuccessful(self):
- "Tells whether or not this result was a success"
- return self.failed_tests == 0
+from subunit.test_results import TestIdPrintingResult
parser = OptionParser(description=__doc__)
diff --git a/lib/subunit/perl/lib/Subunit.pm b/lib/subunit/perl/lib/Subunit.pm
index 05206748e2..dac4a2601d 100644
--- a/lib/subunit/perl/lib/Subunit.pm
+++ b/lib/subunit/perl/lib/Subunit.pm
@@ -159,4 +159,25 @@ sub report_time($)
printf "time: %04d-%02d-%02d %02d:%02d:%02dZ\n", $year+1900, $mon, $mday, $hour, $min, $sec;
}
+sub progress_pop()
+{
+ print "progress: pop\n";
+}
+
+sub progress_push()
+{
+ print "progress: push\n";
+}
+
+sub progress($;$)
+{
+ my ($count, $whence) = @_;
+
+ unless(defined($whence)) {
+ $whence = "";
+ }
+
+ print "progress: $whence$count\n";
+}
+
1;
diff --git a/lib/subunit/python/subunit/__init__.py b/lib/subunit/python/subunit/__init__.py
index 4b25ca3a39..b6f0108f61 100644
--- a/lib/subunit/python/subunit/__init__.py
+++ b/lib/subunit/python/subunit/__init__.py
@@ -133,9 +133,7 @@ try:
except ImportError:
raise ImportError ("testtools.testresult.real does not contain "
"_StringException, check your version.")
-
-
-from testtools.testresult.real import _StringException
+from testtools import testresult
import chunked, details, test_results
@@ -244,7 +242,7 @@ class _ParserState(object):
def lostConnection(self):
"""Connection lost."""
- self.parser._lostConnectionInTest('unknown state of ')
+ self.parser._lostConnectionInTest(u'unknown state of ')
def startTest(self, offset, line):
"""A test start command received."""
@@ -324,7 +322,7 @@ class _InTest(_ParserState):
def lostConnection(self):
"""Connection lost."""
- self.parser._lostConnectionInTest('')
+ self.parser._lostConnectionInTest(u'')
class _OutSideTest(_ParserState):
@@ -359,7 +357,7 @@ class _ReadingDetails(_ParserState):
def lostConnection(self):
"""Connection lost."""
- self.parser._lostConnectionInTest('%s report of ' %
+ self.parser._lostConnectionInTest(u'%s report of ' %
self._outcome_label())
def _outcome_label(self):
@@ -501,7 +499,7 @@ class TestProtocolServer(object):
self._state.lineReceived(line)
def _lostConnectionInTest(self, state_string):
- error_string = "lost connection during %stest '%s'" % (
+ error_string = u"lost connection during %stest '%s'" % (
state_string, self.current_test_description)
self.client.addError(self._current_test, RemoteError(error_string))
self.client.stopTest(self._current_test)
@@ -531,7 +529,7 @@ class TestProtocolServer(object):
self._stream.write(line)
-class TestProtocolClient(unittest.TestResult):
+class TestProtocolClient(testresult.TestResult):
"""A TestResult which generates a subunit stream for a test run.
# Get a TestSuite or TestCase to run
@@ -550,8 +548,9 @@ class TestProtocolClient(unittest.TestResult):
"""
def __init__(self, stream):
- unittest.TestResult.__init__(self)
+ testresult.TestResult.__init__(self)
self._stream = stream
+ _make_stream_binary(stream)
def addError(self, test, error=None, details=None):
"""Report an error in test test.
@@ -618,8 +617,10 @@ class TestProtocolClient(unittest.TestResult):
raise ValueError
if error is not None:
self._stream.write(" [\n")
- for line in self._exc_info_to_string(error, test).splitlines():
- self._stream.write("%s\n" % line)
+ # XXX: this needs to be made much stricter, along the lines of
+ # Martin[gz]'s work in testtools. Perhaps subunit can use that?
+ for line in self._exc_info_to_unicode(error, test).splitlines():
+ self._stream.write(("%s\n" % line).encode('utf8'))
else:
self._write_details(details)
self._stream.write("]\n")
@@ -704,7 +705,7 @@ class TestProtocolClient(unittest.TestResult):
"""Obey the testtools result.done() interface."""
-def RemoteError(description=""):
+def RemoteError(description=u""):
return (_StringException, _StringException(description), None)
@@ -754,7 +755,7 @@ class RemotedTestCase(unittest.TestCase):
def run(self, result=None):
if result is None: result = self.defaultTestResult()
result.startTest(self)
- result.addError(self, RemoteError("Cannot run RemotedTestCases.\n"))
+ result.addError(self, RemoteError(u"Cannot run RemotedTestCases.\n"))
result.stopTest(self)
def _strclass(self):
@@ -784,7 +785,7 @@ class ExecTestCase(unittest.TestCase):
def debug(self):
"""Run the test without collecting errors in a TestResult"""
- self._run(unittest.TestResult())
+ self._run(testresult.TestResult())
def _run(self, result):
protocol = TestProtocolServer(result)
@@ -816,7 +817,7 @@ class IsolatedTestSuite(unittest.TestSuite):
"""
def run(self, result=None):
- if result is None: result = unittest.TestResult()
+ if result is None: result = testresult.TestResult()
run_isolated(unittest.TestSuite, self, result)
@@ -1045,8 +1046,10 @@ class ProtocolTestCase(object):
subunit input is not forwarded.
"""
self._stream = stream
+ _make_stream_binary(stream)
self._passthrough = passthrough
self._forward = forward
+ _make_stream_binary(forward)
def __call__(self, result=None):
return self.run(result)
@@ -1062,7 +1065,7 @@ class ProtocolTestCase(object):
protocol.lostConnection()
-class TestResultStats(unittest.TestResult):
+class TestResultStats(testresult.TestResult):
"""A pyunit TestResult interface implementation for making statistics.
:ivar total_tests: The total tests seen.
@@ -1073,7 +1076,7 @@ class TestResultStats(unittest.TestResult):
def __init__(self, stream):
"""Create a TestResultStats which outputs to stream."""
- unittest.TestResult.__init__(self)
+ testresult.TestResult.__init__(self)
self._stream = stream
self.failed_tests = 0
self.skipped_tests = 0
@@ -1124,3 +1127,14 @@ def get_default_formatter():
else:
return sys.stdout
+
+def _make_stream_binary(stream):
+ """Ensure that a stream will be binary safe. See _make_binary_on_windows."""
+ if getattr(stream, 'fileno', None) is not None:
+ _make_binary_on_windows(stream.fileno())
+
+def _make_binary_on_windows(fileno):
+ """Win32 mangles \r\n to \n and that breaks streams. See bug lp:505078."""
+ if sys.platform == "win32":
+ import msvcrt
+ msvcrt.setmode(fileno, os.O_BINARY)
diff --git a/lib/subunit/python/subunit/details.py b/lib/subunit/python/subunit/details.py
index 65a04046d9..a37b2acb93 100644
--- a/lib/subunit/python/subunit/details.py
+++ b/lib/subunit/python/subunit/details.py
@@ -47,8 +47,12 @@ class SimpleDetailsParser(DetailsParser):
def get_details(self, style=None):
result = {}
if not style:
+ # We know that subunit/testtools serialise [] formatted
+ # tracebacks as utf8, but perhaps we need a ReplacingContent
+ # or something like that.
result['traceback'] = content.Content(
- content_type.ContentType("text", "x-traceback"),
+ content_type.ContentType("text", "x-traceback",
+ {"charset": "utf8"}),
lambda:[self._message])
else:
if style == 'skip':
@@ -92,7 +96,7 @@ class MultipartDetailsParser(DetailsParser):
residue = self._chunk_parser.write(line)
if residue is not None:
# Line based use always ends on no residue.
- assert residue == ''
+ assert residue == '', 'residue: %r' % (residue,)
body = self._body
self._details[self._name] = content.Content(
self._content_type, lambda:[body.getvalue()])
diff --git a/lib/subunit/python/subunit/run.py b/lib/subunit/python/subunit/run.py
index e57939fdfa..daa241a606 100755
--- a/lib/subunit/python/subunit/run.py
+++ b/lib/subunit/python/subunit/run.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python
#
# Simple subunit testrunner for python
# Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
@@ -23,6 +23,13 @@
import sys
from subunit import TestProtocolClient, get_default_formatter
+from testtools.run import (
+ BUFFEROUTPUT,
+ CATCHBREAK,
+ FAILFAST,
+ TestProgram,
+ USAGE_AS_MAIN,
+ )
class SubunitTestRunner(object):
@@ -36,12 +43,30 @@ class SubunitTestRunner(object):
return result
+class SubunitTestProgram(TestProgram):
+
+ USAGE = USAGE_AS_MAIN
+
+ def usageExit(self, msg=None):
+ if msg:
+ print msg
+ usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
+ 'buffer': ''}
+ if self.failfast != False:
+ usage['failfast'] = FAILFAST
+ if self.catchbreak != False:
+ usage['catchbreak'] = CATCHBREAK
+ if self.buffer != False:
+ usage['buffer'] = BUFFEROUTPUT
+ usage_text = self.USAGE % usage
+ usage_lines = usage_text.split('\n')
+ usage_lines.insert(2, "Run a test suite with a subunit reporter.")
+ usage_lines.insert(3, "")
+ print('\n'.join(usage_lines))
+ sys.exit(2)
+
+
if __name__ == '__main__':
- import optparse
- from unittest import TestProgram
- parser = optparse.OptionParser(__doc__)
- args = parser.parse_args()[1]
stream = get_default_formatter()
runner = SubunitTestRunner(stream)
- program = TestProgram(module=None, argv=[sys.argv[0]] + args,
- testRunner=runner)
+ SubunitTestProgram(module=None, argv=sys.argv, testRunner=runner)
diff --git a/lib/subunit/python/subunit/test_results.py b/lib/subunit/python/subunit/test_results.py
index 6cf84c519e..1c91daadc6 100644
--- a/lib/subunit/python/subunit/test_results.py
+++ b/lib/subunit/python/subunit/test_results.py
@@ -6,7 +6,7 @@
# license at the users choice. A copy of both licenses are available in the
# project source as Apache-2.0 and BSD. You may not use this file except in
# compliance with one of these two licences.
-#
+#
# Unless required by applicable law or agreed to in writing, software
# distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -21,16 +21,14 @@ import datetime
import iso8601
import testtools
-import subunit
-
# NOT a TestResult, because we are implementing the interface, not inheriting
# it.
class TestResultDecorator(object):
"""General pass-through decorator.
- This provides a base that other TestResults can inherit from to
- gain basic forwarding functionality. It also takes care of
+ This provides a base that other TestResults can inherit from to
+ gain basic forwarding functionality. It also takes care of
handling the case where the target doesn't support newer methods
or features by degrading them.
"""
@@ -201,11 +199,11 @@ class TestResultFilter(TestResultDecorator):
"""A pyunit TestResult interface implementation which filters tests.
Tests that pass the filter are handed on to another TestResult instance
- for further processing/reporting. To obtain the filtered results,
+ for further processing/reporting. To obtain the filtered results,
the other instance must be interrogated.
:ivar result: The result that tests are passed to after filtering.
- :ivar filter_predicate: The callback run to decide whether to pass
+ :ivar filter_predicate: The callback run to decide whether to pass
a result.
"""
@@ -213,7 +211,7 @@ class TestResultFilter(TestResultDecorator):
filter_success=True, filter_skip=False,
filter_predicate=None):
"""Create a FilterResult object filtering to result.
-
+
:param filter_error: Filter out errors.
:param filter_failure: Filter out failures.
:param filter_success: Filter out successful tests.
@@ -238,9 +236,9 @@ class TestResultFilter(TestResultDecorator):
self._current_test_filtered = None
# The (new, gone) tags for the current test.
self._current_test_tags = None
-
+
def addError(self, test, err=None, details=None):
- if (not self._filter_error and
+ if (not self._filter_error and
self.filter_predicate(test, 'error', err, details)):
self.decorated.startTest(test)
self.decorated.addError(test, err, details=details)
@@ -288,17 +286,17 @@ class TestResultFilter(TestResultDecorator):
def startTest(self, test):
"""Start a test.
-
+
Not directly passed to the client, but used for handling of tags
correctly.
"""
self._current_test = test
self._current_test_filtered = False
self._current_test_tags = set(), set()
-
+
def stopTest(self, test):
"""Stop a test.
-
+
Not directly passed to the client, but used for handling of tags
correctly.
"""
@@ -316,7 +314,7 @@ class TestResultFilter(TestResultDecorator):
Adds and removes tags as appropriate. If a test is currently running,
tags are not affected for subsequent tests.
-
+
:param new_tags: Tags to add,
:param gone_tags: Tags to remove.
"""
@@ -332,3 +330,53 @@ class TestResultFilter(TestResultDecorator):
if id.startswith("subunit.RemotedTestCase."):
return id[len("subunit.RemotedTestCase."):]
return id
+
+
+class TestIdPrintingResult(testtools.TestResult):
+
+ def __init__(self, stream, show_times=False):
+ """Create a FilterResult object outputting to stream."""
+ testtools.TestResult.__init__(self)
+ self._stream = stream
+ self.failed_tests = 0
+ self.__time = 0
+ self.show_times = show_times
+ self._test = None
+ self._test_duration = 0
+
+ def addError(self, test, err):
+ self.failed_tests += 1
+ self._test = test
+
+ def addFailure(self, test, err):
+ self.failed_tests += 1
+ self._test = test
+
+ def addSuccess(self, test):
+ self._test = test
+
+ def reportTest(self, test, duration):
+ if self.show_times:
+ seconds = duration.seconds
+ seconds += duration.days * 3600 * 24
+ seconds += duration.microseconds / 1000000.0
+ self._stream.write(test.id() + ' %0.3f\n' % seconds)
+ else:
+ self._stream.write(test.id() + '\n')
+
+ def startTest(self, test):
+ self._start_time = self._time()
+
+ def stopTest(self, test):
+ test_duration = self._time() - self._start_time
+ self.reportTest(self._test, test_duration)
+
+ def time(self, time):
+ self.__time = time
+
+ def _time(self):
+ return self.__time
+
+ def wasSuccessful(self):
+ "Tells whether or not this result was a success"
+ return self.failed_tests == 0
diff --git a/lib/subunit/python/subunit/tests/test_details.py b/lib/subunit/python/subunit/tests/test_details.py
index 2700d4afc7..41c32129d0 100644
--- a/lib/subunit/python/subunit/tests/test_details.py
+++ b/lib/subunit/python/subunit/tests/test_details.py
@@ -51,7 +51,8 @@ class TestSimpleDetails(unittest.TestCase):
traceback = ""
expected = {}
expected['traceback'] = content.Content(
- content_type.ContentType("text", "x-traceback"),
+ content_type.ContentType("text", "x-traceback",
+ {'charset': 'utf8'}),
lambda:[""])
found = parser.get_details()
self.assertEqual(expected.keys(), found.keys())
diff --git a/lib/subunit/python/subunit/tests/test_test_protocol.py b/lib/subunit/python/subunit/tests/test_test_protocol.py
index f10380b09b..e1287b6c81 100644
--- a/lib/subunit/python/subunit/tests/test_test_protocol.py
+++ b/lib/subunit/python/subunit/tests/test_test_protocol.py
@@ -102,6 +102,9 @@ class TestTestProtocolServerPipe(unittest.TestCase):
"------------\n\n")])
self.assertEqual(client.testsRun, 3)
+ def test_non_test_characters_forwarded_immediately(self):
+ pass
+
class TestTestProtocolServerStartTest(unittest.TestCase):
@@ -243,7 +246,8 @@ class TestTestProtocolServerPassThrough(unittest.TestCase):
self.protocol.lineReceived("]\n")
self.assertEqual(self.stdout.getvalue(), "")
details = {}
- details['traceback'] = Content(ContentType("text", "x-traceback"),
+ details['traceback'] = Content(ContentType("text", "x-traceback",
+ {'charset': 'utf8'}),
lambda:[
"test old mcdonald\n"
"failure a\n"
@@ -285,7 +289,7 @@ class TestTestProtocolServerLostConnection(unittest.TestCase):
self.protocol.lineReceived("test old mcdonald\n")
self.protocol.lostConnection()
failure = subunit.RemoteError(
- "lost connection during test 'old mcdonald'")
+ u"lost connection during test 'old mcdonald'")
self.assertEqual([
('startTest', self.test),
('addError', self.test, failure),
@@ -298,7 +302,7 @@ class TestTestProtocolServerLostConnection(unittest.TestCase):
self.protocol.lostConnection()
self.assertEqual([
('startTest', self.test),
- ('addError', self.test, subunit.RemoteError("")),
+ ('addError', self.test, subunit.RemoteError(u"")),
('stopTest', self.test),
], self.client._events)
@@ -307,7 +311,7 @@ class TestTestProtocolServerLostConnection(unittest.TestCase):
self.protocol.lineReceived("%s old mcdonald %s" % (outcome, opening))
self.protocol.lostConnection()
failure = subunit.RemoteError(
- "lost connection during %s report of test 'old mcdonald'" %
+ u"lost connection during %s report of test 'old mcdonald'" %
outcome)
self.assertEqual([
('startTest', self.test),
@@ -327,7 +331,7 @@ class TestTestProtocolServerLostConnection(unittest.TestCase):
self.protocol.lostConnection()
self.assertEqual([
('startTest', self.test),
- ('addFailure', self.test, subunit.RemoteError("")),
+ ('addFailure', self.test, subunit.RemoteError(u"")),
('stopTest', self.test),
], self.client._events)
@@ -411,8 +415,8 @@ class TestTestProtocolServerAddError(unittest.TestCase):
self.protocol.lineReceived("error mcdonalds farm [\n")
self.protocol.lineReceived("]\n")
details = {}
- details['traceback'] = Content(ContentType("text", "x-traceback"),
- lambda:[""])
+ details['traceback'] = Content(ContentType("text", "x-traceback",
+ {'charset': 'utf8'}), lambda:[""])
self.assertEqual([
('startTest', self.test),
('addError', self.test, details),
@@ -424,8 +428,8 @@ class TestTestProtocolServerAddError(unittest.TestCase):
self.protocol.lineReceived(" ]\n")
self.protocol.lineReceived("]\n")
details = {}
- details['traceback'] = Content(ContentType("text", "x-traceback"),
- lambda:["]\n"])
+ details['traceback'] = Content(ContentType("text", "x-traceback",
+ {'charset': 'utf8'}), lambda:["]\n"])
self.assertEqual([
('startTest', self.test),
('addError', self.test, details),
@@ -469,8 +473,8 @@ class TestTestProtocolServerAddFailure(unittest.TestCase):
self.protocol.lineReceived("failure mcdonalds farm [\n")
self.protocol.lineReceived("]\n")
details = {}
- details['traceback'] = Content(ContentType("text", "x-traceback"),
- lambda:[""])
+ details['traceback'] = Content(ContentType("text", "x-traceback",
+ {'charset': 'utf8'}), lambda:[""])
self.assertFailure(details)
def failure_quoted_bracket(self, keyword):
@@ -478,8 +482,8 @@ class TestTestProtocolServerAddFailure(unittest.TestCase):
self.protocol.lineReceived(" ]\n")
self.protocol.lineReceived("]\n")
details = {}
- details['traceback'] = Content(ContentType("text", "x-traceback"),
- lambda:["]\n"])
+ details['traceback'] = Content(ContentType("text", "x-traceback",
+ {'charset': 'utf8'}), lambda:["]\n"])
self.assertFailure(details)
def test_failure_quoted_bracket(self):
@@ -535,12 +539,13 @@ class TestTestProtocolServerAddxFail(unittest.TestCase):
details = {}
if error_message is not None:
details['traceback'] = Content(
- ContentType("text", "x-traceback"), lambda:[error_message])
+ ContentType("text", "x-traceback", {'charset': 'utf8'}),
+ lambda:[error_message])
if isinstance(self.client, ExtendedTestResult):
value = details
else:
if error_message is not None:
- value = subunit.RemoteError('Text attachment: traceback\n'
+ value = subunit.RemoteError(u'Text attachment: traceback\n'
'------------\n' + error_message + '------------\n')
else:
value = subunit.RemoteError()
@@ -845,15 +850,15 @@ class TestRemotedTestCase(unittest.TestCase):
class TestRemoteError(unittest.TestCase):
def test_eq(self):
- error = subunit.RemoteError("Something went wrong")
- another_error = subunit.RemoteError("Something went wrong")
- different_error = subunit.RemoteError("boo!")
+ error = subunit.RemoteError(u"Something went wrong")
+ another_error = subunit.RemoteError(u"Something went wrong")
+ different_error = subunit.RemoteError(u"boo!")
self.assertEqual(error, another_error)
self.assertNotEqual(error, different_error)
self.assertNotEqual(different_error, another_error)
def test_empty_constructor(self):
- self.assertEqual(subunit.RemoteError(), subunit.RemoteError(""))
+ self.assertEqual(subunit.RemoteError(), subunit.RemoteError(u""))
class TestExecTestCase(unittest.TestCase):
@@ -887,8 +892,8 @@ class TestExecTestCase(unittest.TestCase):
mcdonald = subunit.RemotedTestCase("old mcdonald")
bing = subunit.RemotedTestCase("bing crosby")
bing_details = {}
- bing_details['traceback'] = Content(ContentType("text", "x-traceback"),
- lambda:["foo.c:53:ERROR invalid state\n"])
+ bing_details['traceback'] = Content(ContentType("text", "x-traceback",
+ {'charset': 'utf8'}), lambda:["foo.c:53:ERROR invalid state\n"])
an_error = subunit.RemotedTestCase("an error")
error_details = {}
self.assertEqual([
@@ -1004,7 +1009,7 @@ class TestTestProtocolClient(unittest.TestCase):
ContentType('text', 'plain'), lambda:['serialised\nform'])}
self.sample_tb_details = dict(self.sample_details)
self.sample_tb_details['traceback'] = TracebackContent(
- subunit.RemoteError("boo qux"), self.test)
+ subunit.RemoteError(u"boo qux"), self.test)
def test_start_test(self):
"""Test startTest on a TestProtocolClient."""
@@ -1034,7 +1039,7 @@ class TestTestProtocolClient(unittest.TestCase):
def test_add_failure(self):
"""Test addFailure on a TestProtocolClient."""
self.protocol.addFailure(
- self.test, subunit.RemoteError("boo qux"))
+ self.test, subunit.RemoteError(u"boo qux"))
self.assertEqual(
self.io.getvalue(),
('failure: %s [\n' + _remote_exception_str + ': boo qux\n]\n')
@@ -1058,7 +1063,7 @@ class TestTestProtocolClient(unittest.TestCase):
def test_add_error(self):
"""Test stopTest on a TestProtocolClient."""
self.protocol.addError(
- self.test, subunit.RemoteError("phwoar crikey"))
+ self.test, subunit.RemoteError(u"phwoar crikey"))
self.assertEqual(
self.io.getvalue(),
('error: %s [\n' +
@@ -1083,7 +1088,7 @@ class TestTestProtocolClient(unittest.TestCase):
def test_add_expected_failure(self):
"""Test addExpectedFailure on a TestProtocolClient."""
self.protocol.addExpectedFailure(
- self.test, subunit.RemoteError("phwoar crikey"))
+ self.test, subunit.RemoteError(u"phwoar crikey"))
self.assertEqual(
self.io.getvalue(),
('xfail: %s [\n' +
diff --git a/lib/subunit/setup.py b/lib/subunit/setup.py
new file mode 100755
index 0000000000..b9d6c5acc2
--- /dev/null
+++ b/lib/subunit/setup.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+try:
+ # If the user has setuptools / distribute installed, use it
+ from setuptools import setup
+except ImportError:
+ # Otherwise, fall back to distutils.
+ from distutils.core import setup
+ extra = {}
+else:
+ extra = {
+ 'install_requires': [
+ 'testtools',
+ ]
+ }
+
+try:
+ # Assume we are in a distribution, which has PKG-INFO
+ version_lines = [x for x in open('PKG-INFO').readlines()
+ if x.startswith('Version:')]
+ version_line = version_lines and version_lines[-1] or 'VERSION = 0.0'
+ VERSION = version_line.split(':')[1].strip()
+
+except IOError:
+ # Must be a development checkout, so use the Makefile
+ version_lines = [x for x in open('Makefile').readlines()
+ if x.startswith('VERSION')]
+ version_line = version_lines and version_lines[-1] or 'VERSION = 0.0'
+ VERSION = version_line.split('=')[1].strip()
+
+
+setup(
+ name='python-subunit',
+ version=VERSION,
+ description=('Python implementation of subunit test streaming protocol'),
+ long_description=open('README').read(),
+ classifiers=[
+ 'Intended Audience :: Developers',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Testing',
+ ],
+ keywords='python test streaming',
+ author='Robert Collins',
+ author_email='subunit-dev@lists.launchpad.net',
+ url='http://launchpad.net/subunit',
+ packages=['subunit'],
+ package_dir={'subunit': 'python/subunit'},
+ scripts = [
+ 'filters/subunit2gtk',
+ 'filters/subunit2junitxml',
+ 'filters/subunit2pyunit',
+ 'filters/subunit-filter',
+ 'filters/subunit-ls',
+ 'filters/subunit-notify',
+ 'filters/subunit-stats',
+ 'filters/subunit-tags',
+ 'filters/tap2subunit',
+ ],
+ **extra
+)