* Python for the filters
* 'testtools' (On Debian and Ubuntu systems the 'python-testtools' package,
the testtools package on pypi, or https://launchpad.net/testtools) for
- the extended test API which permits attachments. Version 0.9.11 or newer is
+ the extended test API which permits attachments. Version 0.9.23 or newer is
required. Of particular note, http://testtools.python-hosting.com/ is not
the testtools you want.
* A C compiler for the C bindings
prune perl
exclude py-compile
prune shell
-prune python/iso8601
exclude stamp-h1
include NEWS
python/subunit/tests/test_test_protocol.py \
python/subunit/tests/test_test_results.py \
runtests.py \
+ setup.py \
shell/README \
shell/share/subunit.sh \
shell/subunit-ui.patch \
dist_bin_SCRIPTS = \
filters/subunit-filter \
filters/subunit-ls \
+ filters/subunit-notify \
filters/subunit-stats \
filters/subunit-tags \
+ filters/subunit2csv \
filters/subunit2gtk \
filters/subunit2junitxml \
filters/subunit2pyunit \
NEXT (In development)
---------------------
+0.0.9
+-----
+
BUG FIXES
~~~~~~~~~
+* All the source files are now included in the distribution tarball.
+ (Arfrever Frehtes Taifersar Arahesis, Robert Collins, #996275)
+
* ``python/subunit/tests/test_run.py`` and ``python/subunit/filters.py`` were
not included in the 0.0.8 tarball. (Robert Collins)
+* Test ids which include non-ascii unicode characters are now supported.
+ (Robert Collins, #1029866)
+
+* The ``failfast`` option to ``subunit.run`` will now work. The dependency on
+ testtools has been raised to 0.9.23 to permit this.
+ (Robert Collins, #1090582)
+
0.0.8
-----
Subunit supplies the following filters:
* tap2subunit - convert perl's TestAnythingProtocol to subunit.
+ * subunit2csv - convert a subunit stream to csv.
* subunit2pyunit - convert a subunit stream to pyunit test results.
* subunit2gtk - show a subunit stream in GTK.
* subunit2junitxml - convert a subunit stream to JUnit's XML format.
tests in some frameworks). uxsuccess is used to indicate and unexpected success
where a test though to be failing actually passes. It is complementary to
xfail.
+
+Hacking on subunit
+------------------
+
+Releases
+========
+
+* Update versions in configure.ac and python/subunit/__init__.py.
+* Make PyPI and regular tarball releases. Upload the regular one to LP, the
+ PyPI one to PyPI.
+* Push a tagged commit.
+
m4_define([SUBUNIT_MAJOR_VERSION], [0])
m4_define([SUBUNIT_MINOR_VERSION], [0])
-m4_define([SUBUNIT_MICRO_VERSION], [8])
+m4_define([SUBUNIT_MICRO_VERSION], [9])
m4_define([SUBUNIT_VERSION],
m4_defn([SUBUNIT_MAJOR_VERSION]).m4_defn([SUBUNIT_MINOR_VERSION]).m4_defn([SUBUNIT_MICRO_VERSION]))
AC_PREREQ([2.59])
from subunit import chunked, details, iso8601, test_results
+# same format as sys.version_info: "A tuple containing the five components of
+# the version number: major, minor, micro, releaselevel, and serial. All
+# values except releaselevel are integers; the release level is 'alpha',
+# 'beta', 'candidate', or 'final'. The version_info value corresponding to the
+# Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a
+# releaselevel of 'dev' for unreleased under-development code.
+#
+# If the releaselevel is 'alpha' then the major/minor/micro components are not
+# established at this point, and setup.py will use a version of next-$(revno).
+# If the releaselevel is 'final', then the tarball will be major.minor.micro.
+# Otherwise it is major.minor.micro~$(revno).
+
+__version__ = (0, 0, 9, 'final', 0)
PROGRESS_SET = 0
PROGRESS_CUR = 1
to subunit.Content objects.
"""
self._addOutcome("error", test, error=error, details=details)
+ if self.failfast:
+ self.stop()
def addExpectedFailure(self, test, error=None, details=None):
"""Report an expected failure in test test.
to subunit.Content objects.
"""
self._addOutcome("failure", test, error=error, details=details)
+ if self.failfast:
+ self.stop()
def _addOutcome(self, outcome, test, error=None, details=None,
error_permitted=True):
:param error_permitted: If True then one and only one of error or
details must be supplied. If False then error must not be supplied
and details is still optional. """
- self._stream.write(_b("%s: %s" % (outcome, test.id())))
+ self._stream.write(_b("%s: " % outcome) + self._test_id(test))
if error_permitted:
if error is None and details is None:
raise ValueError
"""
self._addOutcome("uxsuccess", test, details=details,
error_permitted=False)
+ if self.failfast:
+ self.stop()
+
+ def _test_id(self, test):
+ result = test.id()
+ if type(result) is not bytes:
+ result = result.encode('utf8')
+ return result
def startTest(self, test):
"""Mark a test as starting its test run."""
super(TestProtocolClient, self).startTest(test)
- self._stream.write(_b("test: %s\n" % test.id()))
+ self._stream.write(_b("test: ") + self._test_id(test) + _b("\n"))
self._stream.flush()
def stopTest(self, test):
class SubunitTestRunner(object):
- def __init__(self, stream=sys.stdout):
- self.stream = stream
+ def __init__(self, verbosity=None, failfast=None, buffer=None, stream=None):
+ """Create a TestToolsTestRunner.
+
+ :param verbosity: Ignored.
+ :param failfast: Stop running tests at the first failure.
+ :param buffer: Ignored.
+ """
+ self.failfast = failfast
+ self.stream = stream or sys.stdout
def run(self, test):
"Run the given test case or test suite."
result = TestProtocolClient(self.stream)
result = AutoTimingTestResultDecorator(result)
+ if self.failfast is not None:
+ result.failfast = self.failfast
test(result)
return result
if __name__ == '__main__':
stream = get_default_formatter()
- runner = SubunitTestRunner(stream)
+ runner = SubunitTestRunner
SubunitTestProgram(module=None, argv=sys.argv, testRunner=runner,
stdout=sys.stdout)
def addUnexpectedSuccess(self, test, details=None):
return self.decorated.addUnexpectedSuccess(test, details=details)
+ def _get_failfast(self):
+ return getattr(self.decorated, 'failfast', False)
+
+ def _set_failfast(self, value):
+ self.decorated.failfast = value
+ failfast = property(_get_failfast, _set_failfast)
+
def progress(self, offset, whence):
return self.decorated.progress(offset, whence)
import unittest
import os
-from testtools import skipIf, TestCase, TestResult
+from testtools import PlaceHolder, skipIf, TestCase, TestResult
from testtools.compat import _b, _u, BytesIO
from testtools.content import Content, TracebackContent, text_content
from testtools.content_type import ContentType
def setUp(self):
self.io = BytesIO()
self.protocol = subunit.TestProtocolClient(self.io)
+ self.unicode_test = PlaceHolder(_u('\u2603'))
self.test = TestTestProtocolClient("test_start_test")
self.sample_details = {'something':Content(
ContentType('text', 'plain'), lambda:[_b('serialised\nform')])}
self.protocol.startTest(self.test)
self.assertEqual(self.io.getvalue(), _b("test: %s\n" % self.test.id()))
+ def test_start_test_unicode_id(self):
+ """Test startTest on a TestProtocolClient."""
+ self.protocol.startTest(self.unicode_test)
+ expected = _b("test: ") + _u('\u2603').encode('utf8') + _b("\n")
+ self.assertEqual(expected, self.io.getvalue())
+
def test_stop_test(self):
# stopTest doesn't output anything.
self.protocol.stopTest(self.test)
self.assertEqual(
self.io.getvalue(), _b("successful: %s\n" % self.test.id()))
+ def test_add_outcome_unicode_id(self):
+ """Test addSuccess on a TestProtocolClient."""
+ self.protocol.addSuccess(self.unicode_test)
+ expected = _b("successful: ") + _u('\u2603').encode('utf8') + _b("\n")
+ self.assertEqual(expected, self.io.getvalue())
+
def test_add_success_details(self):
"""Test addSuccess on a TestProtocolClient with details."""
self.protocol.addSuccess(self.test, details=self.sample_details)
def __init__(self):
super(TimeCapturingResult, self).__init__()
self._calls = []
+ self.failfast = False
def time(self, a_datetime):
self._calls.append(a_datetime)
self.assertEqual(3, len(self.decorated._calls))
self.assertNotEqual(None, self.decorated._calls[2])
+ def test_set_failfast_True(self):
+ self.assertFalse(self.decorated.failfast)
+ self.result.failfast = True
+ self.assertTrue(self.decorated.failfast)
+
class TestTagCollapsingDecorator(TestCase):
-#!/usr/bin/env python3
+#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 2004 Canonical.com
else:
extra = {
'install_requires': [
- 'testtools>=0.9.11',
+ 'testtools>=0.9.23',
]
}
long_description=open('README').read(),
classifiers=[
'Intended Audience :: Developers',
+ 'Programming Language :: Python :: 3',
'Programming Language :: Python',
'Topic :: Software Development :: Testing',
],
author='Robert Collins',
author_email='subunit-dev@lists.launchpad.net',
url='http://launchpad.net/subunit',
- packages=['subunit'],
+ packages=['subunit', 'subunit.tests'],
package_dir={'subunit': 'python/subunit'},
scripts = [
'filters/subunit2gtk',