testtools: Import newer upstream revision.
authorJelmer Vernooij <jelmer@samba.org>
Sun, 19 Sep 2010 19:29:42 +0000 (12:29 -0700)
committerJelmer Vernooij <jelmer@samba.org>
Sun, 19 Sep 2010 19:34:55 +0000 (12:34 -0700)
This fixes (among other things) a warning during 'make test' on systems with Python 2.6.

lib/testtools/NEWS
lib/testtools/testtools/matchers.py
lib/testtools/testtools/testcase.py
lib/testtools/testtools/tests/test_testtools.py

index dc5e6df8f10c62d2025867a1743194f768d561f7..596df0d6a6b7ce13af84753d3c61f447d32c719b 100644 (file)
@@ -4,6 +4,15 @@ testtools NEWS
 NEXT
 ~~~~
 
+Improvements
+------------
+
+* Code duplication between assertEqual and the matcher Equals has been removed.
+
+* In normal circumstances, a TestCase will no longer share details with clones
+  of itself. (Andrew Bennetts, bug #637725)
+
+
 0.9.6
 ~~~~~
 
@@ -17,32 +26,32 @@ patches and TestCase.assertEqual gives slightly nicer errors.
 Improvements
 ------------
 
- * 'TestCase.assertEqual' now formats errors a little more nicely, in the
-   style of bzrlib.
+* 'TestCase.assertEqual' now formats errors a little more nicely, in the
+  style of bzrlib.
 
- * Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be
-   used to add results to a `TestResult`.
+* Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be
+  used to add results to a `TestResult`.
 
- * 'Mismatch' now takes optional description and details parameters, so
-   custom Matchers aren't compelled to make their own subclass.
+* 'Mismatch' now takes optional description and details parameters, so
+  custom Matchers aren't compelled to make their own subclass.
 
- * jml added a built-in UTF8_TEXT ContentType to make it slightly easier to
-   add details to test results. See bug #520044.
+* jml added a built-in UTF8_TEXT ContentType to make it slightly easier to
+  add details to test results. See bug #520044.
 
- * Fix a bug in our built-in matchers where assertThat would blow up if any
-   of them failed. All built-in mismatch objects now provide get_details().
+* Fix a bug in our built-in matchers where assertThat would blow up if any
+  of them failed. All built-in mismatch objects now provide get_details().
 
- * New 'Is' matcher, which lets you assert that a thing is identical to
-   another thing.
+* New 'Is' matcher, which lets you assert that a thing is identical to
+  another thing.
 
- * New 'LessThan' matcher which lets you assert that a thing is less than
-   another thing.
+* New 'LessThan' matcher which lets you assert that a thing is less than
+  another thing.
 
- * TestCase now has a 'patch()' method to make it easier to monkey-patching
-   objects in tests. See the manual for more information. Fixes bug #310770.
+* TestCase now has a 'patch()' method to make it easier to monkey-patching
+  objects in tests. See the manual for more information. Fixes bug #310770.
 
- * MultiTestResult methods now pass back return values from the results it
-   forwards to.
+* MultiTestResult methods now pass back return values from the results it
+  forwards to.
 
 0.9.5
 ~~~~~
index 6a4c82a2fe4f7cd32c4949ec8b421f4451a461ec..61b5bd74f95381bd6e7da2809ba77484067b787c 100644 (file)
@@ -25,6 +25,7 @@ __all__ = [
 
 import doctest
 import operator
+from pprint import pformat
 
 
 class Matcher(object):
@@ -178,6 +179,14 @@ class _BinaryMismatch(Mismatch):
         self.other = other
 
     def describe(self):
+        left = repr(self.expected)
+        right = repr(self.other)
+        if len(left) + len(right) > 70:
+            return "%s:\nreference = %s\nactual = %s\n" % (
+                self._mismatch_string, pformat(self.expected),
+                pformat(self.other))
+        else:
+            return "%s %s %s" % (left, self._mismatch_string,right)
         return "%r %s %r" % (self.expected, self._mismatch_string, self.other)
 
 
index 48eec71d41809d273ea4734c4b2227a864ca15e1..959c129691b040cd82b95008bccbb71b25a9cec6 100644 (file)
@@ -17,13 +17,16 @@ try:
 except ImportError:
     wraps = None
 import itertools
-from pprint import pformat
 import sys
 import types
 import unittest
 
 from testtools import content
 from testtools.compat import advance_iterator
+from testtools.matchers import (
+    Annotate,
+    Equals,
+    )
 from testtools.monkey import patch
 from testtools.runtest import RunTest
 from testtools.testresult import TestResult
@@ -81,7 +84,9 @@ class TestCase(unittest.TestCase):
         self._traceback_id_gen = itertools.count(0)
         self.__setup_called = False
         self.__teardown_called = False
-        self.__details = {}
+        # __details is lazy-initialized so that a constructed-but-not-run
+        # TestCase is safe to use with clone_test_with_new_id.
+        self.__details = None
         self.__RunTest = kwargs.get('runTest', RunTest)
         self.__exception_handlers = []
         self.exception_handlers = [
@@ -114,6 +119,8 @@ class TestCase(unittest.TestCase):
         :param content_object: The content object for this detail. See
             testtools.content for more detail.
         """
+        if self.__details is None:
+            self.__details = {}
         self.__details[name] = content_object
 
     def getDetails(self):
@@ -121,6 +128,8 @@ class TestCase(unittest.TestCase):
 
         For more details see pydoc testtools.TestResult.
         """
+        if self.__details is None:
+            self.__details = {}
         return self.__details
 
     def patch(self, obj, attribute, value):
@@ -230,18 +239,10 @@ class TestCase(unittest.TestCase):
         :param observed: The observed value.
         :param message: An optional message to include in the error.
         """
-        try:
-            return super(TestCase, self).assertEqual(expected, observed)
-        except self.failureException:
-            lines = []
-            if message:
-                lines.append(message)
-            lines.extend(
-                ["not equal:",
-                 "a = %s" % pformat(expected),
-                 "b = %s" % pformat(observed),
-                 ''])
-            self.fail('\n'.join(lines))
+        matcher = Equals(expected)
+        if message:
+            matcher = Annotate(message, matcher)
+        self.assertThat(observed, matcher)
 
     failUnlessEqual = assertEquals = assertEqual
 
index 9edc5a51764099749f6a68144fb25e64aa5a173b..5dfb3559905dda2f8fceff26c9060de347214651 100644 (file)
@@ -461,6 +461,15 @@ class TestAssertions(TestCase):
              'a = %s' % pformat(a),
              'b = %s' % pformat(b),
              ''])
+        expected_error = '\n'.join([
+            'Match failed. Matchee: "%r"' % b,
+            'Matcher: Annotate(%r, Equals(%r))' % (message, a),
+            'Difference: !=:',
+            'reference = %s' % pformat(a),
+            'actual = %s' % pformat(b),
+            ': ' + message,
+            ''
+            ])
         self.assertFails(expected_error, self.assertEqual, a, b, message)
         self.assertFails(expected_error, self.assertEquals, a, b, message)
         self.assertFails(expected_error, self.failUnlessEqual, a, b, message)
@@ -468,11 +477,12 @@ class TestAssertions(TestCase):
     def test_assertEqual_formatting_no_message(self):
         a = "cat"
         b = "dog"
-        expected_error = '\n'.join(
-            ['not equal:',
-             'a = %s' % pformat(a),
-             'b = %s' % pformat(b),
-             ''])
+        expected_error = '\n'.join([
+            'Match failed. Matchee: "dog"',
+            'Matcher: Equals(\'cat\')',
+            'Difference: \'cat\' != \'dog\'',
+            ''
+            ])
         self.assertFails(expected_error, self.assertEqual, a, b)
         self.assertFails(expected_error, self.assertEquals, a, b)
         self.assertFails(expected_error, self.failUnlessEqual, a, b)
@@ -760,6 +770,18 @@ class TestCloneTestWithNewId(TestCase):
         self.assertEqual(oldName, test.id(),
             "the original test instance should be unchanged.")
 
+    def test_cloned_testcase_does_not_share_details(self):
+        """A cloned TestCase does not share the details dict."""
+        class Test(TestCase):
+            def test_foo(self):
+                self.addDetail(
+                    'foo', content.Content('text/plain', lambda: 'foo'))
+        orig_test = Test('test_foo')
+        cloned_test = clone_test_with_new_id(orig_test, self.getUniqueString())
+        orig_test.run(unittest.TestResult())
+        self.assertEqual('foo', orig_test.getDetails()['foo'].iter_bytes())
+        self.assertEqual(None, cloned_test.getDetails().get('foo'))
+
 
 class TestDetailsProvided(TestWithDetails):