Hello community,

here is the log from the commit of package python-testtools for 
openSUSE:Factory checked in at 2014-12-09 09:14:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-testtools (Old)
 and      /work/SRC/openSUSE:Factory/.python-testtools.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-testtools"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-testtools/python-testtools.changes        
2014-09-17 17:27:24.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-testtools.new/python-testtools.changes   
2014-12-09 09:14:02.000000000 +0100
@@ -1,0 +2,39 @@
+Mon Dec  1 22:48:47 UTC 2014 - [email protected]
+
+- fix typo in last change 
+
+-------------------------------------------------------------------
+Mon Dec  1 17:04:18 UTC 2014 - [email protected]
+
+- Update to version 1.5.0:
+  * When an import error happens ``testtools.run`` will now show the full
+    error rather than just the name of the module that failed to import.
+    (Robert Collins)
+  * ``testtools.TestCase`` now inherits from unittest2.TestCase, which
+    provides a ``setUpClass`` for upcalls on Python 2.6.
+    (Robert Collins, #1393283)
+  * Fixed our setup.py to use setup_requires to ensure the import dependencies
+    for testtools are present before setup.py runs (as setup.py imports 
testtools
+    to read out the version number). (Robert Collins)
+  * Support setUpClass skipping with self.skipException. Previously this worked
+    with unittest from 2.7 and above but was not supported by testtools - it 
was
+    a happy accident. Since we now hard depend on unittest2, we need to invert
+    our exception lookup priorities to support it. Regular skips done through
+    raise self.skipException will continue to work, since they were always 
caught
+    in our code - its because the suite type being used to implement setUpClass
+    has changed that an issue occured.
+    (Robert Collins, #1393068)
+  * Correctly express our unittest2 dependency: we don't work with old 
releases.
+    (Robert Collins)
+  * Depends on unittest2 for discovery functionality and the ``TestProgram`` 
base
+    class. This brings in many fixes made to discovery where previously we were
+    only using the discovery package or the version in the release of Python
+    that the test execution was occuring on. (Robert Collins, #1271133)
+  * Fixed unit tests which were failing under pypy due to a change in the way
+    pypy formats tracebacks. (Thomi Richards)
+  * Fixed the testtools test suite to run correctly when run via ``unit2``
+    or ``testtools.run discover``.
+  * Make `testtools.content.text_content` error if anything other than text
+    is given as content. (Thomi Richards)
+
+-------------------------------------------------------------------

Old:
----
  testtools-1.1.0.tar.gz

New:
----
  testtools-1.5.0.tar.gz

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

Other differences:
------------------
++++++ python-testtools.spec ++++++
--- /var/tmp/diff_new_pack.J454HH/_old  2014-12-09 09:14:03.000000000 +0100
+++ /var/tmp/diff_new_pack.J454HH/_new  2014-12-09 09:14:03.000000000 +0100
@@ -21,7 +21,7 @@
 %bcond_with tests 
 
 Name:           python-testtools
-Version:        1.1.0
+Version:        1.5.0
 Release:        0
 Summary:        Extensions to the Python Standard Library Unit Testing 
Framework
 License:        MIT
@@ -29,15 +29,14 @@
 Url:            https://launchpad.net/testtools
 Source:         
https://pypi.python.org/packages/source/t/testtools/testtools-%{version}.tar.gz
 BuildRequires:  python-devel
-# Documentation requirements:
-BuildRequires:  python-Sphinx
-# Test requirements:
-%if %{with tests}
 BuildRequires:  python-extras
 BuildRequires:  python-python-mimeparse
-%endif
+BuildRequires:  python-unittest2 >= 0.8.0
+# Documentation requirements:
+BuildRequires:  python-Sphinx
 Requires:       python-extras
 Requires:       python-python-mimeparse
+Requires:       python-unittest2 >= 0.8.0
 BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 %if 0%{?suse_version} && 0%{?suse_version} <= 1110
 %{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}

++++++ testtools-1.1.0.tar.gz -> testtools-1.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/Makefile new/testtools-1.5.0/Makefile
--- old/testtools-1.1.0/Makefile        2013-02-05 10:58:30.000000000 +0100
+++ new/testtools-1.5.0/Makefile        2014-10-31 03:10:59.000000000 +0100
@@ -21,11 +21,11 @@
        -rm MANIFEST
 
 release:
-       ./setup.py sdist upload --sign
+       ./setup.py sdist bdist_wheel upload --sign
        $(PYTHON) scripts/_lp_release.py
 
 snapshot: prerelease
-       ./setup.py sdist
+       ./setup.py sdist bdist_wheel
 
 ### Documentation ###
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/NEWS new/testtools-1.5.0/NEWS
--- old/testtools-1.1.0/NEWS    2014-09-14 00:51:43.000000000 +0200
+++ new/testtools-1.5.0/NEWS    2014-11-21 00:20:13.000000000 +0100
@@ -7,6 +7,76 @@
 NEXT
 ~~~~
 
+1.5.0
+~~~~~
+
+Improvements
+------------
+
+* When an import error happens ``testtools.run`` will now show the full
+  error rather than just the name of the module that failed to import.
+  (Robert Collins)
+
+1.4.0
+~~~~~
+
+Changes
+-------
+
+* ``testtools.TestCase`` now inherits from unittest2.TestCase, which
+  provides a ``setUpClass`` for upcalls on Python 2.6.
+  (Robert Collins, #1393283)
+
+1.3.0
+~~~~~
+
+Changes
+-------
+
+* Fixed our setup.py to use setup_requires to ensure the import dependencies
+  for testtools are present before setup.py runs (as setup.py imports testtools
+  to read out the version number). (Robert Collins)
+
+* Support setUpClass skipping with self.skipException. Previously this worked
+  with unittest from 2.7 and above but was not supported by testtools - it was
+  a happy accident. Since we now hard depend on unittest2, we need to invert
+  our exception lookup priorities to support it. Regular skips done through
+  raise self.skipException will continue to work, since they were always caught
+  in our code - its because the suite type being used to implement setUpClass
+  has changed that an issue occured.
+  (Robert Collins, #1393068)
+
+1.2.1
+~~~~~
+
+Changes
+-------
+
+* Correctly express our unittest2 dependency: we don't work with old releases.
+  (Robert Collins)
+
+1.2.0
+~~~~~
+
+Changes
+-------
+
+* Depends on unittest2 for discovery functionality and the ``TestProgram`` base
+  class. This brings in many fixes made to discovery where previously we were
+  only using the discovery package or the version in the release of Python
+  that the test execution was occuring on. (Robert Collins, #1271133)
+
+* Fixed unit tests which were failing under pypy due to a change in the way
+  pypy formats tracebacks. (Thomi Richards)
+
+* Fixed the testtools test suite to run correctly when run via ``unit2``
+  or ``testtools.run discover``.
+
+* Make `testtools.content.text_content` error if anything other than text
+  is given as content. (Thomi Richards)
+
+* We now publish wheels of testtools. (Robert Collins, #issue84)
+
 1.1.0
 ~~~~~
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/PKG-INFO new/testtools-1.5.0/PKG-INFO
--- old/testtools-1.1.0/PKG-INFO        2014-09-14 00:54:43.000000000 +0200
+++ new/testtools-1.5.0/PKG-INFO        2014-11-21 00:20:56.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: testtools
-Version: 1.1.0
+Version: 1.5.0
 Summary: Extensions to the Python standard library unit testing framework
 Home-page: https://github.com/testing-cabal/testtools
 Author: Jonathan M. Lange
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/README.rst 
new/testtools-1.5.0/README.rst
--- old/testtools-1.1.0/README.rst      2013-10-13 06:03:38.000000000 +0200
+++ new/testtools-1.5.0/README.rst      2014-11-15 20:24:03.000000000 +0100
@@ -31,7 +31,7 @@
 Required Dependencies
 ---------------------
 
- * Python 2.6+ or 3.0+
+ * Python 2.6+ or 3.0+ / pypy (2.x+)
 
 If you would like to use testtools for earlier Python's, please use testtools
 0.9.15.
@@ -39,6 +39,9 @@
  * extras (helpers that we intend to push into Python itself in the near
    future).
 
+ * The most recent unittest2 (backports of the latest unittest API from
+   cPython, which we use to avoid code duplication).
+
 
 Optional Dependencies
 ---------------------
Files old/testtools-1.1.0/doc/.hacking.rst.swp and 
new/testtools-1.5.0/doc/.hacking.rst.swp differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/setup.cfg 
new/testtools-1.5.0/setup.cfg
--- old/testtools-1.1.0/setup.cfg       2014-09-14 00:54:43.000000000 +0200
+++ new/testtools-1.5.0/setup.cfg       2014-11-21 00:20:56.000000000 +0100
@@ -3,6 +3,9 @@
 buffer = 1
 catch = 1
 
+[bdist_wheel]
+universal = 1
+
 [egg_info]
 tag_build = 
 tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/setup.py new/testtools-1.5.0/setup.py
--- old/testtools-1.1.0/setup.py        2014-09-03 06:08:31.000000000 +0200
+++ new/testtools-1.5.0/setup.py        2014-11-15 22:33:08.000000000 +0100
@@ -48,7 +48,7 @@
     # Apparently if we just say "snapshot" then distribute won't accept it
     # as satisfying versioned dependencies. This is a problem for the
     # daily build version.
-    return "snapshot-%s" % (version,)
+    return "%s.0dev0" % (version,)
 
 
 def get_long_description():
@@ -56,6 +56,16 @@
         os.path.dirname(__file__), 'doc/overview.rst')
     return open(manual_path).read()
 
+# Since we import testtools in setup.py, our setup requirements are our install
+# requirements.
+deps = [
+    'extras',
+    # 'mimeparse' has not been uploaded by the maintainer with Python3 compat
+    # but someone kindly uploaded a fixed version as 'python-mimeparse'.
+    'python-mimeparse',
+    'unittest2>=0.8.0',
+    ]
+
 
 setup(name='testtools',
       author='Jonathan M. Lange',
@@ -77,10 +87,6 @@
         ],
       cmdclass=cmd_class,
       zip_safe=False,
-      install_requires=[
-        'extras',
-        # 'mimeparse' has not been uploaded by the maintainer with Python3 
compat
-        # but someone kindly uploaded a fixed version as 'python-mimeparse'.
-        'python-mimeparse',
-        ],
+      install_requires=deps,
+      setup_requires=deps,
       )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/__init__.py 
new/testtools-1.5.0/testtools/__init__.py
--- old/testtools-1.1.0/testtools/__init__.py   2014-09-14 00:52:14.000000000 
+0200
+++ new/testtools-1.5.0/testtools/__init__.py   2014-11-21 00:20:00.000000000 
+0100
@@ -122,4 +122,4 @@
 # If the releaselevel is 'final', then the tarball will be major.minor.micro.
 # Otherwise it is major.minor.micro~$(revno).
 
-__version__ = (1, 1, 0, 'final', 0)
+__version__ = (1, 5, 0, 'final', 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/compat.py 
new/testtools-1.5.0/testtools/compat.py
--- old/testtools-1.1.0/testtools/compat.py     2014-01-29 10:53:19.000000000 
+0100
+++ new/testtools-1.5.0/testtools/compat.py     2014-11-15 10:08:40.000000000 
+0100
@@ -7,10 +7,8 @@
     '_b',
     '_u',
     'advance_iterator',
-    'all',
     'BytesIO',
     'classtypes',
-    'isbaseexception',
     'istext',
     'str_is_unicode',
     'StringIO',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/content.py 
new/testtools-1.5.0/testtools/content.py
--- old/testtools-1.1.0/testtools/content.py    2014-08-21 04:57:56.000000000 
+0200
+++ new/testtools-1.5.0/testtools/content.py    2014-10-20 04:16:48.000000000 
+0200
@@ -25,9 +25,9 @@
     _b,
     _format_exception_only,
     _format_stack_list,
-    _isbytes,
     _TB_HEADER,
     _u,
+    istext,
     str_is_unicode,
 )
 from testtools.content_type import ContentType, JSON, UTF8_TEXT
@@ -63,11 +63,11 @@
 class Content(object):
     """A MIME-like Content object.
 
-    Content objects can be serialised to bytes using the iter_bytes method.
-    If the Content-Type is recognised by other code, they are welcome to
+    'Content' objects can be serialised to bytes using the iter_bytes method.
+    If the 'Content-Type' is recognised by other code, they are welcome to
     look for richer contents that mere byte serialisation - for example in
     memory object graphs etc. However, such code MUST be prepared to receive
-    a generic Content object that has been reconstructed from a byte stream.
+    a generic 'Content' object that has been reconstructed from a byte stream.
 
     :ivar content_type: The content type of this Content.
     """
@@ -128,7 +128,7 @@
 class StackLinesContent(Content):
     """Content object for stack lines.
 
-    This adapts a list of "preprocessed" stack lines into a content object.
+    This adapts a list of "preprocessed" stack lines into a 'Content' object.
     The stack lines are most likely produced from ``traceback.extract_stack``
     or ``traceback.extract_tb``.
 
@@ -180,8 +180,8 @@
 def TracebackContent(err, test):
     """Content object for tracebacks.
 
-    This adapts an exc_info tuple to the Content interface.
-    text/x-traceback;language=python is used for the mime type, in order to
+    This adapts an exc_info tuple to the 'Content' interface.
+    'text/x-traceback;language=python' is used for the mime type, in order to
     provide room for other languages to format their tracebacks differently.
     """
     if err is None:
@@ -223,7 +223,7 @@
 def StacktraceContent(prefix_content="", postfix_content=""):
     """Content object for stack traces.
 
-    This function will create and return a content object that contains a
+    This function will create and return a 'Content' object that contains a
     stack trace.
 
     The mime type is set to 'text/x-traceback;language=python', so other
@@ -251,7 +251,7 @@
 
 
 def json_content(json_data):
-    """Create a JSON `Content` object from JSON-encodeable data."""
+    """Create a JSON Content object from JSON-encodeable data."""
     data = json.dumps(json_data)
     if str_is_unicode:
         # The json module perversely returns native str not bytes
@@ -260,12 +260,14 @@
 
 
 def text_content(text):
-    """Create a `Content` object from some text.
+    """Create a Content object from some text.
 
     This is useful for adding details which are short strings.
     """
-    if _isbytes(text):
-        raise TypeError('text_content must be given a string, not bytes.')
+    if not istext(text):
+        raise TypeError(
+            "text_content must be given text, not '%s'." % type(text).__name__
+        )
     return Content(UTF8_TEXT, lambda: [text.encode('utf8')])
 
 
@@ -278,9 +280,9 @@
 
 def content_from_file(path, content_type=None, chunk_size=DEFAULT_CHUNK_SIZE,
                       buffer_now=False, seek_offset=None, seek_whence=0):
-    """Create a `Content` object from a file on disk.
+    """Create a Content object from a file on disk.
 
-    Note that unless 'read_now' is explicitly passed in as True, the file
+    Note that unless ``buffer_now`` is explicitly passed in as True, the file
     will only be read from when ``iter_bytes`` is called.
 
     :param path: The path to the file to be used as content.
@@ -291,7 +293,7 @@
     :param buffer_now: If True, read the file from disk now and keep it in
         memory. Otherwise, only read when the content is serialized.
     :param seek_offset: If non-None, seek within the stream before reading it.
-    :param seek_whence: If supplied, pass to stream.seek() when seeking.
+    :param seek_whence: If supplied, pass to ``stream.seek()`` when seeking.
     """
     if content_type is None:
         content_type = UTF8_TEXT
@@ -308,13 +310,13 @@
 def content_from_stream(stream, content_type=None,
                         chunk_size=DEFAULT_CHUNK_SIZE, buffer_now=False,
                         seek_offset=None, seek_whence=0):
-    """Create a `Content` object from a file-like stream.
+    """Create a Content object from a file-like stream.
 
-    Note that the stream will only be read from when ``iter_bytes`` is
-    called.
+    Note that unless ``buffer_now`` is explicitly passed in as True, the stream
+    will only be read from when ``iter_bytes`` is called.
 
     :param stream: A file-like object to read the content from. The stream
-        is not closed by this function or the content object it returns.
+        is not closed by this function or the 'Content' object it returns.
     :param content_type: The type of content. If not specified, defaults
         to UTF8-encoded text/plain.
     :param chunk_size: The size of chunks to read from the file.
@@ -322,7 +324,7 @@
     :param buffer_now: If True, reads from the stream right now. Otherwise,
         only reads when the content is serialized. Defaults to False.
     :param seek_offset: If non-None, seek within the stream before reading it.
-    :param seek_whence: If supplied, pass to stream.seek() when seeking.
+    :param seek_whence: If supplied, pass to ``stream.seek()`` when seeking.
     """
     if content_type is None:
         content_type = UTF8_TEXT
@@ -353,9 +355,9 @@
 
     This is a convenience method wrapping around ``addDetail``.
 
-    Note that unless 'read_now' is explicitly passed in as True, the file
-    *must* exist when the test result is called with the results of this
-    test, after the test has been torn down.
+    Note that by default the contents of the file will be read immediately. If
+    ``buffer_now`` is False, then the file *must* exist when the test result is
+    called with the results of this test, after the test has been torn down.
 
     :param detailed: An object with details
     :param path: The path to the file to attach.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/run.py 
new/testtools-1.5.0/testtools/run.py
--- old/testtools-1.1.0/testtools/run.py        2014-09-12 00:21:19.000000000 
+0200
+++ new/testtools-1.5.0/testtools/run.py        2014-11-18 22:55:29.000000000 
+0100
@@ -8,9 +8,10 @@
  $ python -m testtools.run testtools.tests.test_suite
 """
 
+import argparse
 from functools import partial
 import os.path
-import unittest
+import unittest2 as unittest
 import sys
 
 from extras import safe_hasattr
@@ -22,21 +23,14 @@
 
 defaultTestLoader = unittest.defaultTestLoader
 defaultTestLoaderCls = unittest.TestLoader
+have_discover = True
+discover_impl = unittest.loader
 
-if getattr(defaultTestLoader, 'discover', None) is None:
-    try:
-        import discover
-        defaultTestLoader = discover.DiscoveringTestLoader()
-        defaultTestLoaderCls = discover.DiscoveringTestLoader
-        have_discover = True
-        discover_impl = discover
-    except ImportError:
-        have_discover = False
-else:
-    have_discover = True
-    discover_impl = unittest.loader
-discover_fixed = False
-
+# Kept for API compatibility, but no longer used.
+BUFFEROUTPUT = ""
+CATCHBREAK = ""
+FAILFAST = ""
+USAGE_AS_MAIN = ""
 
 def list_test(test):
     """Return the test ids that would be run if test() was run.
@@ -52,7 +46,9 @@
         describing things that failed to import.
     """
     unittest_import_strs = set([
-        'unittest.loader.ModuleImportFailure.', 'discover.ModuleImportFailure.'
+        'unittest2.loader.ModuleImportFailure.',
+        'unittest.loader.ModuleImportFailure.',
+        'discover.ModuleImportFailure.'
         ])
     test_ids = []
     errors = []
@@ -84,13 +80,13 @@
             stdout = sys.stdout
         self.stdout = stdout
 
-    def list(self, test):
+    def list(self, test, loader):
         """List the tests that would be run if test() was run."""
-        test_ids, errors = list_test(test)
+        test_ids, _ = list_test(test)
         for test_id in test_ids:
             self.stdout.write('%s\n' % test_id)
+        errors = loader.errors
         if errors:
-            self.stdout.write('Failed to import\n')
             for test_id in errors:
                 self.stdout.write('%s\n' % test_id)
             sys.exit(2)
@@ -110,75 +106,23 @@
 # Taken from python 2.7 and slightly modified for compatibility with
 # older versions. Delete when 2.7 is the oldest supported version.
 # Modifications:
-#  - Use have_discover to raise an error if the user tries to use
-#    discovery on an old version and doesn't have discover installed.
-#  - If --catch is given check that installHandler is available, as
-#    it won't be on old python versions.
-#  - print calls have been been made single-source python3 compatibile.
-#  - exception handling likewise.
-#  - The default help has been changed to USAGE_AS_MAIN and USAGE_FROM_MODULE
-#    removed.
-#  - A tweak has been added to detect 'python -m *.run' and use a
-#    better progName in that case.
-#  - self.module is more comprehensively set to None when being invoked from
-#    the commandline - __name__ is used as a sentinel value.
+#  - If --catch is given, check that installHandler is available, as
+#    it won't be on old python versions or python builds without signals.
 #  - --list has been added which can list tests (should be upstreamed).
 #  - --load-list has been added which can reduce the tests used (should be
 #    upstreamed).
-#  - The limitation of using getopt is declared to the user.
-#  - http://bugs.python.org/issue16709 is worked around, by sorting tests when
-#    discover is used.
-#  - We monkey-patch the discover and unittest loaders to address
-#     http://bugs.python.org/issue16662 with the proposed upstream patch.
-
-FAILFAST     = "  -f, --failfast   Stop on first failure\n"
-CATCHBREAK   = "  -c, --catch      Catch control-C and display results\n"
-BUFFEROUTPUT = "  -b, --buffer     Buffer stdout and stderr during test runs\n"
-
-USAGE_AS_MAIN = """\
-Usage: %(progName)s [options] [tests]
-
-Options:
-  -h, --help       Show this message
-  -v, --verbose    Verbose output
-  -q, --quiet      Minimal output
-  -l, --list       List tests rather than executing them.
-  --load-list      Specifies a file containing test ids, only tests matching
-                   those ids are executed.
-%(failfast)s%(catchbreak)s%(buffer)s
-Examples:
-  %(progName)s test_module               - run tests from test_module
-  %(progName)s module.TestClass          - run tests from module.TestClass
-  %(progName)s module.Class.test_method  - run specified test method
-
-All options must come before [tests].  [tests] can be a list of any number of
-test modules, classes and test methods.
-
-Alternative Usage: %(progName)s discover [options]
-
-Options:
-  -v, --verbose    Verbose output
-%(failfast)s%(catchbreak)s%(buffer)s  -s directory     Directory to start 
discovery ('.' default)
-  -p pattern       Pattern to match test files ('test*.py' default)
-  -t directory     Top level directory of project (default to
-                   start directory)
-  -l, --list       List tests rather than executing them.
-  --load-list      Specifies a file containing test ids, only tests matching
-                   those ids are executed.
-
-For test discovery all test modules must be importable from the top
-level directory of the project.
-"""
 
 
-class TestProgram(object):
+class TestProgram(unittest.TestProgram):
     """A command-line program that runs a set of tests; this is primarily
        for making test modules conveniently executable.
     """
-    USAGE = USAGE_AS_MAIN
 
     # defaults for testing
+    module=None
+    verbosity = 1
     failfast = catchbreak = buffer = progName = None
+    _discovery_parser = None
 
     def __init__(self, module=__name__, defaultTest=None, argv=None,
                     testRunner=None, testLoader=defaultTestLoader,
@@ -204,6 +148,7 @@
         self.verbosity = verbosity
         self.buffer = buffer
         self.defaultTest = defaultTest
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
         self.listtests = False
         self.load_list = None
         self.testRunner = testRunner
@@ -216,6 +161,7 @@
             progName = os.path.basename(argv[0])
         self.progName = progName
         self.parseArgs(argv)
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
         if self.load_list:
             # TODO: preserve existing suites (like testresources does in
             # OptimisingTestSuite.add, but with a standard protocol).
@@ -228,155 +174,38 @@
                 source.close()
             test_ids = set(line.strip().decode('utf-8') for line in lines)
             self.test = filter_by_ids(self.test, test_ids)
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
         if not self.listtests:
             self.runTests()
         else:
             runner = self._get_runner()
             if safe_hasattr(runner, 'list'):
-                runner.list(self.test)
+                try:
+                    runner.list(self.test, loader=self.testLoader)
+                except TypeError:
+                    runner.list(self.test)
             else:
                 for test in iterate_tests(self.test):
                     self.stdout.write('%s\n' % test.id())
+        del self.testLoader.errors[:]
 
-    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
-        print(self.USAGE % usage)
-        sys.exit(2)
-
-    def parseArgs(self, argv):
-        if len(argv) > 1 and argv[1].lower() == 'discover':
-            self._do_discovery(argv[2:])
-            return
-
-        import getopt
-        long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer',
-            'list', 'load-list=']
-        try:
-            options, args = getopt.getopt(argv[1:], 'hHvqfcbl', long_opts)
-            for opt, value in options:
-                if opt in ('-h','-H','--help'):
-                    self.usageExit()
-                if opt in ('-q','--quiet'):
-                    self.verbosity = 0
-                if opt in ('-v','--verbose'):
-                    self.verbosity = 2
-                if opt in ('-f','--failfast'):
-                    if self.failfast is None:
-                        self.failfast = True
-                    # Should this raise an exception if -f is not valid?
-                if opt in ('-c','--catch'):
-                    if self.catchbreak is None:
-                        self.catchbreak = True
-                    # Should this raise an exception if -c is not valid?
-                if opt in ('-b','--buffer'):
-                    if self.buffer is None:
-                        self.buffer = True
-                    # Should this raise an exception if -b is not valid?
-                if opt in ('-l', '--list'):
-                    self.listtests = True
-                if opt == '--load-list':
-                    self.load_list = value
-            if len(args) == 0 and self.defaultTest is None:
-                # createTests will load tests from self.module
-                self.testNames = None
-            elif len(args) > 0:
-                self.testNames = args
-            else:
-                self.testNames = (self.defaultTest,)
-            self.createTests()
-        except getopt.error:
-            self.usageExit(sys.exc_info()[1])
-
-    def createTests(self):
-        if self.testNames is None:
-            self.test = self.testLoader.loadTestsFromModule(self.module)
-        else:
-            self.test = self.testLoader.loadTestsFromNames(self.testNames,
-                                                           self.module)
-
-    def _do_discovery(self, argv, Loader=defaultTestLoaderCls):
-        # handle command line args for test discovery
-        if not have_discover:
-            raise AssertionError("Unable to use discovery, must use python 2.7 
"
-                    "or greater, or install the discover package.")
-        _fix_discovery()
-        self.progName = '%s discover' % self.progName
-        import optparse
-        parser = optparse.OptionParser()
-        parser.prog = self.progName
-        parser.add_option('-v', '--verbose', dest='verbose', default=False,
-                          help='Verbose output', action='store_true')
-        if self.failfast != False:
-            parser.add_option('-f', '--failfast', dest='failfast', 
default=False,
-                              help='Stop on first fail or error',
-                              action='store_true')
-        if self.catchbreak != False:
-            parser.add_option('-c', '--catch', dest='catchbreak', 
default=False,
-                              help='Catch ctrl-C and display results so far',
-                              action='store_true')
-        if self.buffer != False:
-            parser.add_option('-b', '--buffer', dest='buffer', default=False,
-                              help='Buffer stdout and stderr during tests',
-                              action='store_true')
-        parser.add_option('-s', '--start-directory', dest='start', default='.',
-                          help="Directory to start discovery ('.' default)")
-        parser.add_option('-p', '--pattern', dest='pattern', 
default='test*.py',
-                          help="Pattern to match tests ('test*.py' default)")
-        parser.add_option('-t', '--top-level-directory', dest='top', 
default=None,
-                          help='Top level directory of project (defaults to 
start directory)')
-        parser.add_option('-l', '--list', dest='listtests', default=False, 
action="store_true",
-                          help='List tests rather than running them.')
-        parser.add_option('--load-list', dest='load_list', default=None,
-                          help='Specify a filename containing the test ids to 
use.')
-
-        options, args = parser.parse_args(argv)
-        if len(args) > 3:
-            self.usageExit()
-
-        for name, value in zip(('start', 'pattern', 'top'), args):
-            setattr(options, name, value)
-
-        # only set options from the parsing here
-        # if they weren't set explicitly in the constructor
-        if self.failfast is None:
-            self.failfast = options.failfast
-        if self.catchbreak is None:
-            self.catchbreak = options.catchbreak
-        if self.buffer is None:
-            self.buffer = options.buffer
-        self.listtests = options.listtests
-        self.load_list = options.load_list
-
-        if options.verbose:
-            self.verbosity = 2
-
-        start_dir = options.start
-        pattern = options.pattern
-        top_level_dir = options.top
-
-        loader = Loader()
-        # See http://bugs.python.org/issue16709
-        # While sorting here is intrusive, its better than being random.
-        # Rules for the sort:
-        # - standard suites are flattened, and the resulting tests sorted by
-        #   id.
-        # - non-standard suites are preserved as-is, and sorted into position
-        #   by the first test found by iterating the suite.
-        # We do this by a DSU process: flatten and grab a key, sort, strip the
-        # keys.
-        loaded = loader.discover(start_dir, pattern, top_level_dir)
-        self.test = sorted_tests(loaded)
+    def _getParentArgParser(self):
+        parser = super(TestProgram, self)._getParentArgParser()
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
+        parser.add_argument('-l', '--list', dest='listtests', default=False,
+            action='store_true', help='List tests rather than executing them')
+        parser.add_argument('--load-list', dest='load_list', default=None,
+            help='Specifies a file containing test ids, only tests matching '
+                'those ids are executed')
+        return parser
+
+    def _do_discovery(self, argv, Loader=None):
+        super(TestProgram, self)._do_discovery(argv, Loader=Loader)
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
+        self.test = sorted_tests(self.test)
 
     def runTests(self):
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
         if (self.catchbreak
             and getattr(unittest, 'installHandler', None) is not None):
             unittest.installHandler()
@@ -386,6 +215,7 @@
             sys.exit(not self.result.wasSuccessful())
 
     def _get_runner(self):
+        # XXX: Local edit (see http://bugs.python.org/issue22860)
         if self.testRunner is None:
             self.testRunner = TestToolsTestRunner
         try:
@@ -410,120 +240,6 @@
         return testRunner
 
 
-def _fix_discovery():
-    # Monkey patch in the bugfix from http://bugs.python.org/issue16662
-    # - the code here is a straight copy from the Python core tree
-    # with the patch applied.
-    global discover_fixed
-    if discover_fixed:
-        return
-    # Do we have a fixed Python?
-    # (not committed upstream yet - so we can't uncomment this code,
-    # but when it gets committed, the next version to be released won't
-    # need monkey patching.
-    # if sys.version_info[:2] > (3, 4):
-    #     discover_fixed = True
-    #     return
-    if not have_discover:
-        return
-    if safe_hasattr(discover_impl, '_jython_aware_splitext'):
-        _jython_aware_splitext = discover_impl._jython_aware_splitext
-    else:
-        def _jython_aware_splitext(path):
-            if path.lower().endswith('$py.class'):
-                return path[:-9]
-            return os.path.splitext(path)[0]
-    def loadTestsFromModule(self, module, use_load_tests=True, pattern=None):
-        """Return a suite of all tests cases contained in the given module"""
-        # use_load_tests is preserved for compatability though it was never
-        # an official API.
-        # pattern is not an official API either; it is used in discovery to
-        # chain the requested pattern down.
-        tests = []
-        for name in dir(module):
-            obj = getattr(module, name)
-            if isinstance(obj, type) and issubclass(obj, unittest.TestCase):
-                tests.append(self.loadTestsFromTestCase(obj))
-
-        load_tests = getattr(module, 'load_tests', None)
-        tests = self.suiteClass(tests)
-        if use_load_tests and load_tests is not None:
-            try:
-                return load_tests(self, tests, pattern)
-            except Exception as e:
-                return discover_impl._make_failed_load_tests(
-                    module.__name__, e, self.suiteClass)
-        return tests
-    def _find_tests(self, start_dir, pattern, namespace=False):
-        """Used by discovery. Yields test suites it loads."""
-        paths = sorted(os.listdir(start_dir))
-
-        for path in paths:
-            full_path = os.path.join(start_dir, path)
-            if os.path.isfile(full_path):
-                if not discover_impl.VALID_MODULE_NAME.match(path):
-                    # valid Python identifiers only
-                    continue
-                if not self._match_path(path, full_path, pattern):
-                    continue
-                # if the test file matches, load it
-                name = self._get_name_from_path(full_path)
-                try:
-                    module = self._get_module_from_name(name)
-                except testcase.TestSkipped as e:
-                    yield discover_impl._make_skipped_test(
-                        name, e, self.suiteClass)
-                except:
-                    yield discover_impl._make_failed_import_test(
-                        name, self.suiteClass)
-                else:
-                    mod_file = os.path.abspath(getattr(module, '__file__', 
full_path))
-                    realpath = _jython_aware_splitext(
-                        os.path.realpath(mod_file))
-                    fullpath_noext = _jython_aware_splitext(
-                        os.path.realpath(full_path))
-                    if realpath.lower() != fullpath_noext.lower():
-                        module_dir = os.path.dirname(realpath)
-                        mod_name = _jython_aware_splitext(
-                            os.path.basename(full_path))
-                        expected_dir = os.path.dirname(full_path)
-                        msg = ("%r module incorrectly imported from %r. 
Expected %r. "
-                               "Is this module globally installed?")
-                        raise ImportError(msg % (mod_name, module_dir, 
expected_dir))
-                    yield self.loadTestsFromModule(module, pattern=pattern)
-            elif os.path.isdir(full_path):
-                if (not namespace and
-                    not os.path.isfile(os.path.join(full_path, 
'__init__.py'))):
-                    continue
-
-                load_tests = None
-                tests = None
-                name = self._get_name_from_path(full_path)
-                try:
-                    package = self._get_module_from_name(name)
-                except testcase.TestSkipped as e:
-                    yield discover_impl._make_skipped_test(
-                        name, e, self.suiteClass)
-                except:
-                    yield discover_impl._make_failed_import_test(
-                        name, self.suiteClass)
-                else:
-                    load_tests = getattr(package, 'load_tests', None)
-                    tests = self.loadTestsFromModule(package, pattern=pattern)
-                    if tests is not None:
-                        # tests loaded from package file
-                        yield tests
-
-                    if load_tests is not None:
-                        # loadTestsFromModule(package) has load_tests for us.
-                        continue
-                    # recurse into the package
-                    pkg_tests =  self._find_tests(
-                        full_path, pattern, namespace=namespace)
-                    for test in pkg_tests:
-                        yield test
-    defaultTestLoaderCls.loadTestsFromModule = loadTestsFromModule
-    defaultTestLoaderCls._find_tests = _find_tests
 
 ################
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/testcase.py 
new/testtools-1.5.0/testtools/testcase.py
--- old/testtools-1.1.0/testtools/testcase.py   2014-09-12 00:40:41.000000000 
+0200
+++ new/testtools-1.5.0/testtools/testcase.py   2014-11-17 19:40:37.000000000 
+0100
@@ -20,12 +20,12 @@
 import itertools
 import sys
 import types
-import unittest
 
 from extras import (
     safe_hasattr,
     try_import,
     )
+import unittest2 as unittest
 
 from testtools import (
     content,
@@ -57,8 +57,8 @@
 
 class TestSkipped(Exception):
     """Raised within TestCase.run() when a test is skipped."""
-TestSkipped = try_import('unittest2.case.SkipTest', TestSkipped)
 TestSkipped = try_import('unittest.case.SkipTest', TestSkipped)
+TestSkipped = try_import('unittest2.case.SkipTest', TestSkipped)
 
 
 class _UnexpectedSuccess(Exception):
@@ -68,9 +68,9 @@
     module.
     """
 _UnexpectedSuccess = try_import(
-    'unittest2.case._UnexpectedSuccess', _UnexpectedSuccess)
-_UnexpectedSuccess = try_import(
     'unittest.case._UnexpectedSuccess', _UnexpectedSuccess)
+_UnexpectedSuccess = try_import(
+    'unittest2.case._UnexpectedSuccess', _UnexpectedSuccess)
 
 class _ExpectedFailure(Exception):
     """An expected failure occured.
@@ -79,9 +79,9 @@
     module.
     """
 _ExpectedFailure = try_import(
-    'unittest2.case._ExpectedFailure', _ExpectedFailure)
-_ExpectedFailure = try_import(
     'unittest.case._ExpectedFailure', _ExpectedFailure)
+_ExpectedFailure = try_import(
+    'unittest2.case._ExpectedFailure', _ExpectedFailure)
 
 
 # Copied from unittest before python 3.4 release. Used to maintain
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/__init__.py 
new/testtools-1.5.0/testtools/tests/__init__.py
--- old/testtools-1.1.0/testtools/tests/__init__.py     2014-09-12 
00:21:19.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/__init__.py     2014-11-15 
10:08:40.000000000 +0100
@@ -25,6 +25,7 @@
         test_testcase,
         test_testresult,
         test_testsuite,
+        test_with_with,
         )
     modules = [
         matchers,
@@ -44,6 +45,7 @@
         test_testcase,
         test_testresult,
         test_testsuite,
+        test_with_with,
         ]
     suites = map(lambda x: x.test_suite(), modules)
     return TestSuite(suites)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/test_content.py 
new/testtools-1.5.0/testtools/tests/test_content.py
--- old/testtools-1.1.0/testtools/tests/test_content.py 2014-08-21 
04:57:56.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_content.py 2014-10-20 
04:16:48.000000000 +0200
@@ -196,6 +196,17 @@
         data = _b("Some Bytes")
         self.assertRaises(TypeError, text_content, data)
 
+    def test_text_content_raises_TypeError_when_passed_non_text(self):
+        bad_values = (None, list(), dict(), 42, 1.23)
+        for value in bad_values:
+            self.assertThat(
+                lambda: text_content(value),
+                raises(
+                    TypeError("text_content must be given text, not '%s'." %
+                        type(value).__name__)
+                ),
+            )
+
     def test_json_content(self):
         data = {'foo': 'bar'}
         expected = Content(JSON, lambda: [_b('{"foo": "bar"}')])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/testtools-1.1.0/testtools/tests/test_deferredruntest.py 
new/testtools-1.5.0/testtools/tests/test_deferredruntest.py
--- old/testtools-1.1.0/testtools/tests/test_deferredruntest.py 2014-09-02 
13:20:23.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_deferredruntest.py 2014-11-15 
10:08:40.000000000 +0100
@@ -771,7 +771,10 @@
 
 
 def test_suite():
-    from unittest import TestLoader, TestSuite
-    return TestSuite(
-        [TestLoader().loadTestsFromName(__name__),
-         make_integration_tests()])
+    from unittest2 import TestLoader, TestSuite
+    return TestLoader().loadTestsFromName(__name__)
+
+
+def load_tests(loader, tests, pattern):
+    tests.addTest(make_integration_tests())
+    return tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/test_run.py 
new/testtools-1.5.0/testtools/tests/test_run.py
--- old/testtools-1.1.0/testtools/tests/test_run.py     2014-09-12 
00:21:19.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_run.py     2014-11-18 
22:59:06.000000000 +0100
@@ -2,6 +2,7 @@
 
 """Tests for the test runner logic."""
 
+import doctest
 from unittest import TestSuite
 import sys
 from textwrap import dedent
@@ -9,6 +10,7 @@
 from extras import try_import
 fixtures = try_import('fixtures')
 testresources = try_import('testresources')
+import unittest2
 
 import testtools
 from testtools import TestCase, run, skipUnless
@@ -19,6 +21,7 @@
     )
 from testtools.matchers import (
     Contains,
+    DocTestMatches,
     MatchesRegex,
     )
 
@@ -105,7 +108,7 @@
             testtools.__path__.append(self.package.base)
 
 
-if fixtures and run.have_discover:
+if fixtures:
     class SampleLoadTestsPackage(fixtures.Fixture):
         """Creates a test suite package using load_tests."""
 
@@ -155,6 +158,27 @@
         self.assertEqual([set(['testtools.runexample.TestFoo.test_bar',
             'testtools.runexample.TestFoo.test_quux'])], tests)
 
+    def test_run_list_with_loader(self):
+        # list() is attempted with a loader first.
+        self.useFixture(SampleTestFixture())
+        tests = []
+        class CaptureList(run.TestToolsTestRunner):
+            def list(self, test, loader=None):
+                tests.append(set([case.id() for case
+                    in testtools.testsuite.iterate_tests(test)]))
+                tests.append(loader)
+        out = StringIO()
+        try:
+            program = run.TestProgram(
+                argv=['prog', '-l', 'testtools.runexample.test_suite'],
+                stdout=out, testRunner=CaptureList)
+        except SystemExit:
+            exc_info = sys.exc_info()
+            raise AssertionError("-l tried to exit. %r" % exc_info[1])
+        self.assertEqual([set(['testtools.runexample.TestFoo.test_bar',
+            'testtools.runexample.TestFoo.test_quux']), program.testLoader],
+            tests)
+
     def test_run_list(self):
         self.useFixture(SampleTestFixture())
         out = StringIO()
@@ -168,17 +192,27 @@
 """, out.getvalue())
 
     def test_run_list_failed_import(self):
-        if not run.have_discover:
-            self.skipTest("Need discover")
         broken = self.useFixture(SampleTestFixture(broken=True))
         out = StringIO()
+        # XXX: http://bugs.python.org/issue22811
+        unittest2.defaultTestLoader._top_level_dir = None
         exc = self.assertRaises(
             SystemExit,
             run.main, ['prog', 'discover', '-l', broken.package.base, '*.py'], 
out)
         self.assertEqual(2, exc.args[0])
-        self.assertEqual("""Failed to import
-runexample
-""", out.getvalue())
+        self.assertThat(out.getvalue(), DocTestMatches("""\
+Failed to import test module: runexample
+Traceback (most recent call last):
+  File ".../loader.py", line ..., in _find_test_path
+    package = self._get_module_from_name(name)
+  File ".../loader.py", line ..., in _get_module_from_name
+    __import__(name)
+  File ".../runexample/__init__.py", line 1
+    class not in
+...^...
+SyntaxError: invalid syntax
+
+""", doctest.ELLIPSIS))
 
     def test_run_orders_tests(self):
         self.useFixture(SampleTestFixture())
@@ -201,7 +235,8 @@
                 'testtools.runexample.test_suite'], out)
         except SystemExit:
             exc_info = sys.exc_info()
-            raise AssertionError("-l tried to exit. %r" % exc_info[1])
+            raise AssertionError(
+                "-l --load-list tried to exit. %r" % exc_info[1])
         self.assertEqual("""testtools.runexample.TestFoo.test_bar
 """, out.getvalue())
 
@@ -226,7 +261,8 @@
                 'testtools.runexample.test_suite'], out)
         except SystemExit:
             exc_info = sys.exc_info()
-            raise AssertionError("-l tried to exit. %r" % exc_info[1])
+            raise AssertionError(
+                "-l --load-list tried to exit. %r" % exc_info[1])
         self.assertEqual("""testtools.runexample.TestFoo.test_bar
 """, out.getvalue())
 
@@ -287,7 +323,6 @@
 OK
 """)))
 
-    @skipUnless(run.have_discover, "discovery not present")
     @skipUnless(fixtures, "fixtures not present")
     def test_issue_16662(self):
         # unittest's discover implementation didn't handle load_tests on
@@ -296,6 +331,8 @@
         # See http://bugs.python.org/issue16662
         pkg = self.useFixture(SampleLoadTestsPackage())
         out = StringIO()
+        # XXX: http://bugs.python.org/issue22811
+        unittest2.defaultTestLoader._top_level_dir = None
         self.assertEqual(None, run.main(
             ['prog', 'discover', '-l', pkg.package.base], out))
         self.assertEqual(dedent("""\
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/test_testcase.py 
new/testtools-1.5.0/testtools/tests/test_testcase.py
--- old/testtools-1.1.0/testtools/tests/test_testcase.py        2014-09-02 
13:20:23.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_testcase.py        2014-11-17 
19:40:37.000000000 +0100
@@ -51,12 +51,6 @@
     FullStackRunTest,
     LoggingResult,
     )
-try:
-    exec('from __future__ import with_statement')
-except SyntaxError:
-    pass
-else:
-    from testtools.tests.test_with_with import *
 
 
 class TestPlaceHolder(TestCase):
@@ -1608,40 +1602,43 @@
         self.assertRaises(ZeroDivisionError, wrapped)
 
 
+class Attributes(WithAttributes, TestCase):
+    @attr('foo')
+    def simple(self):
+        pass
+
+    # Not sorted here, forward or backwards.
+    @attr('foo', 'quux', 'bar')
+    def many(self):
+        pass
+
+    # Not sorted here, forward or backwards.
+    @attr('bar')
+    @attr('quux')
+    @attr('foo')
+    def decorated(self):
+        pass
+
+
 class TestAttributes(TestCase):
 
     def test_simple_attr(self):
         # Adding an attr to a test changes its id().
-        class MyTest(WithAttributes, TestCase):
-            @attr('foo')
-            def test_bar(self):
-                pass
-        case = MyTest('test_bar')
-        self.assertEqual('testtools.tests.test_testcase.MyTest.test_bar[foo]',
+        case = Attributes('simple')
+        self.assertEqual(
+            'testtools.tests.test_testcase.Attributes.simple[foo]',
             case.id())
 
     def test_multiple_attributes(self):
-        class MyTest(WithAttributes, TestCase):
-            # Not sorted here, forward or backwards.
-            @attr('foo', 'quux', 'bar')
-            def test_bar(self):
-                pass
-        case = MyTest('test_bar')
+        case = Attributes('many')
         self.assertEqual(
-            'testtools.tests.test_testcase.MyTest.test_bar[bar,foo,quux]',
+            'testtools.tests.test_testcase.Attributes.many[bar,foo,quux]',
             case.id())
 
     def test_multiple_attr_decorators(self):
-        class MyTest(WithAttributes, TestCase):
-            # Not sorted here, forward or backwards.
-            @attr('bar')
-            @attr('quux')
-            @attr('foo')
-            def test_bar(self):
-                pass
-        case = MyTest('test_bar')
+        case = Attributes('decorated')
         self.assertEqual(
-            'testtools.tests.test_testcase.MyTest.test_bar[bar,foo,quux]',
+            'testtools.tests.test_testcase.Attributes.decorated[bar,foo,quux]',
             case.id())
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/test_testresult.py 
new/testtools-1.5.0/testtools/tests/test_testresult.py
--- old/testtools-1.1.0/testtools/tests/test_testresult.py      2014-09-03 
06:08:31.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_testresult.py      2014-10-20 
04:16:48.000000000 +0200
@@ -9,6 +9,7 @@
 import doctest
 from itertools import chain, combinations
 import os
+import re
 import shutil
 import sys
 import tempfile
@@ -69,6 +70,7 @@
     HasLength,
     MatchesAny,
     MatchesException,
+    MatchesRegex,
     Raises,
     )
 from testtools.tests.helpers import (
@@ -2547,13 +2549,18 @@
         self._write_module("bad", "iso-8859-5",
             "# coding: iso-8859-5\n%% = 0 # %s\n" % text)
         textoutput = self._run_external_case()
-        self.assertIn(self._as_output(_u(
-            #'bad.py", line 2\n'
-            '    %% = 0 # %s\n'
-            + ' ' * self._error_on_character +
-            '   ^\n'
-            'SyntaxError: ') %
-            (text,)), textoutput)
+        self.assertThat(
+            textoutput,
+            MatchesRegex(
+                self._as_output(_u(
+                #'bad.py", line 2\n'
+                '.*%% = 0 # %s\n'
+                + ' ' * self._error_on_character +
+                '\\s*\\^\n'
+                'SyntaxError:.*') %
+                (text,)),
+            re.MULTILINE | re.DOTALL)
+        )
 
     def test_syntax_error_line_euc_jp(self):
         """Syntax error on a euc_jp line shows the line decoded"""
@@ -2579,13 +2586,17 @@
         textoutput = self._setup_external_case("import bad")
         self._write_module("bad", "utf-8", _u("\ufeff^ = 0 # %s\n") % text)
         textoutput = self._run_external_case()
-        self.assertIn(self._as_output(_u(
-            'bad.py", line 1\n'
-            '    ^ = 0 # %s\n'
-            + ' ' * self._error_on_character +
-            '   ^\n'
-            'SyntaxError: ') %
-            text), textoutput)
+        self.assertThat(
+            textoutput,
+            MatchesRegex(
+                self._as_output(_u(
+                    '.*bad.py", line 1\n'
+                    '\\s*\\^ = 0 # %s\n'
+                    + ' ' * self._error_on_character +
+                    '\\s*\\^\n'
+                    'SyntaxError:.*') % text),
+                re.M | re.S)
+        )
 
 
 class TestNonAsciiResultsWithUnittest(TestNonAsciiResults):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/test_testsuite.py 
new/testtools-1.5.0/testtools/tests/test_testsuite.py
--- old/testtools-1.1.0/testtools/tests/test_testsuite.py       2014-08-24 
07:10:42.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_testsuite.py       2014-11-17 
19:40:37.000000000 +0100
@@ -8,6 +8,7 @@
 from functools import partial
 import sys
 import unittest
+import unittest2
 
 from extras import try_import
 
@@ -200,6 +201,42 @@
         tests = list(enumerate(iterate_tests(suite)))
         return [(test, _u(str(pos))) for pos, test in tests]
 
+    def test_setupclass_skip(self):
+        # We should support setupclass skipping using cls.skipException.
+        # Because folk have used that.
+        class Skips(TestCase):
+            @classmethod
+            def setUpClass(cls):
+                raise cls.skipException('foo')
+            def test_notrun(self):
+                pass
+        # Test discovery uses the default suite from unittest2 (unless users
+        # deliberately change things, in which case they keep both pieces).
+        suite = unittest2.TestSuite([Skips("test_notrun")])
+        log = []
+        result = LoggingResult(log)
+        suite.run(result)
+        self.assertEqual(['addSkip'], [item[0] for item in log])
+
+    def test_setupclass_upcall(self):
+        # Note that this is kindof-a-case-test, kindof-suite, because
+        # setUpClass is linked between them.
+        class Simples(TestCase):
+            @classmethod
+            def setUpClass(cls):
+                super(Simples, cls).setUpClass()
+            def test_simple(self):
+                pass
+        # Test discovery uses the default suite from unittest2 (unless users
+        # deliberately change things, in which case they keep both pieces).
+        suite = unittest2.TestSuite([Simples("test_simple")])
+        log = []
+        result = LoggingResult(log)
+        suite.run(result)
+        self.assertEqual(
+            ['startTest', 'addSuccess', 'stopTest'],
+            [item[0] for item in log])
+
 
 class TestFixtureSuite(TestCase):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/tests/test_with_with.py 
new/testtools-1.5.0/testtools/tests/test_with_with.py
--- old/testtools-1.1.0/testtools/tests/test_with_with.py       2013-04-20 
07:10:07.000000000 +0200
+++ new/testtools-1.5.0/testtools/tests/test_with_with.py       2014-11-15 
10:08:40.000000000 +0100
@@ -86,3 +86,8 @@
                 pass
         exc = self.assertRaises(AssertionError, die)
         self.assertThat(exc.args[0], EndsWith(': foo'))
+
+
+def test_suite():
+    from unittest import TestLoader
+    return TestLoader().loadTestsFromName(__name__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools/testsuite.py 
new/testtools-1.5.0/testtools/testsuite.py
--- old/testtools-1.1.0/testtools/testsuite.py  2014-08-25 03:43:46.000000000 
+0200
+++ new/testtools-1.5.0/testtools/testsuite.py  2014-11-15 21:44:58.000000000 
+0100
@@ -14,6 +14,7 @@
 import sys
 import threading
 import unittest
+import unittest2
 
 from extras import safe_hasattr, try_imports
 
@@ -34,7 +35,7 @@
                 yield subtest
 
 
-class ConcurrentTestSuite(unittest.TestSuite):
+class ConcurrentTestSuite(unittest2.TestSuite):
     """A TestSuite whose run() calls out to a concurrency strategy."""
 
     def __init__(self, suite, make_tests, wrap_result=None):
@@ -197,7 +198,7 @@
             process_result.stopTestRun()
 
 
-class FixtureSuite(unittest.TestSuite):
+class FixtureSuite(unittest2.TestSuite):
 
     def __init__(self, fixture, tests):
         super(FixtureSuite, self).__init__(tests)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools.egg-info/PKG-INFO 
new/testtools-1.5.0/testtools.egg-info/PKG-INFO
--- old/testtools-1.1.0/testtools.egg-info/PKG-INFO     2014-09-14 
00:54:43.000000000 +0200
+++ new/testtools-1.5.0/testtools.egg-info/PKG-INFO     2014-11-21 
00:20:56.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: testtools
-Version: 1.1.0
+Version: 1.5.0
 Summary: Extensions to the Python standard library unit testing framework
 Home-page: https://github.com/testing-cabal/testtools
 Author: Jonathan M. Lange
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/testtools-1.1.0/testtools.egg-info/requires.txt 
new/testtools-1.5.0/testtools.egg-info/requires.txt
--- old/testtools-1.1.0/testtools.egg-info/requires.txt 2014-09-14 
00:54:43.000000000 +0200
+++ new/testtools-1.5.0/testtools.egg-info/requires.txt 2014-11-21 
00:20:56.000000000 +0100
@@ -1,2 +1,3 @@
 extras
-python-mimeparse
\ No newline at end of file
+python-mimeparse
+unittest2>=0.8.0
\ No newline at end of file

-- 
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to