The branch, master has been updated via a53caea subunit: Update to latest upstream version. via 7b654a8 testtools: Update to latest version. from d10c737 smbd_open_one_socket does not use the messaging_context variable so why pass it in?
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit a53caea7a27c8616cabfc2e5bdf91a90e35891d5 Author: Jelmer Vernooij <jel...@samba.org> Date: Wed Nov 14 09:47:16 2012 +0100 subunit: Update to latest upstream version. Autobuild-User(master): Jelmer Vernooij <jel...@samba.org> Autobuild-Date(master): Wed Nov 14 12:11:58 CET 2012 on sn-devel-104 commit 7b654a8c180a6467147189332916a5e56634b5af Author: Jelmer Vernooij <jel...@samba.org> Date: Wed Nov 14 09:46:53 2012 +0100 testtools: Update to latest version. ----------------------------------------------------------------------- Summary of changes: lib/subunit/Makefile.am | 2 + lib/subunit/NEWS | 37 +- lib/subunit/configure.ac | 2 +- lib/subunit/filters/subunit-filter | 148 ++- lib/subunit/filters/subunit-notify | 53 +- lib/subunit/filters/subunit2csv | 23 + lib/subunit/filters/subunit2junitxml | 42 +- lib/subunit/perl/Makefile.PL.in | 1 + lib/subunit/python/subunit/__init__.py | 71 +- lib/subunit/python/subunit/filters.py | 125 ++ lib/subunit/python/subunit/iso8601.py | 2 +- lib/subunit/python/subunit/test_results.py | 404 +++++-- lib/subunit/python/subunit/tests/sample-script.py | 20 +- .../python/subunit/tests/sample-two-script.py | 8 +- lib/subunit/python/subunit/tests/test_run.py | 4 +- .../python/subunit/tests/test_subunit_filter.py | 170 +++- .../python/subunit/tests/test_test_protocol.py | 38 +- .../python/subunit/tests/test_test_results.py | 272 ++++- lib/subunit/runtests.py | 2 +- lib/subunit/setup.py | 25 +- lib/subunit/shell/tests/test_function_output.sh | 12 +- lib/testtools/.testr.conf | 2 +- lib/testtools/MANIFEST.in | 2 - lib/testtools/NEWS | 213 ++++ lib/testtools/README | 5 +- lib/testtools/doc/for-framework-folk.rst | 8 + lib/testtools/doc/for-test-authors.rst | 21 + lib/testtools/doc/hacking.rst | 9 +- lib/testtools/doc/index.rst | 5 +- lib/testtools/doc/overview.rst | 10 +- lib/testtools/scripts/all-pythons | 2 +- lib/testtools/setup.py | 11 +- lib/testtools/testtools/__init__.py | 12 +- lib/testtools/testtools/_compat3x.py | 2 +- lib/testtools/testtools/compat.py | 1 - lib/testtools/testtools/content.py | 91 ++- lib/testtools/testtools/content_type.py | 6 +- lib/testtools/testtools/deferredruntest.py | 2 +- lib/testtools/testtools/helpers.py | 34 +- lib/testtools/testtools/matchers.py | 1284 ------------------- lib/testtools/testtools/matchers/__init__.py | 113 ++ lib/testtools/testtools/matchers/_basic.py | 315 +++++ .../testtools/matchers/_datastructures.py | 228 ++++ lib/testtools/testtools/matchers/_dict.py | 259 ++++ lib/testtools/testtools/matchers/_doctest.py | 104 ++ lib/testtools/testtools/matchers/_exception.py | 124 ++ lib/testtools/testtools/matchers/_filesystem.py | 192 +++ lib/testtools/testtools/matchers/_higherorder.py | 269 ++++ lib/testtools/testtools/matchers/_impl.py | 175 +++ lib/testtools/testtools/run.py | 2 +- lib/testtools/testtools/tags.py | 34 + lib/testtools/testtools/testcase.py | 98 +- lib/testtools/testtools/testresult/__init__.py | 8 +- lib/testtools/testtools/testresult/doubles.py | 21 + lib/testtools/testtools/testresult/real.py | 348 +++++- lib/testtools/testtools/tests/__init__.py | 6 +- lib/testtools/testtools/tests/helpers.py | 36 +- lib/testtools/testtools/tests/matchers/__init__.py | 29 + lib/testtools/testtools/tests/matchers/helpers.py | 42 + .../testtools/tests/matchers/test_basic.py | 374 ++++++ .../tests/matchers/test_datastructures.py | 209 +++ .../testtools/tests/matchers/test_dict.py | 222 ++++ .../testtools/tests/matchers/test_doctest.py | 82 ++ .../testtools/tests/matchers/test_exception.py | 192 +++ .../testtools/tests/matchers/test_filesystem.py | 243 ++++ .../testtools/tests/matchers/test_higherorder.py | 194 +++ .../testtools/tests/matchers/test_impl.py | 132 ++ lib/testtools/testtools/tests/test_compat.py | 42 +- lib/testtools/testtools/tests/test_content.py | 19 +- lib/testtools/testtools/tests/test_content_type.py | 16 +- .../testtools/tests/test_deferredruntest.py | 13 + .../testtools/tests/test_fixturesupport.py | 8 +- lib/testtools/testtools/tests/test_helpers.py | 29 +- lib/testtools/testtools/tests/test_matchers.py | 1325 -------------------- lib/testtools/testtools/tests/test_tags.py | 84 ++ lib/testtools/testtools/tests/test_testcase.py | 113 ++- lib/testtools/testtools/tests/test_testresult.py | 652 +++++++++-- lib/testtools/testtools/tests/test_testsuite.py | 41 +- lib/testtools/testtools/testsuite.py | 25 +- 79 files changed, 6350 insertions(+), 3249 deletions(-) create mode 100755 lib/subunit/filters/subunit2csv create mode 100644 lib/subunit/python/subunit/filters.py delete mode 100644 lib/testtools/testtools/matchers.py create mode 100644 lib/testtools/testtools/matchers/__init__.py create mode 100644 lib/testtools/testtools/matchers/_basic.py create mode 100644 lib/testtools/testtools/matchers/_datastructures.py create mode 100644 lib/testtools/testtools/matchers/_dict.py create mode 100644 lib/testtools/testtools/matchers/_doctest.py create mode 100644 lib/testtools/testtools/matchers/_exception.py create mode 100644 lib/testtools/testtools/matchers/_filesystem.py create mode 100644 lib/testtools/testtools/matchers/_higherorder.py create mode 100644 lib/testtools/testtools/matchers/_impl.py create mode 100644 lib/testtools/testtools/tags.py create mode 100644 lib/testtools/testtools/tests/matchers/__init__.py create mode 100644 lib/testtools/testtools/tests/matchers/helpers.py create mode 100644 lib/testtools/testtools/tests/matchers/test_basic.py create mode 100644 lib/testtools/testtools/tests/matchers/test_datastructures.py create mode 100644 lib/testtools/testtools/tests/matchers/test_dict.py create mode 100644 lib/testtools/testtools/tests/matchers/test_doctest.py create mode 100644 lib/testtools/testtools/tests/matchers/test_exception.py create mode 100644 lib/testtools/testtools/tests/matchers/test_filesystem.py create mode 100644 lib/testtools/testtools/tests/matchers/test_higherorder.py create mode 100644 lib/testtools/testtools/tests/matchers/test_impl.py delete mode 100644 lib/testtools/testtools/tests/test_matchers.py create mode 100644 lib/testtools/testtools/tests/test_tags.py Changeset truncated at 500 lines: diff --git a/lib/subunit/Makefile.am b/lib/subunit/Makefile.am index 716fa0f..310c042 100644 --- a/lib/subunit/Makefile.am +++ b/lib/subunit/Makefile.am @@ -28,6 +28,7 @@ EXTRA_DIST = \ python/subunit/tests/test_details.py \ python/subunit/tests/test_progress_model.py \ python/subunit/tests/test_subunit_filter.py \ + python/subunit/tests/test_run.py \ python/subunit/tests/test_subunit_stats.py \ python/subunit/tests/test_subunit_tags.py \ python/subunit/tests/test_tap2subunit.py \ @@ -67,6 +68,7 @@ pkgpython_PYTHON = \ python/subunit/__init__.py \ python/subunit/chunked.py \ python/subunit/details.py \ + python/subunit/filters.py \ python/subunit/iso8601.py \ python/subunit/progress_model.py \ python/subunit/run.py \ diff --git a/lib/subunit/NEWS b/lib/subunit/NEWS index 713d272..f28ec5a 100644 --- a/lib/subunit/NEWS +++ b/lib/subunit/NEWS @@ -5,12 +5,30 @@ subunit release notes NEXT (In development) --------------------- +BUG FIXES +~~~~~~~~~ + +* ``python/subunit/tests/test_run.py`` and ``python/subunit/filters.py`` were + not included in the 0.0.8 tarball. (Robert Collins) + +0.0.8 +----- + IMPROVEMENTS ~~~~~~~~~~~~ * Perl module now correctly outputs "failure" instead of "fail". (Stewart Smith) -* Shell functions now output timestamps. (Stewart Smith) +* Shell functions now output timestamps. (Stewart Smith, Robert Collins) + +* 'subunit2csv' script that converts subunit output to CSV format. + (Jonathan Lange) + +* ``TagCollapsingDecorator`` now correctly distinguishes between local and + global tags. (Jonathan Lange) + +* ``TestResultFilter`` always forwards ``time:`` events. + (Benji York, Brad Crittenden) BUG FIXES ~~~~~~~~~ @@ -22,6 +40,23 @@ BUG FIXES '--no-xfail', '--no-passthrough, '--no-success', and gives you just the failure stream. (John Arbash Meinel) +* Python2.6 support was broken by the fixup feature. + (Arfrever Frehtes Taifersar Arahesis, #987490) + +* Python3 support regressed in trunk. + (Arfrever Frehtes Taifersar Arahesis, #987514) + +* Python3 support was insufficiently robust in detecting unicode streams. + (Robert Collins, Arfrever Frehtes Taifersar Arahesis) + +* Tag support has been implemented for TestProtocolClient. + (Robert Collins, #518016) + +* Tags can now be filtered. (Jonathan Lange, #664171) + +* Test suite works with latest testtools (but not older ones - formatting + changes only). (Robert Collins) + 0.0.7 ----- diff --git a/lib/subunit/configure.ac b/lib/subunit/configure.ac index 4375c37..223b3c9 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], [7]) +m4_define([SUBUNIT_MICRO_VERSION], [8]) 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-filter b/lib/subunit/filters/subunit-filter index 7f5620f..6a1ecc9 100755 --- a/lib/subunit/filters/subunit-filter +++ b/lib/subunit/filters/subunit-filter @@ -36,41 +36,59 @@ from subunit import ( TestProtocolClient, read_test_list, ) -from subunit.test_results import TestResultFilter - -parser = OptionParser(description=__doc__) -parser.add_option("--error", action="store_false", - help="include errors", default=False, dest="error") -parser.add_option("-e", "--no-error", action="store_true", - help="exclude errors", dest="error") -parser.add_option("--failure", action="store_false", - help="include failures", default=False, dest="failure") -parser.add_option("-f", "--no-failure", action="store_true", - help="exclude failures", dest="failure") -parser.add_option("--passthrough", action="store_false", - help="Show all non subunit input.", default=False, dest="no_passthrough") -parser.add_option("--no-passthrough", action="store_true", - help="Hide all non subunit input.", default=False, dest="no_passthrough") -parser.add_option("-s", "--success", action="store_false", - help="include successes", dest="success") -parser.add_option("--no-success", action="store_true", - help="exclude successes", default=True, dest="success") -parser.add_option("--no-skip", action="store_true", - help="exclude skips", dest="skip") -parser.add_option("--xfail", action="store_false", - help="include expected falures", default=True, dest="xfail") -parser.add_option("--no-xfail", action="store_true", - help="exclude expected falures", default=True, dest="xfail") -parser.add_option("-m", "--with", type=str, - help="regexp to include (case-sensitive by default)", - action="append", dest="with_regexps") -parser.add_option("--fixup-expected-failures", type=str, - help="File with list of test ids that are expected to fail; on failure " - "their result will be changed to xfail; on success they will be " - "changed to error.", dest="fixup_expected_failures", action="append") -parser.add_option("--without", type=str, - help="regexp to exclude (case-sensitive by default)", - action="append", dest="without_regexps") +from subunit.filters import filter_by_result +from subunit.test_results import ( + and_predicates, + make_tag_filter, + TestResultFilter, + ) + + +def make_options(description): + parser = OptionParser(description=__doc__) + parser.add_option("--error", action="store_false", + help="include errors", default=False, dest="error") + parser.add_option("-e", "--no-error", action="store_true", + help="exclude errors", dest="error") + parser.add_option("--failure", action="store_false", + help="include failures", default=False, dest="failure") + parser.add_option("-f", "--no-failure", action="store_true", + help="exclude failures", dest="failure") + parser.add_option("--passthrough", action="store_false", + help="Show all non subunit input.", default=False, dest="no_passthrough") + parser.add_option("--no-passthrough", action="store_true", + help="Hide all non subunit input.", default=False, dest="no_passthrough") + parser.add_option("-s", "--success", action="store_false", + help="include successes", dest="success") + parser.add_option("--no-success", action="store_true", + help="exclude successes", default=True, dest="success") + parser.add_option("--no-skip", action="store_true", + help="exclude skips", dest="skip") + parser.add_option("--xfail", action="store_false", + help="include expected falures", default=True, dest="xfail") + parser.add_option("--no-xfail", action="store_true", + help="exclude expected falures", default=True, dest="xfail") + parser.add_option( + "--with-tag", type=str, + help="include tests with these tags", action="append", dest="with_tags") + parser.add_option( + "--without-tag", type=str, + help="exclude tests with these tags", action="append", dest="without_tags") + parser.add_option("-m", "--with", type=str, + help="regexp to include (case-sensitive by default)", + action="append", dest="with_regexps") + parser.add_option("--fixup-expected-failures", type=str, + help="File with list of test ids that are expected to fail; on failure " + "their result will be changed to xfail; on success they will be " + "changed to error.", dest="fixup_expected_failures", action="append") + parser.add_option("--without", type=str, + help="regexp to exclude (case-sensitive by default)", + action="append", dest="without_regexps") + parser.add_option("-F", "--only-genuine-failures", action="callback", + callback=only_genuine_failures_callback, + help="Only pass through failures and exceptions.") + return parser + def only_genuine_failures_callback(option, opt, value, parser): parser.rargs.insert(0, '--no-passthrough') @@ -78,11 +96,6 @@ def only_genuine_failures_callback(option, opt, value, parser): parser.rargs.insert(0, '--no-skip') parser.rargs.insert(0, '--no-success') -parser.add_option("-F", "--only-genuine-failures", action="callback", - callback=only_genuine_failures_callback, - help="Only pass through failures and exceptions.") - -(options, args) = parser.parse_args() def _compile_re_from_list(l): return re.compile("|".join(l), re.MULTILINE) @@ -97,7 +110,7 @@ def _make_regexp_filter(with_regexps, without_regexps): with_re = with_regexps and _compile_re_from_list(with_regexps) without_re = without_regexps and _compile_re_from_list(without_regexps) - def check_regexps(test, outcome, err, details): + def check_regexps(test, outcome, err, details, tags): """Check if this test and error match the regexp filters.""" test_str = str(test) + outcome + str(err) + str(details) if with_re and not with_re.search(test_str): @@ -108,21 +121,38 @@ def _make_regexp_filter(with_regexps, without_regexps): return check_regexps -regexp_filter = _make_regexp_filter(options.with_regexps, - options.without_regexps) -fixup_expected_failures = set() -for path in options.fixup_expected_failures or (): - fixup_expected_failures.update(read_test_list(path)) -result = TestProtocolClient(sys.stdout) -result = TestResultFilter(result, filter_error=options.error, - filter_failure=options.failure, filter_success=options.success, - filter_skip=options.skip, filter_xfail=options.xfail, - filter_predicate=regexp_filter, - fixup_expected_failures=fixup_expected_failures) -if options.no_passthrough: - passthrough_stream = DiscardStream() -else: - passthrough_stream = None -test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream) -test.run(result) -sys.exit(0) +def _make_result(output, options, predicate): + """Make the result that we'll send the test outcomes to.""" + fixup_expected_failures = set() + for path in options.fixup_expected_failures or (): + fixup_expected_failures.update(read_test_list(path)) + return TestResultFilter( + TestProtocolClient(output), + filter_error=options.error, + filter_failure=options.failure, + filter_success=options.success, + filter_skip=options.skip, + filter_xfail=options.xfail, + filter_predicate=predicate, + fixup_expected_failures=fixup_expected_failures) + + +def main(): + parser = make_options(__doc__) + (options, args) = parser.parse_args() + + regexp_filter = _make_regexp_filter( + options.with_regexps, options.without_regexps) + tag_filter = make_tag_filter(options.with_tags, options.without_tags) + filter_predicate = and_predicates([regexp_filter, tag_filter]) + + filter_by_result( + lambda output_to: _make_result(sys.stdout, options, filter_predicate), + output_path=None, + passthrough=(not options.no_passthrough), + forward=False) + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/lib/subunit/filters/subunit-notify b/lib/subunit/filters/subunit-notify index 758e7fc..8cce2d1 100755 --- a/lib/subunit/filters/subunit-notify +++ b/lib/subunit/filters/subunit-notify @@ -16,50 +16,29 @@ """Notify the user of a finished test run.""" -from optparse import OptionParser -import sys - import pygtk pygtk.require('2.0') import pynotify -from subunit import DiscardStream, ProtocolTestCase, TestResultStats +from subunit import TestResultStats +from subunit.filters import run_filter_script if not pynotify.init("Subunit-notify"): sys.exit(1) -parser = OptionParser(description=__doc__) -parser.add_option("--no-passthrough", action="store_true", - help="Hide all non subunit input.", default=False, dest="no_passthrough") -parser.add_option("-f", "--forward", action="store_true", default=False, - help="Forward subunit stream on stdout.") -(options, args) = parser.parse_args() -result = TestResultStats(sys.stdout) -if options.no_passthrough: - passthrough_stream = DiscardStream() -else: - passthrough_stream = None -if options.forward: - forward_stream = sys.stdout -else: - forward_stream = None -test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream, - forward=forward_stream) -test.run(result) -if result.failed_tests > 0: - summary = "Test run failed" -else: - summary = "Test run successful" -body = "Total tests: %d; Passed: %d; Failed: %d" % ( - result.total_tests, - result.passed_tests, - result.failed_tests, + +def notify_of_result(result): + if result.failed_tests > 0: + summary = "Test run failed" + else: + summary = "Test run successful" + body = "Total tests: %d; Passed: %d; Failed: %d" % ( + result.total_tests, + result.passed_tests, + result.failed_tests, ) -nw = pynotify.Notification(summary, body) -nw.show() + nw = pynotify.Notification(summary, body) + nw.show() + -if result.wasSuccessful(): - exit_code = 0 -else: - exit_code = 1 -sys.exit(exit_code) +run_filter_script(TestResultStats, __doc__, notify_of_result) diff --git a/lib/subunit/filters/subunit2csv b/lib/subunit/filters/subunit2csv new file mode 100755 index 0000000..14620ff --- /dev/null +++ b/lib/subunit/filters/subunit2csv @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# subunit: extensions to python unittest to get test results from subprocesses. +# Copyright (C) 2009 Robert Collins <robe...@robertcollins.net> +# +# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause +# 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 d on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# license you chose for the specific language governing permissions and +# limitations under that license. +# + +"""Turn a subunit stream into a CSV""" + +from subunit.filters import run_filter_script +from subunit.test_results import CsvResult + + +run_filter_script(CsvResult, __doc__) diff --git a/lib/subunit/filters/subunit2junitxml b/lib/subunit/filters/subunit2junitxml index bea795d..d568c71 100755 --- a/lib/subunit/filters/subunit2junitxml +++ b/lib/subunit/filters/subunit2junitxml @@ -16,11 +16,10 @@ """Filter a subunit stream to get aggregate statistics.""" -from optparse import OptionParser + import sys -import unittest +from subunit.filters import run_filter_script -from subunit import DiscardStream, ProtocolTestCase try: from junitxml import JUnitXmlResult except ImportError: @@ -28,38 +27,5 @@ except ImportError: "http://pypi.python.org/pypi/junitxml) is required for this filter.") raise -parser = OptionParser(description=__doc__) -parser.add_option("--no-passthrough", action="store_true", - help="Hide all non subunit input.", default=False, dest="no_passthrough") -parser.add_option("-o", "--output-to", - help="Output the XML to this path rather than stdout.") -parser.add_option("-f", "--forward", action="store_true", default=False, - help="Forward subunit stream on stdout.") -(options, args) = parser.parse_args() -if options.output_to is None: - output_to = sys.stdout -else: - output_to = file(options.output_to, 'wb') -try: - result = JUnitXmlResult(output_to) - if options.no_passthrough: - passthrough_stream = DiscardStream() - else: - passthrough_stream = None - if options.forward: - forward_stream = sys.stdout - else: - forward_stream = None - test = ProtocolTestCase(sys.stdin, passthrough=passthrough_stream, - forward=forward_stream) - result.startTestRun() - test.run(result) - result.stopTestRun() -finally: - if options.output_to is not None: - output_to.close() -if result.wasSuccessful(): - exit_code = 0 -else: - exit_code = 1 -sys.exit(exit_code) + +run_filter_script(JUnitXmlResult, __doc__) diff --git a/lib/subunit/perl/Makefile.PL.in b/lib/subunit/perl/Makefile.PL.in index 26e1c18..cf5e6c4 100755 --- a/lib/subunit/perl/Makefile.PL.in +++ b/lib/subunit/perl/Makefile.PL.in @@ -13,6 +13,7 @@ check: # test uninstall_distcheck: rm -fr $(DESTINSTALLARCHLIB) + rm MYMETA.yml VPATH = @srcdir@ .PHONY: uninstall_distcheck diff --git a/lib/subunit/python/subunit/__init__.py b/lib/subunit/python/subunit/__init__.py index b4c9397..6015c0e 100644 --- a/lib/subunit/python/subunit/__init__.py +++ b/lib/subunit/python/subunit/__init__.py @@ -121,8 +121,14 @@ import re import subprocess import sys import unittest +if sys.version_info > (3, 0): + from io import UnsupportedOperation as _UnsupportedOperation +else: + _UnsupportedOperation = AttributeError + from testtools import content, content_type, ExtendedToOriginalDecorator +from testtools.content import TracebackContent from testtools.compat import _b, _u, BytesIO, StringIO try: from testtools.testresult.real import _StringException @@ -182,9 +188,15 @@ def tags_to_new_gone(tags): class DiscardStream(object): """A filelike object which discards what is written to it.""" + def fileno(self): + raise _UnsupportedOperation() + def write(self, bytes): pass + def read(self, len=0): + return _b('') + class _ParserState(object): """State for the subunit parser.""" @@ -599,8 +611,8 @@ class TestProtocolClient(testresult.TestResult): def __init__(self, stream): testresult.TestResult.__init__(self) + stream = _make_stream_binary(stream) self._stream = stream - _make_stream_binary(stream) self._progress_fmt = _b("progress: ") self._bytes_eol = _b("\n") self._progress_plus = _b("+") @@ -682,10 +694,9 @@ class TestProtocolClient(testresult.TestResult): raise ValueError if error is not None: self._stream.write(self._start_simple) - # 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')) + tb_content = TracebackContent(error, test) + for bytes in tb_content.iter_bytes(): + self._stream.write(bytes) elif details is not None: self._write_details(details) else: @@ -755,6 +766,15 @@ class TestProtocolClient(testresult.TestResult): -- Samba Shared Repository