]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add a double-mock guard to the base test case
authorKevin Benton <blak111@gmail.com>
Fri, 26 Jun 2015 01:34:38 +0000 (18:34 -0700)
committerKevin Benton <kevinbenton@buttewifi.com>
Fri, 26 Jun 2015 20:18:51 +0000 (20:18 +0000)
Use mock to patch mock with a check to prevent multiple active
patches to the same target. Multiple patches to the same target
result in non-deterministic behavior when stopall() tries to
undo the patches.[1]

1. http://bugs.python.org/issue21239

Change-Id: I3dd3d561a0267d80f464c15d69a4258b0a5e8aba
Closes-Bug: #1468998

neutron/tests/base.py

index 31068a97755703cf1b0936da302e6d6e4a837cad..28391d5dbd8e18b9ea9b6dd1efb0f1903645f325 100644 (file)
@@ -154,6 +154,7 @@ class DietTestCase(testtools.TestCase):
         self.useFixture(fixtures.NestedTempfile())
         self.useFixture(fixtures.TempHomeDir())
 
+        self.setup_double_mock_guard()
         self.addCleanup(mock.patch.stopall)
 
         if bool_from_env('OS_STDOUT_CAPTURE'):
@@ -166,6 +167,34 @@ class DietTestCase(testtools.TestCase):
         self.addOnException(self.check_for_systemexit)
         self.orig_pid = os.getpid()
 
+    def setup_double_mock_guard(self):
+        # mock.patch.stopall() uses a set in python < 3.4 so patches may not
+        # be unwound in the same order they were applied. This can leak mocks
+        # and cause tests down the line to fail.
+        # More info: http://bugs.python.org/issue21239
+        #
+        # Use mock to patch mock.patch.start to check if a target has already
+        # been patched and fail if it has.
+        self.first_traceback = {}
+        orig_start = mock._patch.start
+
+        def new_start(mself):
+            mytarget = mself.getter()
+            myattr = mself.attribute
+            for patch in mself._active_patches:
+                if (mytarget, myattr) == (patch.target, patch.attribute):
+                    key = str((patch.target, patch.attribute))
+                    self.fail("mock.patch was setup on an already patched "
+                              "target %s.%s. Stop the original patch before "
+                              "starting a new one. Traceback of 1st patch: %s"
+                              % (mytarget, myattr,
+                                 ''.join(self.first_traceback.get(key, []))))
+            self.first_traceback[
+                str((mytarget, myattr))] = traceback.format_stack()[:-2]
+            return orig_start(mself)
+
+        mock.patch('mock._patch.start', new=new_start).start()
+
     def check_for_systemexit(self, exc_info):
         if isinstance(exc_info[1], SystemExit):
             if os.getpid() != self.orig_pid: