Hello community,

here is the log from the commit of package python-python-subunit for 
openSUSE:Factory checked in at 2014-01-30 17:45:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-subunit (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-subunit.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-subunit"

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-python-subunit/python-python-subunit.changes  
    2014-01-23 15:54:39.000000000 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-python-subunit.new/python-python-subunit.changes
 2014-01-30 17:45:11.000000000 +0100
@@ -1,0 +2,15 @@
+Thu Jan 30 11:53:52 UTC 2014 - [email protected]
+
+- Update to version 0.0.18:
+  + Fix compatibility with testtools 0.9.35 which dropped the 'all' compat
+    symbol. This breaks support for Python versions lower than 2.6.
+- Changes from version 0.0.17:
+  + Add subunit-output tool that can generate a Subunit v2 bytestream from
+    arguments passed on the command line. (Thomi Richards, #1252084)
+- Add /usr/bin/subunit-output
+- Add python-subunit-filter-path.patch:
+  + Fix filters path in testsuite
+- Properly run testsuite
+- Fix requirements
+
+-------------------------------------------------------------------
@@ -12 +27 @@
- * When tests fail to import ``python subunit.run -l ...`` will now write a 
+ * When tests fail to import python subunit.run -l ... will now write a 
@@ -33,3 +48,3 @@
-  * `subunit-stats` no longer outputs encapsulated stdout as subunit.
-  * The logic for `subunit.run` is now importable via python -
-  `subunit.run.main`. (Robert Collins, #606770)
+  * subunit-stats no longer outputs encapsulated stdout as subunit.
+  * The logic for subunit.run is now importable via python -
+    subunit.run.main. (Robert Collins, #606770)

Old:
----
  python-subunit-0.0.16.tar.gz

New:
----
  python-subunit-0.0.18.tar.gz
  python-subunit-filter-path.patch

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-subunit.spec ++++++
--- /var/tmp/diff_new_pack.LlFNoN/_old  2014-01-30 17:45:12.000000000 +0100
+++ /var/tmp/diff_new_pack.LlFNoN/_new  2014-01-30 17:45:12.000000000 +0100
@@ -17,16 +17,21 @@
 
 
 Name:           python-python-subunit
-Version:        0.0.16
+Version:        0.0.18
 Release:        0
 Summary:        Python implementation of subunit test streaming protocol
 License:        Apache-2.0 or BSD-3-Clause
 Group:          Development/Languages/Python
 Url:            http://launchpad.net/subunit
 Source:         
http://pypi.python.org/packages/source/p/python-subunit/python-subunit-%{version}.tar.gz
+# PATCH-FIX-OPENSUSE [email protected] -- Filters are installed in /usr/bin.
+Patch0:         python-subunit-filter-path.patch
 BuildRequires:  python-devel
-BuildRequires:  python-nose
+# Test requriements
+BuildRequires:  python-testscenarios
+BuildRequires:  python-testtools >= 0.9.34
 Requires:       python-extras
+Requires:       python-testscenarios
 Requires:       python-testtools >= 0.9.34
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 %if 0%{?suse_version} && 0%{?suse_version} <= 1110
@@ -46,15 +51,23 @@
 
 %prep
 %setup -q -n python-subunit-%{version}
+%patch0 -p1
 
 %build
 python setup.py build
 
 %install
 python setup.py install --prefix=%{_prefix} --root=%{buildroot}
+#NOTE(saschpe): The testsuite demands executables:
+chmod +x %{buildroot}%{python_sitelib}/subunit/tests/sample-*.py
 
 %check
-nosetests
+#NOTE(saschpe): Pesky testsuite doesn't know about buildroot
+sed -i "s|'/',|'%{buildroot}',|" 
%{buildroot}%{python_sitelib}/subunit/tests/test_subunit_filter.py
+PYTHONPATH=%{buildroot}%{python_sitelib} python -m testtools.run 
subunit.test_suite
+sed -i "s|'%{buildroot}',|'/',|" 
%{buildroot}%{python_sitelib}/subunit/tests/test_subunit_filter.py
+# Fixup compiled python module afterwards:
+%py_compile %{buildroot}%{python_sitelib}/subunit/tests
 
 %files
 %defattr(-,root,root,-)
@@ -66,10 +79,12 @@
 %{_bindir}/subunit-notify
 %{_bindir}/subunit-stats
 %{_bindir}/subunit-tags
+%{_bindir}/subunit-output
 %{_bindir}/subunit2gtk
 %{_bindir}/subunit2junitxml
 %{_bindir}/subunit2pyunit
 %{_bindir}/tap2subunit
-%{python_sitelib}/*
+%{python_sitelib}/subunit
+%{python_sitelib}/python_subunit-%{version}-py%{py_ver}.egg-info
 
 %changelog

++++++ python-subunit-0.0.16.tar.gz -> python-subunit-0.0.18.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/NEWS 
new/python-subunit-0.0.18/NEWS
--- old/python-subunit-0.0.16/NEWS      2013-11-30 03:04:01.000000000 +0100
+++ new/python-subunit-0.0.18/NEWS      2014-01-29 20:13:19.000000000 +0100
@@ -5,6 +5,25 @@
 NEXT (In development)
 ---------------------
 
+0.0.18
+------
+
+IMPROVMENTS
+~~~~~~~~~~~
+
+* Fix compatibility with testtools 0.9.35 which dropped the 'all' compat
+  symbol. This breaks support for Python versions lower than 2.6.
+  (Robert Collins, #1274056)
+
+0.0.17
+------
+
+IMPROVMENTS
+~~~~~~~~~~~
+
+* Add ``subunit-output`` tool that can generate a Subunit v2 bytestream from
+  arguments passed on the command line. (Thomi Richards, #1252084)
+
 0.0.16
 ------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/PKG-INFO 
new/python-subunit-0.0.18/PKG-INFO
--- old/python-subunit-0.0.16/PKG-INFO  2013-11-30 10:17:05.000000000 +0100
+++ new/python-subunit-0.0.18/PKG-INFO  2014-01-29 20:16:59.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: python-subunit
-Version: 0.0.16
+Version: 0.0.18
 Summary: Python implementation of subunit test streaming protocol
 Home-page: http://launchpad.net/subunit
 Author: Robert Collins
@@ -470,6 +470,8 @@
         ========
         
         * Update versions in configure.ac and python/subunit/__init__.py.
+        * Update Makefile in the root or do an inplace configure to get an 
updated Makefile.
+        * Update NEWS.
         * Make PyPI and regular tarball releases. Upload the regular one to 
LP, the
           PyPI one to PyPI.
         * Push a tagged commit.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/README 
new/python-subunit-0.0.18/README
--- old/python-subunit-0.0.16/README    2013-11-25 02:50:04.000000000 +0100
+++ new/python-subunit-0.0.18/README    2014-01-29 10:32:50.000000000 +0100
@@ -462,6 +462,8 @@
 ========
 
 * Update versions in configure.ac and python/subunit/__init__.py.
+* Update Makefile in the root or do an inplace configure to get an updated 
Makefile.
+* Update NEWS.
 * Make PyPI and regular tarball releases. Upload the regular one to LP, the
   PyPI one to PyPI.
 * Push a tagged commit.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/filters/subunit-output 
new/python-subunit-0.0.18/filters/subunit-output
--- old/python-subunit-0.0.16/filters/subunit-output    1970-01-01 
01:00:00.000000000 +0100
+++ new/python-subunit-0.0.18/filters/subunit-output    2014-01-13 
01:07:19.000000000 +0100
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+#  subunit: extensions to python unittest to get test results from 
subprocesses.
+#  Copyright (C) 2013 Subunit Contributors
+#
+#  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 distributed 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.
+
+
+"""A command-line tool to generate a subunit result byte-stream."""
+
+from subunit._output import output_main
+
+
+if __name__ == '__main__':
+    exit(output_main())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/python/subunit/__init__.py 
new/python-subunit-0.0.18/python/subunit/__init__.py
--- old/python-subunit-0.0.16/python/subunit/__init__.py        2013-11-30 
03:02:19.000000000 +0100
+++ new/python-subunit-0.0.18/python/subunit/__init__.py        2014-01-29 
20:14:12.000000000 +0100
@@ -153,7 +153,7 @@
 # If the releaselevel is 'final', then the tarball will be major.minor.micro.
 # Otherwise it is major.minor.micro~$(revno).
 
-__version__ = (0, 0, 16, 'final', 0)
+__version__ = (0, 0, 18, 'final', 0)
 
 PROGRESS_SET = 0
 PROGRESS_CUR = 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/python/subunit/_output.py 
new/python-subunit-0.0.18/python/subunit/_output.py
--- old/python-subunit-0.0.16/python/subunit/_output.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/python-subunit-0.0.18/python/subunit/_output.py 2014-01-13 
01:07:19.000000000 +0100
@@ -0,0 +1,203 @@
+#  subunit: extensions to python unittest to get test results from 
subprocesses.
+#  Copyright (C) 2013 Subunit Contributors
+#
+#  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 distributed 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.
+#
+
+import datetime
+from functools import partial
+from optparse import (
+    OptionGroup,
+    OptionParser,
+    OptionValueError,
+)
+import sys
+
+from subunit import make_stream_binary
+from subunit.iso8601 import UTC
+from subunit.v2 import StreamResultToBytes
+
+
+_FINAL_ACTIONS = frozenset([
+    'exists',
+    'fail',
+    'skip',
+    'success',
+    'uxsuccess',
+    'xfail',
+])
+_ALL_ACTIONS = _FINAL_ACTIONS.union(['inprogress'])
+_CHUNK_SIZE=3670016 # 3.5 MiB
+
+
+def output_main():
+    args = parse_arguments()
+    output = StreamResultToBytes(sys.stdout)
+    generate_stream_results(args, output)
+    return 0
+
+
+def parse_arguments(args=None, ParserClass=OptionParser):
+    """Parse arguments from the command line.
+
+    If specified, args must be a list of strings, similar to sys.argv[1:].
+
+    ParserClass may be specified to override the class we use to parse the
+    command-line arguments. This is useful for testing.
+    """
+    parser = ParserClass(
+        prog="subunit-output",
+        description="A tool to generate a subunit v2 result byte-stream",
+        usage="subunit-output [-h] [status TEST_ID] [options]",
+    )
+    parser.set_default('tags', None)
+    parser.set_default('test_id', None)
+
+    status_commands = OptionGroup(
+        parser,
+        "Status Commands",
+        "These options report the status of a test. TEST_ID must be a string "
+            "that uniquely identifies the test."
+    )
+    for action_name in _ALL_ACTIONS:
+        status_commands.add_option(
+            "--%s" % action_name,
+            nargs=1,
+            action="callback",
+            callback=set_status_cb,
+            callback_args=(action_name,),
+            dest="action",
+            metavar="TEST_ID",
+            help="Report a test status."
+        )
+    parser.add_option_group(status_commands)
+
+    file_commands = OptionGroup(
+        parser,
+        "File Options",
+        "These options control attaching data to a result stream. They can "
+            "either be specified with a status command, in which case the file 
"
+            "is attached to the test status, or by themselves, in which case "
+            "the file is attached to the stream (and not associated with any "
+            "test id)."
+    )
+    file_commands.add_option(
+        "--attach-file",
+        help="Attach a file to the result stream for this test. If '-' is "
+            "specified, stdin will be read instead. In this case, the file "
+            "name will be set to 'stdin' (but can still be overridden with "
+            "the --file-name option)."
+    )
+    file_commands.add_option(
+        "--file-name",
+        help="The name to give this file attachment. If not specified, the "
+            "name of the file on disk will be used, or 'stdin' in the case "
+            "where '-' was passed to the '--attach-file' argument. This option"
+            " may only be specified when '--attach-file' is specified.",
+        )
+    file_commands.add_option(
+        "--mimetype",
+        help="The mime type to send with this file. This is only used if the "
+            "--attach-file argument is used. This argument is optional. If it "
+            "is not specified, the file will be sent without a mime type. This 
"
+            "option may only be specified when '--attach-file' is specified.",
+        default=None
+    )
+    parser.add_option_group(file_commands)
+
+    parser.add_option(
+        "--tag",
+        help="Specifies a tag. May be used multiple times",
+        action="append",
+        dest="tags",
+        default=[]
+    )
+
+    (options, args) = parser.parse_args(args)
+    if options.mimetype and not options.attach_file:
+        parser.error("Cannot specify --mimetype without --attach-file")
+    if options.file_name and not options.attach_file:
+        parser.error("Cannot specify --file-name without --attach-file")
+    if options.attach_file:
+        if options.attach_file == '-':
+            if not options.file_name:
+                options.file_name = 'stdin'
+                options.attach_file = make_stream_binary(sys.stdin)
+        else:
+            try:
+                options.attach_file = open(options.attach_file, 'rb')
+            except IOError as e:
+                parser.error("Cannot open %s (%s)" % (options.attach_file, 
e.strerror))
+
+    return options
+
+
+def set_status_cb(option, opt_str, value, parser, status_name):
+    if getattr(parser.values, "action", None) is not None:
+        raise OptionValueError("argument %s: Only one status may be specified 
at once." % opt_str)
+
+    if len(parser.rargs) == 0:
+        raise OptionValueError("argument %s: must specify a single TEST_ID." % 
opt_str)
+    parser.values.action = status_name
+    parser.values.test_id = parser.rargs.pop(0)
+
+
+def generate_stream_results(args, output_writer):
+    output_writer.startTestRun()
+
+    if args.attach_file:
+        reader = partial(args.attach_file.read, _CHUNK_SIZE)
+        this_file_hunk = reader()
+        next_file_hunk = reader()
+
+    is_first_packet = True
+    is_last_packet = False
+    while not is_last_packet:
+        write_status = output_writer.status
+
+        if is_first_packet:
+            if args.attach_file:
+                if args.mimetype:
+                    write_status = partial(write_status, 
mime_type=args.mimetype)
+            if args.tags:
+                write_status = partial(write_status, test_tags=set(args.tags))
+            write_status = partial(write_status, timestamp=create_timestamp())
+            if args.action not in _FINAL_ACTIONS:
+                write_status = partial(write_status, test_status=args.action)
+            is_first_packet = False
+
+        if args.attach_file:
+            filename = args.file_name or args.attach_file.name
+            write_status = partial(write_status, file_name=filename, 
file_bytes=this_file_hunk)
+            if next_file_hunk == b'':
+                write_status = partial(write_status, eof=True)
+                is_last_packet = True
+            else:
+                this_file_hunk = next_file_hunk
+                next_file_hunk = reader()
+        else:
+            is_last_packet = True
+
+        if args.test_id:
+            write_status = partial(write_status, test_id=args.test_id)
+
+        if is_last_packet:
+            if args.action in _FINAL_ACTIONS:
+                write_status = partial(write_status, test_status=args.action)
+
+        write_status()
+
+    output_writer.stopTestRun()
+
+
+def create_timestamp():
+    return datetime.datetime.now(UTC)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/python/subunit/test_results.py 
new/python-subunit-0.0.18/python/subunit/test_results.py
--- old/python-subunit-0.0.16/python/subunit/test_results.py    2013-04-08 
12:28:36.000000000 +0200
+++ new/python-subunit-0.0.18/python/subunit/test_results.py    2014-01-29 
20:10:33.000000000 +0100
@@ -20,7 +20,6 @@
 import datetime
 
 import testtools
-from testtools.compat import all
 from testtools.content import (
     text_content,
     TracebackContent,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-subunit-0.0.16/python/subunit/tests/__init__.py 
new/python-subunit-0.0.18/python/subunit/tests/__init__.py
--- old/python-subunit-0.0.16/python/subunit/tests/__init__.py  2013-11-25 
03:34:32.000000000 +0100
+++ new/python-subunit-0.0.18/python/subunit/tests/__init__.py  2014-01-13 
01:07:19.000000000 +0100
@@ -17,6 +17,8 @@
 import sys
 from unittest import TestLoader
 
+from testscenarios import generate_scenarios
+
 
 # Before the test module imports to avoid circularity.
 # For testing: different pythons have different str() implementations.
@@ -34,6 +36,7 @@
     test_chunked,
     test_details,
     test_filters,
+    test_output_filter,
     test_progress_model,
     test_run,
     test_subunit_filter,
@@ -60,4 +63,7 @@
     result.addTest(loader.loadTestsFromModule(test_subunit_tags))
     result.addTest(loader.loadTestsFromModule(test_subunit_stats))
     result.addTest(loader.loadTestsFromModule(test_run))
+    result.addTests(
+        generate_scenarios(loader.loadTestsFromModule(test_output_filter))
+    )
     return result
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-subunit-0.0.16/python/subunit/tests/test_output_filter.py 
new/python-subunit-0.0.18/python/subunit/tests/test_output_filter.py
--- old/python-subunit-0.0.16/python/subunit/tests/test_output_filter.py        
1970-01-01 01:00:00.000000000 +0100
+++ new/python-subunit-0.0.18/python/subunit/tests/test_output_filter.py        
2014-01-13 01:07:19.000000000 +0100
@@ -0,0 +1,596 @@
+#
+#  subunit: extensions to python unittest to get test results from 
subprocesses.
+#  Copyright (C) 2013 Subunit Contributors
+#
+#  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 distributed 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.
+#
+
+import datetime
+from functools import partial
+from io import BytesIO, StringIO, TextIOWrapper
+import optparse
+import sys
+from tempfile import NamedTemporaryFile
+
+from contextlib import contextmanager
+from testtools import TestCase
+from testtools.compat import _u
+from testtools.matchers import (
+    Equals,
+    Matcher,
+    MatchesAny,
+    MatchesListwise,
+    Mismatch,
+    raises,
+)
+from testtools.testresult.doubles import StreamResult
+
+from subunit.iso8601 import UTC
+from subunit.v2 import StreamResultToBytes, ByteStreamToStreamResult
+from subunit._output import (
+    _ALL_ACTIONS,
+    _FINAL_ACTIONS,
+    generate_stream_results,
+    parse_arguments,
+)
+import subunit._output as _o
+
+
+class SafeOptionParser(optparse.OptionParser):
+    """An ArgumentParser class that doesn't call sys.exit."""
+
+    def exit(self, status=0, message=""):
+        raise RuntimeError(message)
+
+    def error(self, message):
+        raise RuntimeError(message)
+
+
+safe_parse_arguments = partial(parse_arguments, ParserClass=SafeOptionParser)
+
+
+class TestStatusArgParserTests(TestCase):
+
+    scenarios = [
+        (cmd, dict(command=cmd, option='--' + cmd)) for cmd in _ALL_ACTIONS
+    ]
+
+    def test_can_parse_all_commands_with_test_id(self):
+        test_id = self.getUniqueString()
+        args = safe_parse_arguments(args=[self.option, test_id])
+
+        self.assertThat(args.action, Equals(self.command))
+        self.assertThat(args.test_id, Equals(test_id))
+
+    def test_all_commands_parse_file_attachment(self):
+        with NamedTemporaryFile() as tmp_file:
+            args = safe_parse_arguments(
+                args=[self.option, 'foo', '--attach-file', tmp_file.name]
+            )
+            self.assertThat(args.attach_file.name, Equals(tmp_file.name))
+
+    def test_all_commands_accept_mimetype_argument(self):
+        with NamedTemporaryFile() as tmp_file:
+            args = safe_parse_arguments(
+                args=[self.option, 'foo', '--attach-file', tmp_file.name, 
'--mimetype', "text/plain"]
+            )
+            self.assertThat(args.mimetype, Equals("text/plain"))
+
+    def test_all_commands_accept_file_name_argument(self):
+        with NamedTemporaryFile() as tmp_file:
+            args = safe_parse_arguments(
+                args=[self.option, 'foo', '--attach-file', tmp_file.name, 
'--file-name', "foo"]
+            )
+            self.assertThat(args.file_name, Equals("foo"))
+
+    def test_all_commands_accept_tags_argument(self):
+        args = safe_parse_arguments(
+            args=[self.option, 'foo', '--tag', "foo", "--tag", "bar", "--tag", 
"baz"]
+        )
+        self.assertThat(args.tags, Equals(["foo", "bar", "baz"]))
+
+    def test_attach_file_with_hyphen_opens_stdin(self):
+        self.patch(_o.sys, 'stdin', TextIOWrapper(BytesIO(b"Hello")))
+        args = safe_parse_arguments(
+            args=[self.option, "foo", "--attach-file", "-"]
+        )
+
+        self.assertThat(args.attach_file.read(), Equals(b"Hello"))
+
+    def test_attach_file_with_hyphen_sets_filename_to_stdin(self):
+        args = safe_parse_arguments(
+            args=[self.option, "foo", "--attach-file", "-"]
+        )
+
+        self.assertThat(args.file_name, Equals("stdin"))
+
+    def test_can_override_stdin_filename(self):
+        args = safe_parse_arguments(
+            args=[self.option, "foo", "--attach-file", "-", '--file-name', 
'foo']
+        )
+
+        self.assertThat(args.file_name, Equals("foo"))
+
+    def test_requires_test_id(self):
+        fn = lambda: safe_parse_arguments(args=[self.option])
+        self.assertThat(
+            fn,
+            raises(RuntimeError('argument %s: must specify a single TEST_ID.' 
% self.option))
+        )
+
+
+class ArgParserTests(TestCase):
+
+    def test_can_parse_attach_file_without_test_id(self):
+        with NamedTemporaryFile() as tmp_file:
+            args = safe_parse_arguments(
+                args=["--attach-file", tmp_file.name]
+            )
+            self.assertThat(args.attach_file.name, Equals(tmp_file.name))
+
+    def test_can_run_without_args(self):
+        args = safe_parse_arguments([])
+
+    def test_cannot_specify_more_than_one_status_command(self):
+        fn = lambda: safe_parse_arguments(['--fail', 'foo', '--skip', 'bar'])
+        self.assertThat(
+            fn,
+            raises(RuntimeError('argument --skip: Only one status may be 
specified at once.'))
+        )
+
+    def test_cannot_specify_mimetype_without_attach_file(self):
+        fn = lambda: safe_parse_arguments(['--mimetype', 'foo'])
+        self.assertThat(
+            fn,
+            raises(RuntimeError('Cannot specify --mimetype without 
--attach-file'))
+        )
+
+    def test_cannot_specify_filename_without_attach_file(self):
+        fn = lambda: safe_parse_arguments(['--file-name', 'foo'])
+        self.assertThat(
+            fn,
+            raises(RuntimeError('Cannot specify --file-name without 
--attach-file'))
+        )
+
+    def test_can_specify_tags_without_status_command(self):
+        args = safe_parse_arguments(['--tag', 'foo'])
+        self.assertEqual(['foo'], args.tags)
+
+    def test_must_specify_tags_with_tags_options(self):
+        fn = lambda: safe_parse_arguments(['--fail', 'foo', '--tag'])
+        self.assertThat(
+            fn,
+            MatchesAny(
+                raises(RuntimeError('--tag option requires 1 argument')),
+                raises(RuntimeError('--tag option requires an argument')),
+            )
+        )
+
+def get_result_for(commands):
+    """Get a result object from *commands.
+
+    Runs the 'generate_stream_results' function from subunit._output after
+    parsing *commands as if they were specified on the command line. The
+    resulting bytestream is then converted back into a result object and
+    returned.
+    """
+    result = StreamResult()
+    args = safe_parse_arguments(commands)
+    generate_stream_results(args, result)
+    return result
+
+
+@contextmanager
+def temp_file_contents(data):
+    """Create a temporary file on disk containing 'data'."""
+    with NamedTemporaryFile() as f:
+        f.write(data)
+        f.seek(0)
+        yield f
+
+
+class StatusStreamResultTests(TestCase):
+
+    scenarios = [
+        (s, dict(status=s, option='--' + s)) for s in _ALL_ACTIONS
+    ]
+
+    _dummy_timestamp = datetime.datetime(2013, 1, 1, 0, 0, 0, 0, UTC)
+
+    def setUp(self):
+        super(StatusStreamResultTests, self).setUp()
+        self.patch(_o, 'create_timestamp', lambda: self._dummy_timestamp)
+        self.test_id = self.getUniqueString()
+
+    def test_only_one_packet_is_generated(self):
+        result = get_result_for([self.option, self.test_id])
+        self.assertThat(
+            len(result._events),
+            Equals(3) # startTestRun and stopTestRun are also called, making 3 
total.
+        )
+
+    def test_correct_status_is_generated(self):
+        result = get_result_for([self.option, self.test_id])
+
+        self.assertThat(
+            result._events[1],
+            MatchesStatusCall(test_status=self.status)
+        )
+
+    def test_all_commands_generate_tags(self):
+        result = get_result_for([self.option, self.test_id, '--tag', 'hello', 
'--tag', 'world'])
+        self.assertThat(
+            result._events[1],
+            MatchesStatusCall(test_tags=set(['hello', 'world']))
+        )
+
+    def test_all_commands_generate_timestamp(self):
+        result = get_result_for([self.option, self.test_id])
+
+        self.assertThat(
+            result._events[1],
+            MatchesStatusCall(timestamp=self._dummy_timestamp)
+        )
+
+    def test_all_commands_generate_correct_test_id(self):
+        result = get_result_for([self.option, self.test_id])
+
+        self.assertThat(
+            result._events[1],
+            MatchesStatusCall(test_id=self.test_id)
+        )
+
+    def test_file_is_sent_in_single_packet(self):
+        with temp_file_contents(b"Hello") as f:
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_bytes=b'Hello', eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_can_read_binary_files(self):
+        with temp_file_contents(b"\xDE\xAD\xBE\xEF") as f:
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_bytes=b"\xDE\xAD\xBE\xEF", 
eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_can_read_empty_files(self):
+        with temp_file_contents(b"") as f:
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_bytes=b"", file_name=f.name, 
eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_can_read_stdin(self):
+        self.patch(_o.sys, 'stdin', 
TextIOWrapper(BytesIO(b"\xFE\xED\xFA\xCE")))
+        result = get_result_for([self.option, self.test_id, '--attach-file', 
'-'])
+
+        self.assertThat(
+            result._events,
+            MatchesListwise([
+                MatchesStatusCall(call='startTestRun'),
+                MatchesStatusCall(file_bytes=b"\xFE\xED\xFA\xCE", 
file_name='stdin', eof=True),
+                MatchesStatusCall(call='stopTestRun'),
+            ])
+        )
+
+    def test_file_is_sent_with_test_id(self):
+        with temp_file_contents(b"Hello") as f:
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_id=self.test_id, 
file_bytes=b'Hello', eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_file_is_sent_with_test_status(self):
+        with temp_file_contents(b"Hello") as f:
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_status=self.status, 
file_bytes=b'Hello', eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_file_chunk_size_is_honored(self):
+        with temp_file_contents(b"Hello") as f:
+            self.patch(_o, '_CHUNK_SIZE', 1)
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_id=self.test_id, file_bytes=b'H', 
eof=False),
+                    MatchesStatusCall(test_id=self.test_id, file_bytes=b'e', 
eof=False),
+                    MatchesStatusCall(test_id=self.test_id, file_bytes=b'l', 
eof=False),
+                    MatchesStatusCall(test_id=self.test_id, file_bytes=b'l', 
eof=False),
+                    MatchesStatusCall(test_id=self.test_id, file_bytes=b'o', 
eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_file_mimetype_specified_once_only(self):
+        with temp_file_contents(b"Hi") as f:
+            self.patch(_o, '_CHUNK_SIZE', 1)
+            result = get_result_for([
+                self.option,
+                self.test_id,
+                '--attach-file',
+                f.name,
+                '--mimetype',
+                'text/plain',
+            ])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_id=self.test_id, 
mime_type='text/plain', file_bytes=b'H', eof=False),
+                    MatchesStatusCall(test_id=self.test_id, mime_type=None, 
file_bytes=b'i', eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_tags_specified_once_only(self):
+        with temp_file_contents(b"Hi") as f:
+            self.patch(_o, '_CHUNK_SIZE', 1)
+            result = get_result_for([
+                self.option,
+                self.test_id,
+                '--attach-file',
+                f.name,
+                '--tag',
+                'foo',
+                '--tag',
+                'bar',
+            ])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_id=self.test_id, 
test_tags=set(['foo', 'bar'])),
+                    MatchesStatusCall(test_id=self.test_id, test_tags=None),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_timestamp_specified_once_only(self):
+        with temp_file_contents(b"Hi") as f:
+            self.patch(_o, '_CHUNK_SIZE', 1)
+            result = get_result_for([
+                self.option,
+                self.test_id,
+                '--attach-file',
+                f.name,
+            ])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_id=self.test_id, 
timestamp=self._dummy_timestamp),
+                    MatchesStatusCall(test_id=self.test_id, timestamp=None),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_test_status_specified_once_only(self):
+        with temp_file_contents(b"Hi") as f:
+            self.patch(_o, '_CHUNK_SIZE', 1)
+            result = get_result_for([
+                self.option,
+                self.test_id,
+                '--attach-file',
+                f.name,
+            ])
+
+            # 'inprogress' status should be on the first packet only, all other
+            # statuses should be on the last packet.
+            if self.status in _FINAL_ACTIONS:
+                first_call = MatchesStatusCall(test_id=self.test_id, 
test_status=None)
+                last_call = MatchesStatusCall(test_id=self.test_id, 
test_status=self.status)
+            else:
+                first_call = MatchesStatusCall(test_id=self.test_id, 
test_status=self.status)
+                last_call = MatchesStatusCall(test_id=self.test_id, 
test_status=None)
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    first_call,
+                    last_call,
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_filename_can_be_overridden(self):
+        with temp_file_contents(b"Hello") as f:
+            specified_file_name = self.getUniqueString()
+            result = get_result_for([
+                self.option,
+                self.test_id,
+                '--attach-file',
+                f.name,
+                '--file-name',
+                specified_file_name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_name=specified_file_name, 
file_bytes=b'Hello'),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_file_name_is_used_by_default(self):
+        with temp_file_contents(b"Hello") as f:
+            result = get_result_for([self.option, self.test_id, 
'--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_name=f.name, file_bytes=b'Hello', 
eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+
+class FileDataTests(TestCase):
+
+    def test_can_attach_file_without_test_id(self):
+        with temp_file_contents(b"Hello") as f:
+            result = get_result_for(['--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(test_id=None, file_bytes=b'Hello', 
eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_file_name_is_used_by_default(self):
+        with temp_file_contents(b"Hello") as f:
+            result = get_result_for(['--attach-file', f.name])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_name=f.name, file_bytes=b'Hello', 
eof=True),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_filename_can_be_overridden(self):
+        with temp_file_contents(b"Hello") as f:
+            specified_file_name = self.getUniqueString()
+            result = get_result_for([
+                '--attach-file',
+                f.name,
+                '--file-name',
+                specified_file_name
+            ])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_name=specified_file_name, 
file_bytes=b'Hello'),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_files_have_timestamp(self):
+        _dummy_timestamp = datetime.datetime(2013, 1, 1, 0, 0, 0, 0, UTC)
+        self.patch(_o, 'create_timestamp', lambda: _dummy_timestamp)
+
+        with temp_file_contents(b"Hello") as f:
+            specified_file_name = self.getUniqueString()
+            result = get_result_for([
+                '--attach-file',
+                f.name,
+            ])
+
+            self.assertThat(
+                result._events,
+                MatchesListwise([
+                    MatchesStatusCall(call='startTestRun'),
+                    MatchesStatusCall(file_bytes=b'Hello', 
timestamp=_dummy_timestamp),
+                    MatchesStatusCall(call='stopTestRun'),
+                ])
+            )
+
+    def test_can_specify_tags_without_test_status(self):
+        result = get_result_for([
+            '--tag',
+            'foo',
+        ])
+
+        self.assertThat(
+            result._events,
+            MatchesListwise([
+                MatchesStatusCall(call='startTestRun'),
+                MatchesStatusCall(test_tags=set(['foo'])),
+                MatchesStatusCall(call='stopTestRun'),
+            ])
+        )
+
+
+class MatchesStatusCall(Matcher):
+
+    _position_lookup = {
+        'call': 0,
+        'test_id': 1,
+        'test_status': 2,
+        'test_tags': 3,
+        'runnable': 4,
+        'file_name': 5,
+        'file_bytes': 6,
+        'eof': 7,
+        'mime_type': 8,
+        'route_code': 9,
+        'timestamp': 10,
+    }
+
+    def __init__(self, **kwargs):
+        unknown_kwargs = list(filter(
+            lambda k: k not in self._position_lookup,
+            kwargs
+        ))
+        if unknown_kwargs:
+            raise ValueError("Unknown keywords: %s" % ','.join(unknown_kwargs))
+        self._filters = kwargs
+
+    def match(self, call_tuple):
+        for k, v in self._filters.items():
+            try:
+                pos = self._position_lookup[k]
+                if call_tuple[pos] != v:
+                    return Mismatch(
+                        "Value for key is %r, not %r" % (call_tuple[pos], v)
+                    )
+            except IndexError:
+                return Mismatch("Key %s is not present." % k)
+
+    def __str__(self):
+        return "<MatchesStatusCall %r>" % self._filters
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-subunit-0.0.16/python_subunit.egg-info/PKG-INFO 
new/python-subunit-0.0.18/python_subunit.egg-info/PKG-INFO
--- old/python-subunit-0.0.16/python_subunit.egg-info/PKG-INFO  2013-11-30 
10:17:04.000000000 +0100
+++ new/python-subunit-0.0.18/python_subunit.egg-info/PKG-INFO  2014-01-29 
20:16:59.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: python-subunit
-Version: 0.0.16
+Version: 0.0.18
 Summary: Python implementation of subunit test streaming protocol
 Home-page: http://launchpad.net/subunit
 Author: Robert Collins
@@ -470,6 +470,8 @@
         ========
         
         * Update versions in configure.ac and python/subunit/__init__.py.
+        * Update Makefile in the root or do an inplace configure to get an 
updated Makefile.
+        * Update NEWS.
         * Make PyPI and regular tarball releases. Upload the regular one to 
LP, the
           PyPI one to PyPI.
         * Push a tagged commit.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-subunit-0.0.16/python_subunit.egg-info/SOURCES.txt 
new/python-subunit-0.0.18/python_subunit.egg-info/SOURCES.txt
--- old/python-subunit-0.0.16/python_subunit.egg-info/SOURCES.txt       
2013-11-30 10:17:05.000000000 +0100
+++ new/python-subunit-0.0.18/python_subunit.egg-info/SOURCES.txt       
2014-01-29 20:16:59.000000000 +0100
@@ -7,6 +7,7 @@
 filters/subunit-filter
 filters/subunit-ls
 filters/subunit-notify
+filters/subunit-output
 filters/subunit-stats
 filters/subunit-tags
 filters/subunit2gtk
@@ -14,6 +15,7 @@
 filters/subunit2pyunit
 filters/tap2subunit
 python/subunit/__init__.py
+python/subunit/_output.py
 python/subunit/chunked.py
 python/subunit/details.py
 python/subunit/filters.py
@@ -28,6 +30,7 @@
 python/subunit/tests/test_chunked.py
 python/subunit/tests/test_details.py
 python/subunit/tests/test_filters.py
+python/subunit/tests/test_output_filter.py
 python/subunit/tests/test_progress_model.py
 python/subunit/tests/test_run.py
 python/subunit/tests/test_subunit_filter.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python-subunit-0.0.16/python_subunit.egg-info/requires.txt 
new/python-subunit-0.0.18/python_subunit.egg-info/requires.txt
--- old/python-subunit-0.0.16/python_subunit.egg-info/requires.txt      
2013-11-30 10:17:04.000000000 +0100
+++ new/python-subunit-0.0.18/python_subunit.egg-info/requires.txt      
2014-01-29 20:16:59.000000000 +0100
@@ -1,2 +1,3 @@
 extras
+testscenarios
 testtools>=0.9.34
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-subunit-0.0.16/setup.py 
new/python-subunit-0.0.18/setup.py
--- old/python-subunit-0.0.16/setup.py  2013-11-30 10:15:03.000000000 +0100
+++ new/python-subunit-0.0.18/setup.py  2014-01-13 01:07:19.000000000 +0100
@@ -10,6 +10,7 @@
     extra = {
         'install_requires': [
             'extras',
+            'testscenarios',
             'testtools>=0.9.34',
         ]
     }
@@ -52,14 +53,15 @@
     scripts = [
         'filters/subunit-1to2',
         'filters/subunit-2to1',
-        'filters/subunit2gtk',
-        'filters/subunit2junitxml',
-        'filters/subunit2pyunit',
         'filters/subunit-filter',
         'filters/subunit-ls',
         'filters/subunit-notify',
+        'filters/subunit-output',
         'filters/subunit-stats',
         'filters/subunit-tags',
+        'filters/subunit2gtk',
+        'filters/subunit2junitxml',
+        'filters/subunit2pyunit',
         'filters/tap2subunit',
     ],
     **extra

++++++ python-subunit-filter-path.patch ++++++
diff -ruN a/python/subunit/tests/test_subunit_filter.py 
b/python/subunit/tests/test_subunit_filter.py
--- a/python/subunit/tests/test_subunit_filter.py       2013-06-16 
11:58:35.000000000 +0200
+++ b/python/subunit/tests/test_subunit_filter.py       2014-01-30 
13:30:26.069310330 +0100
@@ -290,7 +290,7 @@
     def run_command(self, args, stream):
         root = os.path.dirname(
             os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
-        script_path = os.path.join(root, 'filters', 'subunit-filter')
+        script_path = os.path.join('/', 'usr', 'bin', 'subunit-filter')
         command = [sys.executable, script_path] + list(args)
         ps = subprocess.Popen(
             command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to