cpython icon indicating copy to clipboard operation
cpython copied to clipboard

gh-62965: Add debugging support to unittest (--debug --pm --pdb --trace)

Open kxrob opened this issue 3 years ago • 2 comments

Note: The --debug mode (break run with original exception for seamless external post-mortem handling - e.g. for IDEs, interactive, super-runners ...) was originally inspired by #23900 . Some test code was cherry-picked. Yet the implementation here became completely different, as the primitive TestCase.debug() method cannot really provide compatible test control flow consistent with modern features.

The other debugging modes (with in-line callback way of execution) were inspired by the original discussion in #62965 and pytest.

  • Issue: gh-62965

kxrob avatar Nov 06 '22 15:11 kxrob

at first glance this is likely good, i'll need to dive into the case or suite code to understand the postmortem hook stuff better. having some other eyeballs on this would be good as well.

gpshead avatar Nov 06 '22 17:11 gpshead

Test code I used to try things out:

import sys, traceback, gc
import unittest

class TC(unittest.TestCase):
    def clInstanceA(self):
        print("TC.clInstanceA", self)
    def clInstanceB(self):
        print("TC.clInstanceB", self)
        ##CLIB / 0
    def clInstanceC(self):
        print("TC.clInstanceC", self)
        ##CLIC / 0
    def setUp(self):
        print("TC.setUp", self)
        self.addCleanup(self.clInstanceA)
        self.addCleanup(self.clInstanceB)
        self.addCleanup(self.clInstanceC)
        ##0 / 0
    def tearDown(self):
        print("TC.tearDown", self)
        ##9 / 0
    @classmethod
    def clClass(self):
        print("TC.clClass", self)
        CLC / 0
    @classmethod
    def setUpClass(cls):
        print("TC.setUpClass", cls)
        cls.addClassCleanup(cls.clClass)
        ##breakpoint()
        ##SUC / 0
    @classmethod
    def tearDownClass(cls):
        print("TC.tearDownClass", cls)
        y = unittest.case._AutoDelRunner(lambda: print("-- delframe TDC"))
        TDC / 0
        print("TC.tearDownClassB", cls)

    def test_0(self):
        print("hello 0")
    def test_1(self):
        print("hello 1")
        self.assertTrue(0)
    def test_2(self):
        print("hello 2")
        2 / 0
    def test_3(self):
        print("hello 3")
        with self.subTest():
            3 / 0  # subTest
        print("hello 3B")

def tearDownModule():
        print("testdbg.tearDownModule")
        ##TDM / 0

x = unittest.case._AutoDelRunner(lambda: print("Del X"))

def clear_sysltb():
    print("clear_sysltb", sys.last_traceback, sys.last_value)
    sys.last_traceback = sys.last_value = sys.last_type = None
    ##gc.collect()
    print("clear_sysltb_EXIT", sys.last_traceback, sys.last_value)

if __name__ == "__main__":
    import atexit
    atexit.register(clear_sysltb)
    sys.stderr = sys.stdout  # to work synchronously w/o '-u' in piped run
    unittest.main()

kxrob avatar Nov 06 '22 18:11 kxrob