Hello community, here is the log from the commit of package python3-testtools for openSUSE:Factory checked in at 2015-03-19 21:12:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-testtools (Old) and /work/SRC/openSUSE:Factory/.python3-testtools.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python3-testtools" Changes: -------- --- /work/SRC/openSUSE:Factory/python3-testtools/python3-testtools.changes 2015-02-18 12:08:54.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python3-testtools.new/python3-testtools.changes 2015-03-19 21:12:33.000000000 +0100 @@ -1,0 +2,35 @@ +Wed Mar 11 05:36:08 UTC 2015 - [email protected] + +- specfile: + * added traceback2 requirement + +- update to version 1.7.1: + * Building a wheel on Python 3 was missing "_compat2x.py" needed + for Python2. This was a side effect of the fix to bug #941958, + where we fixed a cosmetic error. (Robert Collins, #1430534) + * During reporting in "TextTestResult" now always uses "ceil" + rather than depending on the undefined rounding behaviour in + string formatting. (Robert Collins) + +- changes from version 1.7.0: + * Empty attachments to tests were triggering a file payload of None + in the "ExtendedToStreamDecorator" code, which caused multiple + copies of attachments that had been output prior to the empty one. + (Robert Collins, #1378609) + +- changes from version 1.6.1: + * Fix installing when "extras" is not already installed. Our guards + for the absence of unittest2 were not sufficient. (Robert + Collins, #1430076) + +- changes from version 1.6.0: + * "testtools.run" now accepts "--locals" to show local variables in + tracebacks, which can be a significant aid in debugging. In doing + so we've removed the code reimplementing linecache and traceback + by using the new traceback2 and linecache2 packages. (Robert + Collins, github #111) + * "testtools" now depends on "unittest2" 1.0.0 which brings in a + dependency on "traceback2" and via it "linecache2". (Robert + Collins) + +------------------------------------------------------------------- Old: ---- testtools-1.5.0.tar.gz New: ---- testtools-1.7.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-testtools.spec ++++++ --- /var/tmp/diff_new_pack.Shp5tS/_old 2015-03-19 21:12:34.000000000 +0100 +++ /var/tmp/diff_new_pack.Shp5tS/_new 2015-03-19 21:12:34.000000000 +0100 @@ -20,7 +20,7 @@ # enable testing with a build conditional (off by default): Name: python3-testtools -Version: 1.5.0 +Version: 1.7.1 Release: 0 Summary: Extensions to the Python Standard Library Unit Testing Framework License: MIT @@ -34,10 +34,12 @@ # Test requirements: BuildRequires: python3-extras BuildRequires: python3-python-mimeparse +BuildRequires: python3-traceback2 BuildRequires: python3-unittest2 -Requires: python3-unittest2 Requires: python3-extras Requires: python3-python-mimeparse +Requires: python3-traceback2 +Requires: python3-unittest2 BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildArch: noarch ++++++ testtools-1.5.0.tar.gz -> testtools-1.7.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/NEWS new/testtools-1.7.1/NEWS --- old/testtools-1.5.0/NEWS 2014-11-21 00:20:13.000000000 +0100 +++ new/testtools-1.7.1/NEWS 2015-03-11 02:17:37.000000000 +0100 @@ -7,6 +7,59 @@ NEXT ~~~~ +1.7.1 +~~~~~ + +Improvements +------------ + +* Building a wheel on Python 3 was missing ``_compat2x.py`` needed for Python2. + This was a side effect of the fix to bug #941958, where we fixed a cosmetic + error. (Robert Collins, #1430534) + +* During reporting in ``TextTestResult`` now always uses ``ceil`` rather than + depending on the undefined rounding behaviour in string formatting. + (Robert Collins) + +1.7.0 +~~~~~ + +Improvements +------------ + +* Empty attachments to tests were triggering a file payload of None in the + ``ExtendedToStreamDecorator`` code, which caused multiple copies of + attachments that had been output prior to the empty one. + (Robert Collins, #1378609) + +1.6.1 +~~~~~ + +Changes +------- + +* Fix installing when ``extras`` is not already installed. Our guards + for the absence of unittest2 were not sufficient. + (Robert Collins, #1430076) + +1.6.0 +~~~~~ + +Improvements +------------ + +* ``testtools.run`` now accepts ``--locals`` to show local variables + in tracebacks, which can be a significant aid in debugging. In doing + so we've removed the code reimplementing linecache and traceback by + using the new traceback2 and linecache2 packages. + (Robert Collins, github #111) + +Changes +------- + +* ``testtools`` now depends on ``unittest2`` 1.0.0 which brings in a dependency + on ``traceback2`` and via it ``linecache2``. (Robert Collins) + 1.5.0 ~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/PKG-INFO new/testtools-1.7.1/PKG-INFO --- old/testtools-1.5.0/PKG-INFO 2014-11-21 00:20:56.000000000 +0100 +++ new/testtools-1.7.1/PKG-INFO 2015-03-11 02:18:22.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: testtools -Version: 1.5.0 +Version: 1.7.1 Summary: Extensions to the Python standard library unit testing framework Home-page: https://github.com/testing-cabal/testtools Author: Jonathan M. Lange Files old/testtools-1.5.0/doc/.hacking.rst.swp and new/testtools-1.7.1/doc/.hacking.rst.swp differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/doc/for-framework-folk.rst new/testtools-1.7.1/doc/for-framework-folk.rst --- old/testtools-1.5.0/doc/for-framework-folk.rst 2014-09-12 00:21:19.000000000 +0200 +++ new/testtools-1.7.1/doc/for-framework-folk.rst 2015-03-10 00:19:47.000000000 +0100 @@ -92,6 +92,13 @@ This is useful when you want to cause a test to fail, but don't want to prevent the remainder of the test code from being executed. +Exception formatting +-------------------- + +Testtools ``TestCase`` instances format their own exceptions. The attribute +``__testtools_tb_locals__`` controls whether to include local variables in the +formatted exceptions. + Test placeholders ================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/doc/for-framework-folk.rst~ new/testtools-1.7.1/doc/for-framework-folk.rst~ --- old/testtools-1.5.0/doc/for-framework-folk.rst~ 2014-09-02 02:16:59.000000000 +0200 +++ new/testtools-1.7.1/doc/for-framework-folk.rst~ 2015-03-09 10:26:26.000000000 +0100 @@ -50,9 +50,9 @@ change everything about how the test executes. To work with ``testtools.TestCase``, a ``RunTest`` must have a factory that -takes a test and an optional list of exception handlers. Instances returned -by the factory must have a ``run()`` method that takes an optional ``TestResult`` -object. +takes a test and an optional list of exception handlers and an optional +last_resort handler. Instances returned by the factory must have a ``run()`` +method that takes an optional ``TestResult`` object. The default is ``testtools.runtest.RunTest``, which calls ``setUp``, the test method, ``tearDown`` and clean ups (see :ref:`addCleanup`) in the normal, vanilla @@ -92,6 +92,13 @@ This is useful when you want to cause a test to fail, but don't want to prevent the remainder of the test code from being executed. +Exception formatting +-------------------- + +Testtools ``TestCase`` do their own formatting of exceptions. The attribute +``__testtools_tb_locals__`` controls whether to include local variables in the +formatted exceptions. + Test placeholders ================= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/doc/for-test-authors.rst new/testtools-1.7.1/doc/for-test-authors.rst --- old/testtools-1.5.0/doc/for-test-authors.rst 2014-08-28 11:34:09.000000000 +0200 +++ new/testtools-1.7.1/doc/for-test-authors.rst 2015-03-10 00:19:47.000000000 +0100 @@ -72,10 +72,12 @@ $ python -m testtools.run discover packagecontainingtests -For more information see the Python 2.7 unittest documentation, or:: +For more information see the Python unittest documentation, and:: python -m testtools.run --help +which describes the options available to ``testtools.run``. + As your testing needs grow and evolve, you will probably want to use a more sophisticated test runner. There are many of these for Python, and almost all of them will happily run testtools tests. In particular: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/doc/for-test-authors.rst~ new/testtools-1.7.1/doc/for-test-authors.rst~ --- old/testtools-1.5.0/doc/for-test-authors.rst~ 2013-11-28 08:54:45.000000000 +0100 +++ new/testtools-1.7.1/doc/for-test-authors.rst~ 2014-08-28 11:34:09.000000000 +0200 @@ -11,7 +11,7 @@ If you are a test author of an unusually large or unusually unusual test suite, you might be interested in :doc:`for-framework-folk`. -You might also be interested in the `testtools API docs`_. +You might also be interested in the :doc:`testtools API docs </api>`. Introduction @@ -288,6 +288,60 @@ self.assertNotEqual(result, 50) +``assert_that`` Function +------------------------ + +In addition to ``self.assertThat``, testtools also provides the ``assert_that`` +function in ``testtools.assertions`` This behaves like the method version does:: + + class TestSquare(TestCase): + + def test_square(): + result = square(7) + assert_that(result, Equals(49)) + + def test_square_silly(): + result = square(7) + assert_that(result, Not(Equals(50))) + + +Delayed Assertions +~~~~~~~~~~~~~~~~~~ + +A failure in the ``self.assertThat`` method will immediately fail the test: No +more test code will be run after the assertion failure. + +The ``expectThat`` method behaves the same as ``assertThat`` with one +exception: when failing the test it does so at the end of the test code rather +than when the mismatch is detected. For example:: + + import subprocess + + from testtools import TestCase + from testtools.matchers import Equals + + + class SomeProcessTests(TestCase): + + def test_process_output(self): + process = subprocess.Popen( + ["my-app", "/some/path"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + + stdout, stderrr = process.communicate() + + self.expectThat(process.returncode, Equals(0)) + self.expectThat(stdout, Equals("Expected Output")) + self.expectThat(stderr, Equals("")) + +In this example, should the ``expectThat`` call fail, the failure will be +recorded in the test result, but the test will continue as normal. If all +three assertions fail, the test result will have three failures recorded, and +the failure details for each failed assertion will be attached to the test +result. + Stock matchers -------------- @@ -408,7 +462,7 @@ except RuntimeError: exc_info = sys.exc_info() self.assertThat(exc_info, MatchesException(RuntimeError)) - self.assertThat(exc_info, MatchesException(RuntimeError('bar')) + self.assertThat(exc_info, MatchesException(RuntimeError('bar'))) Most of the time, you will want to uses `The raises helper`_ instead. @@ -598,7 +652,7 @@ def test_annotate_example(self): result = 43 self.assertThat( - result, Annotate("Not the answer to the Question!", Equals(42)) + result, Annotate("Not the answer to the Question!", Equals(42))) Since the annotation is only ever displayed when there is a mismatch (e.g. when ``result`` does not equal 42), it's a good idea to phrase the note @@ -811,7 +865,7 @@ divisible, '{0} is not divisible by {1}') self.assertThat(7, IsDivisibleBy(1)) self.assertThat(7, IsDivisibleBy(7)) - self.assertThat(7, IsDivisibleBy(2))) + self.assertThat(7, IsDivisibleBy(2)) # This will fail. Which will produce the error message:: @@ -1246,7 +1300,9 @@ force_failure ------------- -Setting the ``testtools.TestCase.force_failure`` instance variable to ``True`` will cause the test to be marked as a failure, but won't stop the test code from running (see :ref:`force_failure`). +Setting the ``testtools.TestCase.force_failure`` instance variable to ``True`` +will cause the test to be marked as a failure, but won't stop the test code +from running (see :ref:`force_failure`). Test helpers @@ -1335,14 +1391,14 @@ id and can be used when filtering tests by id. (e.g. via ``--load-list``):: from testtools.testcase import attr, WithAttributes - + class AnnotatedTests(WithAttributes, TestCase): @attr('simple') def test_one(self): pass - - @attr('more', 'than', 'one) + + @attr('more', 'than', 'one') def test_two(self): pass @@ -1424,7 +1480,6 @@ .. _doctest: http://docs.python.org/library/doctest.html .. _Deferred: http://twistedmatrix.com/documents/current/core/howto/defer.html .. _discover: http://pypi.python.org/pypi/discover -.. _`testtools API docs`: http://mumak.net/testtools/apidocs/ .. _Distutils: http://docs.python.org/library/distutils.html .. _`setup configuration`: http://docs.python.org/distutils/configfile.html .. _broken: http://chipaca.com/post/3210673069/hasattr-17-less-harmful diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/doc/hacking.rst~ new/testtools-1.7.1/doc/hacking.rst~ --- old/testtools-1.5.0/doc/hacking.rst~ 2014-08-29 03:45:37.000000000 +0200 +++ new/testtools-1.7.1/doc/hacking.rst~ 2014-08-31 12:10:39.000000000 +0200 @@ -130,6 +130,18 @@ Changes which all users should be made aware of should be documented in NEWS. +We are now in full backwards compatibility mode - no more releases < 1.0.0, and +breaking compatibility will require consensus on the testtools-dev mailing list. +Exactly what constitutes a backwards incompatible change is vague, but coarsely: + +* adding required arguments or required calls to something that used to work +* removing keyword or position arguments, removing methods, functions or modules +* changing behaviour someone may have reasonably depended on + +Some things are not compatibility issues: + +* changes to _ prefixed methods, functions, modules, packages. + NEWS management --------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/setup.py new/testtools-1.7.1/setup.py --- old/testtools-1.5.0/setup.py 2014-11-15 22:33:08.000000000 +0100 +++ new/testtools-1.7.1/setup.py 2015-03-10 22:19:19.000000000 +0100 @@ -2,10 +2,8 @@ """Distutils installer for testtools.""" from setuptools import setup -from distutils.command.build_py import build_py import email import os -import sys import testtools cmd_class = {} @@ -13,14 +11,6 @@ cmd_class['test'] = testtools.TestCommand -class testtools_build_py(build_py): - def build_module(self, module, module_file, package): - if sys.version_info >= (3,) and module == '_compat2x': - return - return build_py.build_module(self, module, module_file, package) -cmd_class['build_py'] = testtools_build_py - - def get_version_from_pkg_info(): """Get the version from PKG-INFO file if we can.""" pkg_info_path = os.path.join(os.path.dirname(__file__), 'PKG-INFO') @@ -63,7 +53,8 @@ # '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', + 'unittest2>=1.0.0', + 'traceback2', ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/__init__.py new/testtools-1.7.1/testtools/__init__.py --- old/testtools-1.5.0/testtools/__init__.py 2014-11-21 00:20:00.000000000 +0100 +++ new/testtools-1.7.1/testtools/__init__.py 2015-03-11 02:17:24.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, 5, 0, 'final', 0) +__version__ = (1, 7, 1, 'final', 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/compat.py new/testtools-1.7.1/testtools/compat.py --- old/testtools-1.5.0/testtools/compat.py 2014-11-15 10:08:40.000000000 +0100 +++ new/testtools-1.7.1/testtools/compat.py 2015-03-10 22:19:19.000000000 +0100 @@ -18,7 +18,6 @@ import codecs import io -import linecache import locale import os import re @@ -26,14 +25,16 @@ import traceback import unicodedata -from extras import try_imports +from extras import try_import, try_imports BytesIO = try_imports(['StringIO.StringIO', 'io.BytesIO']) StringIO = try_imports(['StringIO.StringIO', 'io.StringIO']) +# To let setup.py work, make this a conditional import. +linecache = try_import('linecache2') try: from testtools import _compat2x as _compat -except (SyntaxError, ImportError): +except SyntaxError: from testtools import _compat3x as _compat reraise = _compat.reraise @@ -209,61 +210,6 @@ except AttributeError: pass return writer(stream, "replace") - - -# The default source encoding is actually "iso-8859-1" until Python 2.5 but -# using non-ascii causes a deprecation warning in 2.4 and it's cleaner to -# treat all versions the same way -_default_source_encoding = "ascii" - -# Pattern specified in <http://www.python.org/dev/peps/pep-0263/> -_cookie_search=re.compile("coding[:=]\s*([-\w.]+)").search - -def _detect_encoding(lines): - """Get the encoding of a Python source file from a list of lines as bytes - - This function does less than tokenize.detect_encoding added in Python 3 as - it does not attempt to raise a SyntaxError when the interpreter would, it - just wants the encoding of a source file Python has already compiled and - determined is valid. - """ - if not lines: - return _default_source_encoding - if lines[0].startswith("\xef\xbb\xbf"): - # Source starting with UTF-8 BOM is either UTF-8 or a SyntaxError - return "utf-8" - # Only the first two lines of the source file are examined - magic = _cookie_search("".join(lines[:2])) - if magic is None: - return _default_source_encoding - encoding = magic.group(1) - try: - codecs.lookup(encoding) - except LookupError: - # Some codecs raise something other than LookupError if they don't - # support the given error handler, but not the text ones that could - # actually be used for Python source code - return _default_source_encoding - return encoding - - -class _EncodingTuple(tuple): - """A tuple type that can have an encoding attribute smuggled on""" - - -def _get_source_encoding(filename): - """Detect, cache and return the encoding of Python source at filename""" - try: - return linecache.cache[filename].encoding - except (AttributeError, KeyError): - encoding = _detect_encoding(linecache.getlines(filename)) - if filename in linecache.cache: - newtuple = _EncodingTuple(linecache.cache[filename]) - newtuple.encoding = encoding - linecache.cache[filename] = newtuple - return encoding - - def _get_exception_encoding(): """Return the encoding we expect messages from the OS to be encoded in""" if os.name == "nt": @@ -276,110 +222,3 @@ return locale.getlocale(locale.LC_MESSAGES)[1] or "ascii" -def _exception_to_text(evalue): - """Try hard to get a sensible text value out of an exception instance""" - try: - return unicode(evalue) - except KeyboardInterrupt: - raise - except: - # Apparently this is what traceback._some_str does. Sigh - RBC 20100623 - pass - try: - return str(evalue).decode(_get_exception_encoding(), "replace") - except KeyboardInterrupt: - raise - except: - # Apparently this is what traceback._some_str does. Sigh - RBC 20100623 - pass - # Okay, out of ideas, let higher level handle it - return None - - -def _format_stack_list(stack_lines): - """Format 'stack_lines' and return a list of unicode strings. - - :param stack_lines: A list of filename, lineno, name, and line variables, - probably obtained by calling traceback.extract_tb or - traceback.extract_stack. - """ - fs_enc = sys.getfilesystemencoding() - extracted_list = [] - for filename, lineno, name, line in stack_lines: - extracted_list.append(( - filename.decode(fs_enc, "replace"), - lineno, - name.decode("ascii", "replace"), - line and line.decode( - _get_source_encoding(filename), "replace"))) - return traceback.format_list(extracted_list) - - -def _format_exception_only(eclass, evalue): - """Format the excption part of a traceback. - - :param eclass: The type of the exception being formatted. - :param evalue: The exception instance. - :returns: A list of unicode strings. - """ - list = [] - if evalue is None: - # Is a (deprecated) string exception - list.append((eclass + "\n").decode("ascii", "replace")) - return list - if isinstance(evalue, SyntaxError): - # Avoid duplicating the special formatting for SyntaxError here, - # instead create a new instance with unicode filename and line - # Potentially gives duff spacing, but that's a pre-existing issue - try: - msg, (filename, lineno, offset, line) = evalue - except (TypeError, ValueError): - pass # Strange exception instance, fall through to generic code - else: - # Errors during parsing give the line from buffer encoded as - # latin-1 or utf-8 or the encoding of the file depending on the - # coding and whether the patch for issue #1031213 is applied, so - # give up on trying to decode it and just read the file again - if line: - bytestr = linecache.getline(filename, lineno) - if bytestr: - if lineno == 1 and bytestr.startswith("\xef\xbb\xbf"): - bytestr = bytestr[3:] - line = bytestr.decode( - _get_source_encoding(filename), "replace") - del linecache.cache[filename] - else: - line = line.decode("ascii", "replace") - if filename: - fs_enc = sys.getfilesystemencoding() - filename = filename.decode(fs_enc, "replace") - evalue = eclass(msg, (filename, lineno, offset, line)) - list.extend(traceback.format_exception_only(eclass, evalue)) - return list - sclass = eclass.__name__ - svalue = _exception_to_text(evalue) - if svalue: - list.append("%s: %s\n" % (sclass, svalue)) - elif svalue is None: - # GZ 2010-05-24: Not a great fallback message, but keep for the moment - list.append(_u("%s: <unprintable %s object>\n" % (sclass, sclass))) - else: - list.append(_u("%s\n" % sclass)) - return list - - -_TB_HEADER = _u('Traceback (most recent call last):\n') - - -def _format_exc_info(eclass, evalue, tb, limit=None): - """Format a stack trace and the exception information as unicode - - Compatibility function for Python 2 which ensures each component of a - traceback is correctly decoded according to its origins. - - Based on traceback.format_exception and related functions. - """ - return [_TB_HEADER] \ - + _format_stack_list(traceback.extract_tb(tb, limit)) \ - + _format_exception_only(eclass, evalue) - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/content.py new/testtools-1.7.1/testtools/content.py --- old/testtools-1.5.0/testtools/content.py 2014-10-20 04:16:48.000000000 +0200 +++ new/testtools-1.7.1/testtools/content.py 2015-03-10 00:19:47.000000000 +0100 @@ -17,15 +17,13 @@ import json import os import sys -import traceback from extras import try_import +# To let setup.py work, make this a conditional import. +traceback = try_import('traceback2') from testtools.compat import ( _b, - _format_exception_only, - _format_stack_list, - _TB_HEADER, _u, istext, str_is_unicode, @@ -163,61 +161,52 @@ def _stack_lines_to_unicode(self, stack_lines): """Converts a list of pre-processed stack lines into a unicode string. """ + msg_lines = traceback.format_list(stack_lines) + return _u('').join(msg_lines) - # testtools customization. When str is unicode (e.g. IronPython, - # Python 3), traceback.format_exception returns unicode. For Python 2, - # it returns bytes. We need to guarantee unicode. - if str_is_unicode: - format_stack_lines = traceback.format_list - else: - format_stack_lines = _format_stack_list - msg_lines = format_stack_lines(stack_lines) - - return ''.join(msg_lines) - - -def TracebackContent(err, test): +class TracebackContent(Content): """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 provide room for other languages to format their tracebacks differently. """ - if err is None: - raise ValueError("err may not be None") - exctype, value, tb = err - # Skip test runner traceback levels - if StackLinesContent.HIDE_INTERNAL_STACK: - while tb and '__unittest' in tb.tb_frame.f_globals: - tb = tb.tb_next - - # testtools customization. When str is unicode (e.g. IronPython, - # Python 3), traceback.format_exception_only returns unicode. For Python 2, - # it returns bytes. We need to guarantee unicode. - if str_is_unicode: - format_exception_only = traceback.format_exception_only - else: - format_exception_only = _format_exception_only - - limit = None - # Disabled due to https://bugs.launchpad.net/testtools/+bug/1188420 - if (False - and StackLinesContent.HIDE_INTERNAL_STACK - and test.failureException - and isinstance(value, test.failureException)): - # Skip assert*() traceback levels - limit = 0 - while tb and not self._is_relevant_tb_level(tb): - limit += 1 - tb = tb.tb_next - - prefix = _TB_HEADER - stack_lines = traceback.extract_tb(tb, limit) - postfix = ''.join(format_exception_only(exctype, value)) + def __init__(self, err, test, capture_locals=False): + """Create a TracebackContent for ``err``. - return StackLinesContent(stack_lines, prefix, postfix) + :param err: An exc_info error tuple. + :param test: A test object used to obtain failureException. + :param capture_locals: If true, show locals in the traceback. + """ + if err is None: + raise ValueError("err may not be None") + + exctype, value, tb = err + # Skip test runner traceback levels + if StackLinesContent.HIDE_INTERNAL_STACK: + while tb and '__unittest' in tb.tb_frame.f_globals: + tb = tb.tb_next + + limit = None + # Disabled due to https://bugs.launchpad.net/testtools/+bug/1188420 + if (False + and StackLinesContent.HIDE_INTERNAL_STACK + and test.failureException + and isinstance(value, test.failureException)): + # Skip assert*() traceback levels + limit = 0 + while tb and not self._is_relevant_tb_level(tb): + limit += 1 + tb = tb.tb_next + + stack_lines = list(traceback.TracebackException(exctype, value, tb, + limit=limit, capture_locals=capture_locals).format()) + content_type = ContentType('text', 'x-traceback', + {"language": "python", "charset": "utf8"}) + super(TracebackContent, self).__init__( + content_type, lambda: [x.encode('utf8') for x in stack_lines]) def StacktraceContent(prefix_content="", postfix_content=""): @@ -232,22 +221,20 @@ :param prefix_content: A unicode string to add before the stack lines. :param postfix_content: A unicode string to add after the stack lines. """ - stack = inspect.stack()[1:] - - if StackLinesContent.HIDE_INTERNAL_STACK: - limit = 1 - while limit < len(stack) and '__unittest' not in stack[limit][0].f_globals: - limit += 1 - else: - limit = -1 - - frames_only = [line[0] for line in stack[:limit]] - processed_stack = [ ] - for frame in reversed(frames_only): - filename, line, function, context, _ = inspect.getframeinfo(frame) - context = ''.join(context) - processed_stack.append((filename, line, function, context)) - return StackLinesContent(processed_stack, prefix_content, postfix_content) + stack = traceback.walk_stack(None) + def filter_stack(stack): + # Discard the filter_stack frame. + next(stack) + # Discard the StacktraceContent frame. + next(stack) + for f, f_lineno in stack: + if StackLinesContent.HIDE_INTERNAL_STACK: + if '__unittest' in f.f_globals: + return + yield f, f_lineno + extract = traceback.StackSummary.extract(filter_stack(stack)) + extract.reverse() + return StackLinesContent(extract, prefix_content, postfix_content) def json_content(json_data): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/run.py new/testtools-1.7.1/testtools/run.py --- old/testtools-1.5.0/testtools/run.py 2014-11-18 22:55:29.000000000 +0100 +++ new/testtools-1.7.1/testtools/run.py 2015-03-10 01:17:43.000000000 +0100 @@ -11,10 +11,11 @@ import argparse from functools import partial import os.path -import unittest2 as unittest import sys -from extras import safe_hasattr +from extras import safe_hasattr, try_imports +# To let setup.py work, make this a conditional import. +unittest = try_imports(['unittest2', 'unittest']) from testtools import TextTestResult, testcase from testtools.compat import classtypes, istext, unicode_output_stream @@ -24,7 +25,10 @@ defaultTestLoader = unittest.defaultTestLoader defaultTestLoaderCls = unittest.TestLoader have_discover = True -discover_impl = unittest.loader +# This shouldn't really be public - its legacy. Try to set it if we can, and +# if we can't (during installs before unittest2 is installed) just stub it out +# to None. +discover_impl = getattr(unittest, 'loader', None) # Kept for API compatibility, but no longer used. BUFFEROUTPUT = "" @@ -67,18 +71,20 @@ """ A thunk object to support unittest.TestProgram.""" def __init__(self, verbosity=None, failfast=None, buffer=None, - stdout=None): + stdout=None, tb_locals=False, **kwargs): """Create a TestToolsTestRunner. :param verbosity: Ignored. :param failfast: Stop running tests at the first failure. :param buffer: Ignored. :param stdout: Stream to use for stdout. + :param tb_locals: If True include local variables in tracebacks. """ self.failfast = failfast if stdout is None: stdout = sys.stdout self.stdout = stdout + self.tb_locals = tb_locals def list(self, test, loader): """List the tests that would be run if test() was run.""" @@ -94,7 +100,8 @@ def run(self, test): "Run the given test case or test suite." result = TextTestResult( - unicode_output_stream(self.stdout), failfast=self.failfast) + unicode_output_stream(self.stdout), failfast=self.failfast, + tb_locals=self.tb_locals) result.startTestRun() try: return test.run(result) @@ -127,7 +134,7 @@ def __init__(self, module=__name__, defaultTest=None, argv=None, testRunner=None, testLoader=defaultTestLoader, exit=True, verbosity=1, failfast=None, catchbreak=None, - buffer=None, stdout=None): + buffer=None, stdout=None, tb_locals=False): if module == __name__: self.module = None elif istext(module): @@ -147,6 +154,7 @@ self.catchbreak = catchbreak self.verbosity = verbosity self.buffer = buffer + self.tb_locals = tb_locals self.defaultTest = defaultTest # XXX: Local edit (see http://bugs.python.org/issue22860) self.listtests = False @@ -219,10 +227,18 @@ if self.testRunner is None: self.testRunner = TestToolsTestRunner try: - testRunner = self.testRunner(verbosity=self.verbosity, - failfast=self.failfast, - buffer=self.buffer, - stdout=self.stdout) + try: + testRunner = self.testRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + stdout=self.stdout, + tb_locals=self.tb_locals) + except TypeError: + # didn't accept the tb_locals parameter + testRunner = self.testRunner(verbosity=self.verbosity, + failfast=self.failfast, + buffer=self.buffer, + stdout=self.stdout) except TypeError: # didn't accept the verbosity, buffer, failfast or stdout arguments # Try with the prior contract diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/runtest.py new/testtools-1.7.1/testtools/runtest.py --- old/testtools-1.5.0/testtools/runtest.py 2014-09-02 13:20:23.000000000 +0200 +++ new/testtools-1.7.1/testtools/runtest.py 2015-03-10 00:19:47.000000000 +0100 @@ -103,6 +103,8 @@ self.result = result try: self._exceptions = [] + self.case.__testtools_tb_locals__ = getattr( + result, 'tb_locals', False) self._run_core() if self._exceptions: # One or more caught exceptions, now trigger the test's diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/testcase.py new/testtools-1.7.1/testtools/testcase.py --- old/testtools-1.5.0/testtools/testcase.py 2014-11-17 19:40:37.000000000 +0100 +++ new/testtools-1.7.1/testtools/testcase.py 2015-03-10 00:19:47.000000000 +0100 @@ -24,8 +24,10 @@ from extras import ( safe_hasattr, try_import, + try_imports, ) -import unittest2 as unittest +# To let setup.py work, make this a conditional import. +unittest = try_imports(['unittest2', 'unittest']) from testtools import ( content, @@ -585,7 +587,9 @@ tb_label = '%s-%d' % (tb_label, tb_id) if tb_label not in self.getDetails(): break - self.addDetail(tb_label, content.TracebackContent(exc_info, self)) + self.addDetail(tb_label, content.TracebackContent( + exc_info, self, capture_locals=getattr( + self, '__testtools_tb_locals__', False))) @staticmethod def _report_unexpected_success(self, result, err): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/testresult/real.py new/testtools-1.7.1/testtools/testresult/real.py --- old/testtools-1.5.0/testtools/testresult/real.py 2014-09-03 06:08:31.000000000 +0200 +++ new/testtools-1.7.1/testtools/testresult/real.py 2015-03-11 01:03:38.000000000 +0100 @@ -23,6 +23,7 @@ ] import datetime +import math from operator import methodcaller import sys import unittest @@ -80,12 +81,13 @@ :ivar skip_reasons: A dict of skip-reasons -> list of tests. See addSkip. """ - def __init__(self, failfast=False): + def __init__(self, failfast=False, tb_locals=False): # startTestRun resets all attributes, and older clients don't know to # call startTestRun, so it is called once here. # Because subclasses may reasonably not expect this, we call the # specific version we want to run. self.failfast = failfast + self.tb_locals = tb_locals TestResult.startTestRun(self) def addExpectedFailure(self, test, err=None, details=None): @@ -174,7 +176,8 @@ def _err_details_to_string(self, test, err=None, details=None): """Convert an error in exc_info form or a contents dict to a string.""" if err is not None: - return TracebackContent(err, test).as_text() + return TracebackContent( + err, test, capture_locals=self.tb_locals).as_text() return _details_to_str(details, special='traceback') def _exc_info_to_unicode(self, err, test): @@ -201,8 +204,9 @@ pristine condition ready for use in another test run. Note that this is different from Python 2.7's startTestRun, which does nothing. """ - # failfast is reset by the super __init__, so stash it. + # failfast and tb_locals are reset by the super __init__, so save them. failfast = self.failfast + tb_locals = self.tb_locals super(TestResult, self).__init__() self.skip_reasons = {} self.__now = None @@ -212,6 +216,8 @@ self.unexpectedSuccesses = [] self.failfast = failfast # -- End: As per python 2.7 -- + # -- Python 3.5 + self.tb_locals = tb_locals def stopTestRun(self): """Called after a test run completes @@ -875,16 +881,22 @@ class TextTestResult(TestResult): """A TestResult which outputs activity to a text stream.""" - def __init__(self, stream, failfast=False): + def __init__(self, stream, failfast=False, tb_locals=False): """Construct a TextTestResult writing to stream.""" - super(TextTestResult, self).__init__(failfast=failfast) + super(TextTestResult, self).__init__( + failfast=failfast, tb_locals=tb_locals) self.stream = stream self.sep1 = '=' * 70 + '\n' self.sep2 = '-' * 70 + '\n' - def _delta_to_float(self, a_timedelta): - return (a_timedelta.days * 86400.0 + a_timedelta.seconds + - a_timedelta.microseconds / 1000000.0) + def _delta_to_float(self, a_timedelta, precision): + # This calls ceiling to ensure that the most pessimistic view of time + # taken is shown (rather than leaving it to the Python %f operator + # to decide whether to round/floor/ceiling. This was added when we + # had pyp3 test failures that suggest a floor was happening. + shift = 10 ** precision + return math.ceil((a_timedelta.days * 86400.0 + a_timedelta.seconds + + a_timedelta.microseconds / 1000000.0) * shift) / shift def _show_list(self, label, error_list): for test, output in error_list: @@ -912,7 +924,7 @@ self.sep1, test.id(), self.sep2)) self.stream.write("\nRan %d test%s in %.3fs\n" % (self.testsRun, plural, - self._delta_to_float(stop - self.__start))) + self._delta_to_float(stop - self.__start, 3))) if self.wasSuccessful(): self.stream.write("OK\n") else: @@ -1340,6 +1352,8 @@ self.status(file_name=name, file_bytes=file_bytes, mime_type=mime_type, test_id=test_id, timestamp=now) file_bytes = next_bytes + if file_bytes is None: + file_bytes = _b("") self.status(file_name=name, file_bytes=file_bytes, eof=True, mime_type=mime_type, test_id=test_id, timestamp=now) if reason is not None: @@ -1642,7 +1656,8 @@ def _err_to_details(self, test, err, details): if details: return details - return {'traceback': TracebackContent(err, test)} + return {'traceback': TracebackContent( + err, test, capture_locals=self.tb_locals)} def addSuccess(self, test, details=None): super(TestByTestResult, self).addSuccess(test) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_compat.py new/testtools-1.7.1/testtools/tests/test_compat.py --- old/testtools-1.5.0/testtools/tests/test_compat.py 2013-11-29 23:52:10.000000000 +0100 +++ new/testtools-1.7.1/testtools/tests/test_compat.py 2015-03-10 00:19:47.000000000 +0100 @@ -3,7 +3,7 @@ """Tests for miscellaneous compatibility functions""" import io -import linecache +import linecache2 as linecache import os import sys import tempfile @@ -13,11 +13,6 @@ from testtools.compat import ( _b, - _detect_encoding, - _format_exc_info, - _format_exception_only, - _format_stack_list, - _get_source_encoding, _u, reraise, str_is_unicode, @@ -34,161 +29,6 @@ ) -class TestDetectEncoding(testtools.TestCase): - """Test detection of Python source encodings""" - - def _check_encoding(self, expected, lines, possibly_invalid=False): - """Check lines are valid Python and encoding is as expected""" - if not possibly_invalid: - compile(_b("".join(lines)), "<str>", "exec") - encoding = _detect_encoding(lines) - self.assertEqual(expected, encoding, - "Encoding %r expected but got %r from lines %r" % - (expected, encoding, lines)) - - def test_examples_from_pep(self): - """Check the examples given in PEP 263 all work as specified - - See 'Examples' section of <http://www.python.org/dev/peps/pep-0263/> - """ - # With interpreter binary and using Emacs style file encoding comment: - self._check_encoding("latin-1", ( - "#!/usr/bin/python\n", - "# -*- coding: latin-1 -*-\n", - "import os, sys\n")) - self._check_encoding("iso-8859-15", ( - "#!/usr/bin/python\n", - "# -*- coding: iso-8859-15 -*-\n", - "import os, sys\n")) - self._check_encoding("ascii", ( - "#!/usr/bin/python\n", - "# -*- coding: ascii -*-\n", - "import os, sys\n")) - # Without interpreter line, using plain text: - self._check_encoding("utf-8", ( - "# This Python file uses the following encoding: utf-8\n", - "import os, sys\n")) - # Text editors might have different ways of defining the file's - # encoding, e.g. - self._check_encoding("latin-1", ( - "#!/usr/local/bin/python\n", - "# coding: latin-1\n", - "import os, sys\n")) - # Without encoding comment, Python's parser will assume ASCII text: - self._check_encoding("ascii", ( - "#!/usr/local/bin/python\n", - "import os, sys\n")) - # Encoding comments which don't work: - # Missing "coding:" prefix: - self._check_encoding("ascii", ( - "#!/usr/local/bin/python\n", - "# latin-1\n", - "import os, sys\n")) - # Encoding comment not on line 1 or 2: - self._check_encoding("ascii", ( - "#!/usr/local/bin/python\n", - "#\n", - "# -*- coding: latin-1 -*-\n", - "import os, sys\n")) - # Unsupported encoding: - self._check_encoding("ascii", ( - "#!/usr/local/bin/python\n", - "# -*- coding: utf-42 -*-\n", - "import os, sys\n"), - possibly_invalid=True) - - def test_bom(self): - """Test the UTF-8 BOM counts as an encoding declaration""" - self._check_encoding("utf-8", ( - "\xef\xbb\xbfimport sys\n", - )) - self._check_encoding("utf-8", ( - "\xef\xbb\xbf# File encoding: utf-8\n", - )) - self._check_encoding("utf-8", ( - '\xef\xbb\xbf"""Module docstring\n', - '\xef\xbb\xbfThat should just be a ZWNB"""\n')) - self._check_encoding("latin-1", ( - '"""Is this coding: latin-1 or coding: utf-8 instead?\n', - '\xef\xbb\xbfThose should be latin-1 bytes"""\n')) - self._check_encoding("utf-8", ( - "\xef\xbb\xbf# Is the coding: utf-8 or coding: euc-jp instead?\n", - '"""Module docstring say \xe2\x98\x86"""\n'), - possibly_invalid=True) - - def test_multiple_coding_comments(self): - """Test only the first of multiple coding declarations counts""" - self._check_encoding("iso-8859-1", ( - "# Is the coding: iso-8859-1\n", - "# Or is it coding: iso-8859-2\n"), - possibly_invalid=True) - self._check_encoding("iso-8859-1", ( - "#!/usr/bin/python\n", - "# Is the coding: iso-8859-1\n", - "# Or is it coding: iso-8859-2\n")) - self._check_encoding("iso-8859-1", ( - "# Is the coding: iso-8859-1 or coding: iso-8859-2\n", - "# Or coding: iso-8859-3 or coding: iso-8859-4\n"), - possibly_invalid=True) - self._check_encoding("iso-8859-2", ( - "# Is the coding iso-8859-1 or coding: iso-8859-2\n", - "# Spot the missing colon above\n")) - - -class TestGetSourceEncoding(testtools.TestCase): - """Test reading and caching the encodings of source files""" - - def setUp(self): - testtools.TestCase.setUp(self) - dir = tempfile.mkdtemp() - self.addCleanup(os.rmdir, dir) - self.filename = os.path.join(dir, self.id().rsplit(".", 1)[1] + ".py") - self._written = False - - def put_source(self, text): - f = open(self.filename, "w") - try: - f.write(text) - finally: - f.close() - if not self._written: - self._written = True - self.addCleanup(os.remove, self.filename) - self.addCleanup(linecache.cache.pop, self.filename, None) - - def test_nonexistant_file_as_ascii(self): - """When file can't be found, the encoding should default to ascii""" - self.assertEquals("ascii", _get_source_encoding(self.filename)) - - def test_encoding_is_cached(self): - """The encoding should stay the same if the cache isn't invalidated""" - self.put_source( - "# coding: iso-8859-13\n" - "import os\n") - self.assertEquals("iso-8859-13", _get_source_encoding(self.filename)) - self.put_source( - "# coding: rot-13\n" - "vzcbeg bf\n") - self.assertEquals("iso-8859-13", _get_source_encoding(self.filename)) - - def test_traceback_rechecks_encoding(self): - """A traceback function checks the cache and resets the encoding""" - self.put_source( - "# coding: iso-8859-8\n" - "import os\n") - self.assertEquals("iso-8859-8", _get_source_encoding(self.filename)) - self.put_source( - "# coding: utf-8\n" - "import os\n") - try: - exec (compile("raise RuntimeError\n", self.filename, "exec")) - except RuntimeError: - traceback.extract_tb(sys.exc_info()[2]) - else: - self.fail("RuntimeError not raised") - self.assertEquals("utf-8", _get_source_encoding(self.filename)) - - class _FakeOutputStream(object): """A simple file-like object for testing""" @@ -453,151 +293,6 @@ self.assertRaises(CustomException, reraise, *_exc_info) -class Python2CompatibilityTests(testtools.TestCase): - - def setUp(self): - super(Python2CompatibilityTests, self).setUp() - if sys.version[0] >= '3': - self.skip("These tests are only applicable to python 2.") - - -class TestExceptionFormatting(Python2CompatibilityTests): - """Test the _format_exception_only function.""" - - def _assert_exception_format(self, eclass, evalue, expected): - actual = _format_exception_only(eclass, evalue) - self.assertThat(actual, Equals(expected)) - self.assertThat(''.join(actual), IsInstance(unicode)) - - def test_supports_string_exception(self): - self._assert_exception_format( - "String_Exception", - None, - [_u("String_Exception\n")] - ) - - def test_supports_regular_exception(self): - self._assert_exception_format( - RuntimeError, - RuntimeError("Something went wrong"), - [_u("RuntimeError: Something went wrong\n")] - ) - - def test_supports_unprintable_exceptions(self): - """Verify support for exception classes that raise an exception when - __unicode__ or __str__ is called. - """ - class UnprintableException(Exception): - - def __str__(self): - raise Exception() - - def __unicode__(self): - raise Exception() - - self._assert_exception_format( - UnprintableException, - UnprintableException("Foo"), - [_u("UnprintableException: <unprintable UnprintableException object>\n")] - ) - - def test_supports_exceptions_with_no_string_value(self): - class NoStringException(Exception): - - def __str__(self): - return "" - - def __unicode__(self): - return _u("") - - self._assert_exception_format( - NoStringException, - NoStringException("Foo"), - [_u("NoStringException\n")] - ) - - def test_supports_strange_syntax_error(self): - """Test support for syntax errors with unusual number of arguments""" - self._assert_exception_format( - SyntaxError, - SyntaxError("Message"), - [_u("SyntaxError: Message\n")] - ) - - def test_supports_syntax_error(self): - self._assert_exception_format( - SyntaxError, - SyntaxError( - "Some Syntax Message", - ( - "/path/to/file", - 12, - 2, - "This is the line of code", - ) - ), - [ - _u(' File "/path/to/file", line 12\n'), - _u(' This is the line of code\n'), - _u(' ^\n'), - _u('SyntaxError: Some Syntax Message\n'), - ] - ) - - -class StackListFormattingTests(Python2CompatibilityTests): - """Test the _format_stack_list function.""" - - def _assert_stack_format(self, stack_lines, expected_output): - actual = _format_stack_list(stack_lines) - self.assertThat(actual, Equals([expected_output])) - - def test_single_complete_stack_line(self): - stack_lines = [( - '/path/to/filename', - 12, - 'func_name', - 'some_code()', - )] - expected = \ - _u(' File "/path/to/filename", line 12, in func_name\n' \ - ' some_code()\n') - - self._assert_stack_format(stack_lines, expected) - - def test_single_stack_line_no_code(self): - stack_lines = [( - '/path/to/filename', - 12, - 'func_name', - None - )] - expected = _u(' File "/path/to/filename", line 12, in func_name\n') - self._assert_stack_format(stack_lines, expected) - - -class FormatExceptionInfoTests(Python2CompatibilityTests): - - def test_individual_functions_called(self): - self.patch( - testtools.compat, - '_format_stack_list', - lambda stack_list: [_u("format stack list called\n")] - ) - self.patch( - testtools.compat, - '_format_exception_only', - lambda etype, evalue: [_u("format exception only called\n")] - ) - result = _format_exc_info(None, None, None) - expected = [ - _u("Traceback (most recent call last):\n"), - _u("format stack list called\n"), - _u("format exception only called\n"), - ] - self.assertThat(expected, Equals(result)) - - def test_suite(): from unittest import TestLoader return TestLoader().loadTestsFromName(__name__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_content.py new/testtools-1.7.1/testtools/tests/test_content.py --- old/testtools-1.5.0/testtools/tests/test_content.py 2014-10-20 04:16:48.000000000 +0200 +++ new/testtools-1.7.1/testtools/tests/test_content.py 2015-03-10 00:19:47.000000000 +0100 @@ -278,24 +278,20 @@ content = StacktraceContent() content_type = ContentType("text", "x-traceback", {"language": "python", "charset": "utf8"}) - self.assertEqual(content_type, content.content_type) def test_prefix_is_used(self): prefix = self.getUniqueString() actual = StacktraceContent(prefix_content=prefix).as_text() - self.assertTrue(actual.startswith(prefix)) def test_postfix_is_used(self): postfix = self.getUniqueString() actual = StacktraceContent(postfix_content=postfix).as_text() - self.assertTrue(actual.endswith(postfix)) def test_top_frame_is_skipped_when_no_stack_is_specified(self): actual = StacktraceContent().as_text() - self.assertTrue('testtools/content.py' not in actual) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_run.py new/testtools-1.7.1/testtools/tests/test_run.py --- old/testtools-1.5.0/testtools/tests/test_run.py 2014-11-18 22:59:06.000000000 +0100 +++ new/testtools-1.7.1/testtools/tests/test_run.py 2015-03-10 00:19:47.000000000 +0100 @@ -307,6 +307,18 @@ self.assertThat( stdout.getDetails()['stdout'].as_text(), Contains('Ran 1 test')) + def test_run_locals(self): + stdout = self.useFixture(fixtures.StringStream('stdout')) + + class Failing(TestCase): + def test_a(self): + a = 1 + self.fail('a') + runner = run.TestToolsTestRunner(tb_locals=True, stdout=stdout.stream) + runner.run(Failing('test_a')) + self.assertThat( + stdout.getDetails()['stdout'].as_text(), Contains('a = 1')) + def test_stdout_honoured(self): self.useFixture(SampleTestFixture()) tests = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_testresult.py new/testtools-1.7.1/testtools/tests/test_testresult.py --- old/testtools-1.5.0/testtools/tests/test_testresult.py 2014-10-20 04:16:48.000000000 +0200 +++ new/testtools-1.7.1/testtools/tests/test_testresult.py 2015-03-11 00:48:49.000000000 +0100 @@ -95,6 +95,7 @@ def make_erroring_test(): class Test(TestCase): def error(self): + a = 1 1/0 return Test("error") @@ -847,6 +848,53 @@ result.stopTestRun() self.assertEqual(False, result.wasSuccessful()) + def test_empty_detail_status_correct(self): + log = LoggingStreamResult() + result = ExtendedToStreamDecorator(log) + result.startTestRun() + now = datetime.datetime.now(utc) + result.time(now) + result.startTest(self) + result.addError(self, details={'foo': text_content(_u(""))}) + result.stopTest(self) + result.stopTestRun() + self.assertEqual([ + ('startTestRun',), + ('status', + 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct', + 'inprogress', + None, + True, + None, + None, + False, + None, + None, + now), + ('status', + 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct', + None, + None, + True, + 'foo', + _b(''), + True, + 'text/plain; charset="utf8"', + None, + now), + ('status', + 'testtools.tests.test_testresult.TestExtendedToStreamDecorator.test_empty_detail_status_correct', + 'fail', + set(), + True, + None, + None, + False, + None, + None, + now), + ('stopTestRun',)], log._events) + class TestStreamFailFast(TestCase): @@ -1163,6 +1211,32 @@ self.assertEqual( TracebackContent(exc_info, test).as_text(), text_traceback) + def test_traceback_with_locals(self): + result = self.makeResult() + result.tb_locals = True + test = make_erroring_test() + test.run(result) + self.assertThat( + result.errors[0][1], + DocTestMatches( + 'Traceback (most recent call last):\n' + ' File "...testtools...runtest.py", line ..., in _run_user\n' + ' return fn(*args, **kwargs)\n' + ' args = ...\n' + ' fn = ...\n' + ' kwargs = ...\n' + ' self = ...\n' + ' File "...testtools...testcase.py", line ..., in _run_test_method\n' + ' return self._get_test_method()()\n' + ' result = ...\n' + ' self = ...\n' + ' File "...testtools...tests...test_testresult.py", line ..., in error\n' + ' 1/0\n' + ' a = 1\n' + ' self = ...\n' + 'ZeroDivisionError: ...\n', + doctest.ELLIPSIS | doctest.REPORT_UDIFF)) + class TestMultiTestResult(TestCase): """Tests for 'MultiTestResult'.""" @@ -2445,8 +2519,7 @@ if str_is_unicode: output_text = example_text else: - output_text = example_text.encode("shift_jis").decode( - _get_exception_encoding(), "replace") + output_text = "b%r" % example_text.encode("shift_jis") self.assertIn(self._as_output("AssertionError: %s" % output_text), textoutput) @@ -2515,19 +2588,6 @@ textoutput = self._test_external_case("raise SyntaxError(3, 2, 1)") self.assertIn(self._as_output("\nSyntaxError: "), textoutput) - def test_syntax_error_import_binary(self): - """Importing a binary file shouldn't break SyntaxError formatting""" - self._setup_external_case("import bad") - f = open(os.path.join(self.dir, "bad.py"), "wb") - try: - f.write(_b("x\x9c\xcb*\xcd\xcb\x06\x00\x04R\x01\xb9")) - finally: - f.close() - textoutput = self._run_external_case() - matches_error = MatchesAny( - Contains('\nTypeError: '), Contains('\nSyntaxError: ')) - self.assertThat(textoutput, matches_error) - def test_syntax_error_line_iso_8859_1(self): """Syntax error on a latin-1 line shows the line decoded""" text, raw = self._get_sample_text("iso-8859-1") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/tests/test_testsuite.py new/testtools-1.7.1/testtools/tests/test_testsuite.py --- old/testtools-1.5.0/testtools/tests/test_testsuite.py 2014-11-17 19:40:37.000000000 +0100 +++ new/testtools-1.7.1/testtools/tests/test_testsuite.py 2015-03-10 00:19:47.000000000 +0100 @@ -179,17 +179,32 @@ suite.run(result) events = result._events # Check the traceback loosely. - self.assertThat(events[1][6].decode('utf8'), DocTestMatches("""\ -Traceback (most recent call last): + self.assertEqual(events[1][6].decode('utf8'), + "Traceback (most recent call last):\n") + self.assertThat(events[2][6].decode('utf8'), DocTestMatches("""\ File "...testtools/testsuite.py", line ..., in _run_test test.run(process_result) +""", doctest.ELLIPSIS)) + self.assertThat(events[3][6].decode('utf8'), DocTestMatches("""\ TypeError: run() takes ...1 ...argument...2...given... """, doctest.ELLIPSIS)) events = [event[0:10] + (None,) for event in events] events[1] = events[1][:6] + (None,) + events[1][7:] + events[2] = events[2][:6] + (None,) + events[2][7:] + events[3] = events[3][:6] + (None,) + events[3][7:] self.assertEqual([ ('status', "broken-runner-'0'", 'inprogress', None, True, None, None, False, None, _u('0'), None), ('status', "broken-runner-'0'", None, None, True, 'traceback', None, + False, + 'text/x-traceback; charset="utf8"; language="python"', + '0', + None), + ('status', "broken-runner-'0'", None, None, True, 'traceback', None, + False, + 'text/x-traceback; charset="utf8"; language="python"', + '0', + None), + ('status', "broken-runner-'0'", None, None, True, 'traceback', None, True, 'text/x-traceback; charset="utf8"; language="python"', '0', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools/testsuite.py new/testtools-1.7.1/testtools/testsuite.py --- old/testtools-1.5.0/testtools/testsuite.py 2014-11-15 21:44:58.000000000 +0100 +++ new/testtools-1.7.1/testtools/testsuite.py 2015-03-10 00:19:47.000000000 +0100 @@ -14,10 +14,10 @@ import sys import threading import unittest -import unittest2 from extras import safe_hasattr, try_imports - +# This is just to let setup.py work, as testtools is imported in setup.py. +unittest2 = try_imports(['unittest2', 'unittest']) Queue = try_imports(['Queue.Queue', 'queue.Queue']) import testtools diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/testtools-1.5.0/testtools.egg-info/PKG-INFO new/testtools-1.7.1/testtools.egg-info/PKG-INFO --- old/testtools-1.5.0/testtools.egg-info/PKG-INFO 2014-11-21 00:20:56.000000000 +0100 +++ new/testtools-1.7.1/testtools.egg-info/PKG-INFO 2015-03-11 02:18:21.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: testtools -Version: 1.5.0 +Version: 1.7.1 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.5.0/testtools.egg-info/requires.txt new/testtools-1.7.1/testtools.egg-info/requires.txt --- old/testtools-1.5.0/testtools.egg-info/requires.txt 2014-11-21 00:20:56.000000000 +0100 +++ new/testtools-1.7.1/testtools.egg-info/requires.txt 2015-03-11 02:18:21.000000000 +0100 @@ -1,3 +1,4 @@ extras python-mimeparse -unittest2>=0.8.0 \ No newline at end of file +unittest2>=1.0.0 +traceback2 \ No newline at end of file -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
