| 
									
										
										
										
											2023-11-19 01:39:26 -05:00
										 |  |  | """Test debugger, coverage 66%
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Try to make tests pass with draft bdbx, which may replace bdb in 3.13+. | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2016-06-10 18:19:21 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | from idlelib import debugger | 
					
						
							| 
									
										
										
										
											2023-11-19 15:20:38 +11:00
										 |  |  | from collections import namedtuple | 
					
						
							|  |  |  | from textwrap import dedent | 
					
						
							| 
									
										
										
										
											2016-06-10 18:19:21 -04:00
										 |  |  | from tkinter import Tk | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-19 15:20:38 +11:00
										 |  |  | from test.support import requires | 
					
						
							|  |  |  | import unittest | 
					
						
							|  |  |  | from unittest import mock | 
					
						
							|  |  |  | from unittest.mock import Mock, patch | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """A test python script for the debug tests.""" | 
					
						
							|  |  |  | TEST_CODE = dedent("""
 | 
					
						
							|  |  |  |     i = 1 | 
					
						
							|  |  |  |     i += 2 | 
					
						
							|  |  |  |     if i == 3: | 
					
						
							|  |  |  |        print(i) | 
					
						
							|  |  |  |     """)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MockFrame: | 
					
						
							|  |  |  |     "Minimal mock frame." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, code, lineno): | 
					
						
							|  |  |  |         self.f_code = code | 
					
						
							|  |  |  |         self.f_lineno = lineno | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class IdbTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         cls.gui = Mock() | 
					
						
							|  |  |  |         cls.idb = debugger.Idb(cls.gui) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Create test and code objects to simulate a debug session. | 
					
						
							|  |  |  |         code_obj = compile(TEST_CODE, 'idlelib/file.py', mode='exec') | 
					
						
							|  |  |  |         frame1 = MockFrame(code_obj, 1) | 
					
						
							|  |  |  |         frame1.f_back = None | 
					
						
							|  |  |  |         frame2 = MockFrame(code_obj, 2) | 
					
						
							|  |  |  |         frame2.f_back = frame1 | 
					
						
							|  |  |  |         cls.frame = frame2 | 
					
						
							|  |  |  |         cls.msg = 'file.py:2: <module>()' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init(self): | 
					
						
							| 
									
										
										
										
											2023-11-19 01:39:26 -05:00
										 |  |  |         self.assertIs(self.idb.gui, self.gui) | 
					
						
							|  |  |  |         # Won't test super call since two Bdbs are very different. | 
					
						
							| 
									
										
										
										
											2023-11-19 15:20:38 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_user_line(self): | 
					
						
							|  |  |  |         # Test that .user_line() creates a string message for a frame. | 
					
						
							|  |  |  |         self.gui.interaction = Mock() | 
					
						
							|  |  |  |         self.idb.user_line(self.frame) | 
					
						
							|  |  |  |         self.gui.interaction.assert_called_once_with(self.msg, self.frame) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_user_exception(self): | 
					
						
							|  |  |  |         # Test that .user_exception() creates a string message for a frame. | 
					
						
							|  |  |  |         exc_info = (type(ValueError), ValueError(), None) | 
					
						
							|  |  |  |         self.gui.interaction = Mock() | 
					
						
							|  |  |  |         self.idb.user_exception(self.frame, exc_info) | 
					
						
							|  |  |  |         self.gui.interaction.assert_called_once_with( | 
					
						
							|  |  |  |                 self.msg, self.frame, exc_info) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FunctionTest(unittest.TestCase): | 
					
						
							|  |  |  |     # Test module functions together. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_functions(self): | 
					
						
							|  |  |  |         rpc_obj = compile(TEST_CODE,'rpc.py', mode='exec') | 
					
						
							|  |  |  |         rpc_frame = MockFrame(rpc_obj, 2) | 
					
						
							|  |  |  |         rpc_frame.f_back = rpc_frame | 
					
						
							|  |  |  |         self.assertTrue(debugger._in_rpc_code(rpc_frame)) | 
					
						
							|  |  |  |         self.assertEqual(debugger._frame2message(rpc_frame), | 
					
						
							|  |  |  |                          'rpc.py:2: <module>()') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         code_obj = compile(TEST_CODE, 'idlelib/debugger.py', mode='exec') | 
					
						
							|  |  |  |         code_frame = MockFrame(code_obj, 1) | 
					
						
							|  |  |  |         code_frame.f_back = None | 
					
						
							|  |  |  |         self.assertFalse(debugger._in_rpc_code(code_frame)) | 
					
						
							|  |  |  |         self.assertEqual(debugger._frame2message(code_frame), | 
					
						
							|  |  |  |                          'debugger.py:1: <module>()') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         code_frame.f_back = code_frame | 
					
						
							|  |  |  |         self.assertFalse(debugger._in_rpc_code(code_frame)) | 
					
						
							|  |  |  |         code_frame.f_back = rpc_frame | 
					
						
							|  |  |  |         self.assertTrue(debugger._in_rpc_code(code_frame)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DebuggerTest(unittest.TestCase): | 
					
						
							|  |  |  |     "Tests for Debugger that do not need a real root." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         cls.pyshell = Mock() | 
					
						
							|  |  |  |         cls.pyshell.root = Mock() | 
					
						
							|  |  |  |         cls.idb = Mock() | 
					
						
							|  |  |  |         with patch.object(debugger.Debugger, 'make_gui'): | 
					
						
							|  |  |  |             cls.debugger = debugger.Debugger(cls.pyshell, cls.idb) | 
					
						
							|  |  |  |         cls.debugger.root = Mock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_cont(self): | 
					
						
							|  |  |  |         self.debugger.cont() | 
					
						
							|  |  |  |         self.idb.set_continue.assert_called_once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_step(self): | 
					
						
							|  |  |  |         self.debugger.step() | 
					
						
							|  |  |  |         self.idb.set_step.assert_called_once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_quit(self): | 
					
						
							|  |  |  |         self.debugger.quit() | 
					
						
							|  |  |  |         self.idb.set_quit.assert_called_once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_next(self): | 
					
						
							|  |  |  |         with patch.object(self.debugger, 'frame') as frame: | 
					
						
							|  |  |  |             self.debugger.next() | 
					
						
							|  |  |  |             self.idb.set_next.assert_called_once_with(frame) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_ret(self): | 
					
						
							|  |  |  |         with patch.object(self.debugger, 'frame') as frame: | 
					
						
							|  |  |  |             self.debugger.ret() | 
					
						
							|  |  |  |             self.idb.set_return.assert_called_once_with(frame) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_clear_breakpoint(self): | 
					
						
							|  |  |  |         self.debugger.clear_breakpoint('test.py', 4) | 
					
						
							|  |  |  |         self.idb.clear_break.assert_called_once_with('test.py', 4) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_clear_file_breaks(self): | 
					
						
							|  |  |  |         self.debugger.clear_file_breaks('test.py') | 
					
						
							|  |  |  |         self.idb.clear_all_file_breaks.assert_called_once_with('test.py') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_set_load_breakpoints(self): | 
					
						
							|  |  |  |         # Test the .load_breakpoints() method calls idb. | 
					
						
							|  |  |  |         FileIO = namedtuple('FileIO', 'filename') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class MockEditWindow(object): | 
					
						
							|  |  |  |             def __init__(self, fn, breakpoints): | 
					
						
							|  |  |  |                 self.io = FileIO(fn) | 
					
						
							|  |  |  |                 self.breakpoints = breakpoints | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.pyshell.flist = Mock() | 
					
						
							|  |  |  |         self.pyshell.flist.inversedict = ( | 
					
						
							|  |  |  |             MockEditWindow('test1.py', [4, 4]), | 
					
						
							|  |  |  |             MockEditWindow('test2.py', [13, 44, 45]), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         self.debugger.set_breakpoint('test0.py', 1) | 
					
						
							|  |  |  |         self.idb.set_break.assert_called_once_with('test0.py', 1) | 
					
						
							|  |  |  |         self.debugger.load_breakpoints()  # Call set_breakpoint 5 times. | 
					
						
							|  |  |  |         self.idb.set_break.assert_has_calls( | 
					
						
							|  |  |  |             [mock.call('test0.py', 1), | 
					
						
							|  |  |  |              mock.call('test1.py', 4), | 
					
						
							|  |  |  |              mock.call('test1.py', 4), | 
					
						
							|  |  |  |              mock.call('test2.py', 13), | 
					
						
							|  |  |  |              mock.call('test2.py', 44), | 
					
						
							|  |  |  |              mock.call('test2.py', 45)]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_sync_source_line(self): | 
					
						
							|  |  |  |         # Test that .sync_source_line() will set the flist.gotofileline with fixed frame. | 
					
						
							|  |  |  |         test_code = compile(TEST_CODE, 'test_sync.py', 'exec') | 
					
						
							|  |  |  |         test_frame = MockFrame(test_code, 1) | 
					
						
							|  |  |  |         self.debugger.frame = test_frame | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.debugger.flist = Mock() | 
					
						
							|  |  |  |         with patch('idlelib.debugger.os.path.exists', return_value=True): | 
					
						
							|  |  |  |             self.debugger.sync_source_line() | 
					
						
							|  |  |  |         self.debugger.flist.gotofileline.assert_called_once_with('test_sync.py', 1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class DebuggerGuiTest(unittest.TestCase): | 
					
						
							|  |  |  |     """Tests for debugger.Debugger that need tk root.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     close needs debugger.top set in make_gui. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         requires('gui') | 
					
						
							|  |  |  |         cls.root = root = Tk() | 
					
						
							|  |  |  |         root.withdraw() | 
					
						
							|  |  |  |         cls.pyshell = Mock() | 
					
						
							|  |  |  |         cls.pyshell.root = root | 
					
						
							|  |  |  |         cls.idb = Mock() | 
					
						
							|  |  |  | # stack tests fail with debugger here. | 
					
						
							|  |  |  | ##        cls.debugger = debugger.Debugger(cls.pyshell, cls.idb) | 
					
						
							|  |  |  | ##        cls.debugger.root = root | 
					
						
							|  |  |  | ##        # real root needed for real make_gui | 
					
						
							|  |  |  | ##        # run, interacting, abort_loop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         cls.root.destroy() | 
					
						
							|  |  |  |         del cls.root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.debugger = debugger.Debugger(self.pyshell, self.idb) | 
					
						
							|  |  |  |         self.debugger.root = self.root | 
					
						
							|  |  |  |         # real root needed for real make_gui | 
					
						
							|  |  |  |         # run, interacting, abort_loop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_run_debugger(self): | 
					
						
							|  |  |  |         self.debugger.run(1, 'two') | 
					
						
							|  |  |  |         self.idb.run.assert_called_once_with(1, 'two') | 
					
						
							|  |  |  |         self.assertEqual(self.debugger.interacting, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_close(self): | 
					
						
							|  |  |  |         # Test closing the window in an idle state. | 
					
						
							|  |  |  |         self.debugger.close() | 
					
						
							|  |  |  |         self.pyshell.close_debugger.assert_called_once() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_show_stack(self): | 
					
						
							|  |  |  |         self.debugger.show_stack() | 
					
						
							|  |  |  |         self.assertEqual(self.debugger.stackviewer.gui, self.debugger) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_show_stack_with_frame(self): | 
					
						
							|  |  |  |         test_frame = MockFrame(None, None) | 
					
						
							|  |  |  |         self.debugger.frame = test_frame | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Reset the stackviewer to force it to be recreated. | 
					
						
							|  |  |  |         self.debugger.stackviewer = None | 
					
						
							|  |  |  |         self.idb.get_stack.return_value = ([], 0) | 
					
						
							|  |  |  |         self.debugger.show_stack() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Check that the newly created stackviewer has the test gui as a field. | 
					
						
							|  |  |  |         self.assertEqual(self.debugger.stackviewer.gui, self.debugger) | 
					
						
							|  |  |  |         self.idb.get_stack.assert_called_once_with(test_frame, None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class StackViewerTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							|  |  |  |         requires('gui') | 
					
						
							|  |  |  |         cls.root = Tk() | 
					
						
							|  |  |  |         cls.root.withdraw() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         cls.root.destroy() | 
					
						
							|  |  |  |         del cls.root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setUp(self): | 
					
						
							|  |  |  |         self.code = compile(TEST_CODE, 'test_stackviewer.py', 'exec') | 
					
						
							|  |  |  |         self.stack = [ | 
					
						
							|  |  |  |             (MockFrame(self.code, 1), 1), | 
					
						
							|  |  |  |             (MockFrame(self.code, 2), 2) | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         # Create a stackviewer and load the test stack. | 
					
						
							|  |  |  |         self.sv = debugger.StackViewer(self.root, None, None) | 
					
						
							|  |  |  |         self.sv.load_stack(self.stack) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init(self): | 
					
						
							|  |  |  |         # Test creation of StackViewer. | 
					
						
							|  |  |  |         gui = None | 
					
						
							|  |  |  |         flist = None | 
					
						
							|  |  |  |         master_window = self.root | 
					
						
							|  |  |  |         sv = debugger.StackViewer(master_window, flist, gui) | 
					
						
							| 
									
										
										
										
											2025-01-23 13:25:14 -05:00
										 |  |  |         self.assertHasAttr(sv, 'stack') | 
					
						
							| 
									
										
										
										
											2023-11-19 15:20:38 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def test_load_stack(self): | 
					
						
							|  |  |  |         # Test the .load_stack() method against a fixed test stack. | 
					
						
							|  |  |  |         # Check the test stack is assigned and the list contains the repr of them. | 
					
						
							|  |  |  |         self.assertEqual(self.sv.stack, self.stack) | 
					
						
							|  |  |  |         self.assertTrue('?.<module>(), line 1:' in self.sv.get(0)) | 
					
						
							|  |  |  |         self.assertEqual(self.sv.get(1), '?.<module>(), line 2: ') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_show_source(self): | 
					
						
							|  |  |  |         # Test the .show_source() method against a fixed test stack. | 
					
						
							|  |  |  |         # Patch out the file list to monitor it | 
					
						
							|  |  |  |         self.sv.flist = Mock() | 
					
						
							|  |  |  |         # Patch out isfile to pretend file exists. | 
					
						
							|  |  |  |         with patch('idlelib.debugger.os.path.isfile', return_value=True) as isfile: | 
					
						
							|  |  |  |             self.sv.show_source(1) | 
					
						
							|  |  |  |             isfile.assert_called_once_with('test_stackviewer.py') | 
					
						
							|  |  |  |             self.sv.flist.open.assert_called_once_with('test_stackviewer.py') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-10 18:19:21 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | class NameSpaceTest(unittest.TestCase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls): | 
					
						
							| 
									
										
										
										
											2023-11-19 01:39:26 -05:00
										 |  |  |         requires('gui') | 
					
						
							| 
									
										
										
										
											2016-06-10 18:19:21 -04:00
										 |  |  |         cls.root = Tk() | 
					
						
							|  |  |  |         cls.root.withdraw() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def tearDownClass(cls): | 
					
						
							|  |  |  |         cls.root.destroy() | 
					
						
							|  |  |  |         del cls.root | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_init(self): | 
					
						
							|  |  |  |         debugger.NamespaceViewer(self.root, 'Test') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     unittest.main(verbosity=2) |