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