summaryrefslogtreecommitdiff
path: root/lib/subunit/python
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2010-09-04 23:04:28 +0200
committerJelmer Vernooij <jelmer@samba.org>2010-09-04 23:04:28 +0200
commit5de2ec0def3e4ad0ead20b426e81509fd8e48c6d (patch)
treecfb8ef98f11663482c39929e46c8374933b925d5 /lib/subunit/python
parented4253504167748c0bb829176d41c09365937189 (diff)
downloadsamba-5de2ec0def3e4ad0ead20b426e81509fd8e48c6d.tar.gz
samba-5de2ec0def3e4ad0ead20b426e81509fd8e48c6d.tar.bz2
samba-5de2ec0def3e4ad0ead20b426e81509fd8e48c6d.zip
subunit: Import latest upstream.
Diffstat (limited to 'lib/subunit/python')
-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
6 files changed, 163 insertions, 66 deletions
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' +