Rev 5470: (spiv) Fix BZR_TEST_PDB. (#504070) (Martin [gz]) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Fri Oct 8 02:43:14 BST 2010


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 5470 [merge]
revision-id: pqm at pqm.ubuntu.com-20101008014313-6653cym75zyalkxr
parent: pqm at pqm.ubuntu.com-20101007151857-njgnlwp629nb09kr
parent: gzlist at googlemail.com-20101004073659-eptpvu6oipvyjonf
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Fri 2010-10-08 02:43:13 +0100
message:
  (spiv) Fix BZR_TEST_PDB. (#504070) (Martin [gz])
modified:
  bzrlib/tests/__init__.py       selftest.py-20050531073622-8d0e3c8845c97a64
  bzrlib/tests/test_selftest.py  test_selftest.py-20051202044319-c110a115d8c0456a
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py	2010-10-07 12:10:24 +0000
+++ b/bzrlib/tests/__init__.py	2010-10-08 01:43:13 +0000
@@ -195,6 +195,7 @@
         self._strict = strict
         self._first_thread_leaker_id = None
         self._tests_leaking_threads_count = 0
+        self._traceback_from_test = None
 
     def stopTestRun(self):
         run = self.testsRun
@@ -281,6 +282,14 @@
         what = re.sub(r'^bzrlib\.tests\.', '', what)
         return what
 
+    # GZ 2010-10-04: Cloned tests may end up harmlessly calling this method
+    #                multiple times in a row, because the handler is added for
+    #                each test but the container list is shared between cases.
+    #                See lp:498869 lp:625574 and lp:637725 for background.
+    def _record_traceback_from_test(self, exc_info):
+        """Store the traceback from passed exc_info tuple till"""
+        self._traceback_from_test = exc_info[2]
+
     def startTest(self, test):
         super(ExtendedTestResult, self).startTest(test)
         if self.count == 0:
@@ -289,6 +298,10 @@
         self.report_test_start(test)
         test.number = self.count
         self._recordTestStartTime()
+        # Make testtools cases give us the real traceback on failure
+        addOnException = getattr(test, "addOnException", None)
+        if addOnException is not None:
+            addOnException(self._record_traceback_from_test)
         # Only check for thread leaks if the test case supports cleanups
         addCleanup = getattr(test, "addCleanup", None)
         if addCleanup is not None:
@@ -298,6 +311,9 @@
         self.report_tests_starting()
         self._active_threads = threading.enumerate()
 
+    def stopTest(self, test):
+        self._traceback_from_test = None
+
     def _check_leaked_threads(self, test):
         """See if any threads have leaked since last call
 
@@ -324,7 +340,7 @@
         Called from the TestCase run() method when the test
         fails with an unexpected error.
         """
-        self._post_mortem()
+        self._post_mortem(self._traceback_from_test)
         super(ExtendedTestResult, self).addError(test, err)
         self.error_count += 1
         self.report_error(test, err)
@@ -337,7 +353,7 @@
         Called from the TestCase run() method when the test
         fails because e.g. an assert() method failed.
         """
-        self._post_mortem()
+        self._post_mortem(self._traceback_from_test)
         super(ExtendedTestResult, self).addFailure(test, err)
         self.failure_count += 1
         self.report_failure(test, err)
@@ -385,10 +401,11 @@
         self.not_applicable_count += 1
         self.report_not_applicable(test, reason)
 
-    def _post_mortem(self):
+    def _post_mortem(self, tb=None):
         """Start a PDB post mortem session."""
         if os.environ.get('BZR_TEST_PDB', None):
-            import pdb;pdb.post_mortem()
+            import pdb
+            pdb.post_mortem(tb)
 
     def progress(self, offset, whence):
         """The test is adjusting the count of tests to run."""

=== modified file 'bzrlib/tests/test_selftest.py'
--- a/bzrlib/tests/test_selftest.py	2010-10-07 12:10:24 +0000
+++ b/bzrlib/tests/test_selftest.py	2010-10-08 01:43:13 +0000
@@ -3316,6 +3316,77 @@
         self.assertContainsString(result.stream.getvalue(), "leaking threads")
 
 
+class TestPostMortemDebugging(tests.TestCase):
+    """Check post mortem debugging works when tests fail or error"""
+
+    class TracebackRecordingResult(tests.ExtendedTestResult):
+        def __init__(self):
+            tests.ExtendedTestResult.__init__(self, StringIO(), 0, 1)
+            self.postcode = None
+        def _post_mortem(self, tb=None):
+            """Record the code object at the end of the current traceback"""
+            tb = tb or sys.exc_info()[2]
+            if tb is not None:
+                next = tb.tb_next
+                while next is not None:
+                    tb = next
+                    next = next.tb_next
+                self.postcode = tb.tb_frame.f_code
+        def report_error(self, test, err):
+            pass
+        def report_failure(self, test, err):
+            pass
+
+    def test_location_unittest_error(self):
+        """Needs right post mortem traceback with erroring unittest case"""
+        class Test(unittest.TestCase):
+            def runTest(self):
+                raise RuntimeError
+        result = self.TracebackRecordingResult()
+        Test().run(result)
+        self.assertEqual(result.postcode, Test.runTest.func_code)
+
+    def test_location_unittest_failure(self):
+        """Needs right post mortem traceback with failing unittest case"""
+        class Test(unittest.TestCase):
+            def runTest(self):
+                raise self.failureException
+        result = self.TracebackRecordingResult()
+        Test().run(result)
+        self.assertEqual(result.postcode, Test.runTest.func_code)
+
+    def test_location_bt_error(self):
+        """Needs right post mortem traceback with erroring bzrlib.tests case"""
+        class Test(tests.TestCase):
+            def test_error(self):
+                raise RuntimeError
+        result = self.TracebackRecordingResult()
+        Test("test_error").run(result)
+        self.assertEqual(result.postcode, Test.test_error.func_code)
+
+    def test_location_bt_failure(self):
+        """Needs right post mortem traceback with failing bzrlib.tests case"""
+        class Test(tests.TestCase):
+            def test_failure(self):
+                raise self.failureException
+        result = self.TracebackRecordingResult()
+        Test("test_failure").run(result)
+        self.assertEqual(result.postcode, Test.test_failure.func_code)
+
+    def test_env_var_triggers_post_mortem(self):
+        """Check pdb.post_mortem is called iff BZR_TEST_PDB is set"""
+        import pdb
+        result = tests.ExtendedTestResult(StringIO(), 0, 1)
+        post_mortem_calls = []
+        self.overrideAttr(pdb, "post_mortem", post_mortem_calls.append)
+        self.addCleanup(osutils.set_or_unset_env, "BZR_TEST_PDB",
+            osutils.set_or_unset_env("BZR_TEST_PDB", None))
+        result._post_mortem(1)
+        os.environ["BZR_TEST_PDB"] = "on"
+        result._post_mortem(2)
+        self.assertEqual([2], post_mortem_calls)
+
+
 class TestRunSuite(tests.TestCase):
 
     def test_runner_class(self):




More information about the bazaar-commits mailing list