From 5de2ec0def3e4ad0ead20b426e81509fd8e48c6d Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sat, 4 Sep 2010 23:04:28 +0200 Subject: subunit: Import latest upstream. --- lib/subunit/MANIFEST.in | 21 ++++++ lib/subunit/NEWS | 33 ++++++++++ lib/subunit/README | 2 +- lib/subunit/configure.ac | 2 +- lib/subunit/filters/subunit-ls | 51 +-------------- lib/subunit/perl/lib/Subunit.pm | 21 ++++++ lib/subunit/python/subunit/__init__.py | 48 +++++++++----- lib/subunit/python/subunit/details.py | 8 ++- lib/subunit/python/subunit/run.py | 39 +++++++++-- lib/subunit/python/subunit/test_results.py | 76 ++++++++++++++++++---- lib/subunit/python/subunit/tests/test_details.py | 3 +- .../python/subunit/tests/test_test_protocol.py | 55 +++++++++------- lib/subunit/setup.py | 59 +++++++++++++++++ 13 files changed, 300 insertions(+), 118 deletions(-) create mode 100644 lib/subunit/MANIFEST.in create mode 100755 lib/subunit/setup.py (limited to 'lib/subunit') 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 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 +) -- cgit