Rev 5090: (spiv) More robust fix for TestCase cloning, in file:///home/pqm/archives/thelove/bzr/2.2/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Thu Sep 16 08:12:33 BST 2010


At file:///home/pqm/archives/thelove/bzr/2.2/

------------------------------------------------------------
revno: 5090 [merge]
revision-id: pqm at pqm.ubuntu.com-20100916071232-2p0bokcnlnphk0aj
parent: pqm at pqm.ubuntu.com-20100915100907-icd1mnify8zwgiw8
parent: andrew.bennetts at canonical.com-20100915064455-blneq4o2m9akugv1
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: 2.2
timestamp: Thu 2010-09-16 08:12:32 +0100
message:
  (spiv) More robust fix for TestCase cloning,
   this time with tests. (Andrew Bennetts)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
=== modified file 'NEWS'
--- a/NEWS	2010-09-15 08:48:55 +0000
+++ b/NEWS	2010-09-16 07:12:32 +0000
@@ -75,10 +75,10 @@
 *******
 
 * Tracebacks from a parameterized test are no longer reported against every
-  parameterization of that test.  This was done by adding a simple
+  parameterization of that test.  This was done by adding a custom
   ``__copy__`` method to TestCase, so that ``multiply_tests`` no longer
-  causes testtools.TestCase instances to share a details dict.
-  (Andrew Bennetts)
+  causes testtools.TestCase instances to share a details dict (or other
+  mutable attributes).  (Andrew Bennetts, #625574)
 
 
 bzr 2.2

=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2010-09-14 02:23:34 +0000
+++ b/bzrlib/tests/__init__.py	2010-09-15 06:44:55 +0000
@@ -799,9 +799,17 @@
     _log_file_name = None
     # record lsprof data when performing benchmark calls.
     _gather_lsprof_in_benchmarks = False
+    # Mutable attributes defined in bzrlib.tests.TestCase and
+    # testtools.TestCase that should not be shallow copied by clone_test.
+    # This is ugly, but probably the best we can do until testtools provide a
+    # nicer way to deal with this, see
+    # <https://bugs.launchpad.net/testtools/+bug/637725>.
+    _attrs_to_deepcopy = (
+        '_cleanups', 'exception_handlers', '_unique_id_gen',
+        '_traceback_id_gen', '_TestCase__details',
+        '_TestCase__exception_handlers')
 
     def __init__(self, methodName='testMethod'):
-        self.__methodName = methodName
         super(TestCase, self).__init__(methodName)
         self._cleanups = []
         self._directory_isolation = True
@@ -811,7 +819,23 @@
             (TestNotApplicable, self._do_not_applicable))
 
     def __copy__(self):
-        return self.__class__(self.__methodName)
+        # XXX: This method works around the lack of a way to correctly clone a
+        # test with current testtools.  See
+        # <https://bugs.launchpad.net/testtools/+bug/637725>.
+        # The work around is to:
+        #  - shallow copy self.__dict__
+        #  - deep copy individual attributes in the _attrs_to_deepcopy black
+        #    list.
+        #  - create a new instance (skipping __init__) with the new __dict__.
+        attrs = self.__dict__.copy()
+        for attr_name in self._attrs_to_deepcopy:
+            if attr_name in attrs:
+                attrs[attr_name] = copy.deepcopy(attrs[attr_name])
+        # Some Python voodoo to create an instance without invoking its
+        # __init__, because we've already constructed the __dict__ to use.
+        new_instance = self.__class__.__new__(self.__class__)
+        new_instance.__dict__ = attrs
+        return new_instance
 
     def setUp(self):
         super(TestCase, self).setUp()

=== modified file 'bzrlib/tests/test_selftest.py'
--- a/bzrlib/tests/test_selftest.py	2010-09-14 07:12:47 +0000
+++ b/bzrlib/tests/test_selftest.py	2010-09-16 07:12:32 +0000
@@ -26,6 +26,7 @@
 import warnings
 
 from testtools import MultiTestResult
+from testtools.content import Content
 from testtools.content_type import ContentType
 from testtools.matchers import (
     DocTestMatches,
@@ -42,7 +43,6 @@
     lockdir,
     memorytree,
     osutils,
-    progress,
     remote,
     repository,
     symbol_versioning,
@@ -1655,6 +1655,40 @@
         self.assertEqual('original', obj.test_attr)
 
 
+class TestTestCloning(tests.TestCase):
+    """Tests that test cloning of TestCases (as used by multiply_tests)."""
+
+    def test_cloned_testcase_does_not_share_details(self):
+        """A TestCase cloned with clone_test does not share mutable attributes
+        such as details or cleanups.
+        """
+        class Test(tests.TestCase):
+            def test_foo(self):
+                self.addDetail('foo', Content('text/plain', lambda: 'foo'))
+        orig_test = Test('test_foo')
+        cloned_test = tests.clone_test(orig_test, orig_test.id() + '(cloned)')
+        orig_test.run(unittest.TestResult())
+        self.assertEqual('foo', orig_test.getDetails()['foo'].iter_bytes())
+        self.assertEqual(None, cloned_test.getDetails().get('foo'))
+
+    def test_double_apply_scenario_preserves_first_scenario(self):
+        """Applying two levels of scenarios to a test preserves the attributes
+        added by both scenarios.
+        """
+        class Test(tests.TestCase):
+            def test_foo(self):
+                pass
+        test = Test('test_foo')
+        scenarios_x = [('x=1', {'x': 1}), ('x=2', {'x': 2})]
+        scenarios_y = [('y=1', {'y': 1}), ('y=2', {'y': 2})]
+        suite = tests.multiply_tests(test, scenarios_x, unittest.TestSuite())
+        suite = tests.multiply_tests(suite, scenarios_y, unittest.TestSuite())
+        all_tests = list(tests.iter_suite_tests(suite))
+        self.assertLength(4, all_tests)
+        all_xys = sorted((t.x, t.y) for t in all_tests)
+        self.assertEqual([(1, 1), (1, 2), (2, 1), (2, 2)], all_xys)
+
+
 # NB: Don't delete this; it's not actually from 0.11!
 @deprecated_function(deprecated_in((0, 11, 0)))
 def sample_deprecated_function():




More information about the bazaar-commits mailing list