| # A test suite for pdb; at the moment, this only validates skipping of | |
| # specified test modules (RFE #5142). | |
| import imp | |
| import sys | |
| import os | |
| import unittest | |
| import subprocess | |
| import textwrap | |
| from test import test_support | |
| # This little helper class is essential for testing pdb under doctest. | |
| from test_doctest import _FakeInput | |
| class PdbTestCase(unittest.TestCase): | |
| def run_pdb(self, script, commands): | |
| """Run 'script' lines with pdb and the pdb 'commands'.""" | |
| filename = 'main.py' | |
| with open(filename, 'w') as f: | |
| f.write(textwrap.dedent(script)) | |
| self.addCleanup(test_support.unlink, filename) | |
| cmd = [sys.executable, '-m', 'pdb', filename] | |
| stdout = stderr = None | |
| proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, | |
| stdin=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, | |
| ) | |
| stdout, stderr = proc.communicate(commands) | |
| proc.stdout.close() | |
| proc.stdin.close() | |
| return stdout, stderr | |
| def test_issue13183(self): | |
| script = """ | |
| from bar import bar | |
| def foo(): | |
| bar() | |
| def nope(): | |
| pass | |
| def foobar(): | |
| foo() | |
| nope() | |
| foobar() | |
| """ | |
| commands = """ | |
| from bar import bar | |
| break bar | |
| continue | |
| step | |
| step | |
| quit | |
| """ | |
| bar = """ | |
| def bar(): | |
| pass | |
| """ | |
| with open('bar.py', 'w') as f: | |
| f.write(textwrap.dedent(bar)) | |
| self.addCleanup(test_support.unlink, 'bar.py') | |
| self.addCleanup(test_support.unlink, 'bar.pyc') | |
| stdout, stderr = self.run_pdb(script, commands) | |
| self.assertTrue( | |
| any('main.py(5)foo()->None' in l for l in stdout.splitlines()), | |
| 'Fail to step into the caller after a return') | |
| def test_issue16180(self): | |
| # A syntax error in the debuggee. | |
| script = "def f: pass\n" | |
| commands = '' | |
| expected = "SyntaxError:" | |
| stdout, stderr = self.run_pdb(script, commands) | |
| self.assertIn(expected, stdout, | |
| '\n\nExpected:\n{}\nGot:\n{}\n' | |
| 'Fail to handle a syntax error in the debuggee.' | |
| .format(expected, stdout)) | |
| class PdbTestInput(object): | |
| """Context manager that makes testing Pdb in doctests easier.""" | |
| def __init__(self, input): | |
| self.input = input | |
| def __enter__(self): | |
| self.real_stdin = sys.stdin | |
| sys.stdin = _FakeInput(self.input) | |
| def __exit__(self, *exc): | |
| sys.stdin = self.real_stdin | |
| def write(x): | |
| print x | |
| def test_pdb_displayhook(): | |
| """This tests the custom displayhook for pdb. | |
| >>> def test_function(foo, bar): | |
| ... import pdb; pdb.Pdb().set_trace() | |
| ... pass | |
| >>> with PdbTestInput([ | |
| ... 'foo', | |
| ... 'bar', | |
| ... 'for i in range(5): write(i)', | |
| ... 'continue', | |
| ... ]): | |
| ... test_function(1, None) | |
| > <doctest test.test_pdb.test_pdb_displayhook[0]>(3)test_function() | |
| -> pass | |
| (Pdb) foo | |
| 1 | |
| (Pdb) bar | |
| (Pdb) for i in range(5): write(i) | |
| 0 | |
| 1 | |
| 2 | |
| 3 | |
| 4 | |
| (Pdb) continue | |
| """ | |
| def test_pdb_breakpoint_commands(): | |
| """Test basic commands related to breakpoints. | |
| >>> def test_function(): | |
| ... import pdb; pdb.Pdb().set_trace() | |
| ... print(1) | |
| ... print(2) | |
| ... print(3) | |
| ... print(4) | |
| First, need to clear bdb state that might be left over from previous tests. | |
| Otherwise, the new breakpoints might get assigned different numbers. | |
| >>> from bdb import Breakpoint | |
| >>> Breakpoint.next = 1 | |
| >>> Breakpoint.bplist = {} | |
| >>> Breakpoint.bpbynumber = [None] | |
| Now test the breakpoint commands. NORMALIZE_WHITESPACE is needed because | |
| the breakpoint list outputs a tab for the "stop only" and "ignore next" | |
| lines, which we don't want to put in here. | |
| >>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE | |
| ... 'break 3', | |
| ... 'disable 1', | |
| ... 'ignore 1 10', | |
| ... 'condition 1 1 < 2', | |
| ... 'break 4', | |
| ... 'break 4', | |
| ... 'break', | |
| ... 'clear 3', | |
| ... 'break', | |
| ... 'condition 1', | |
| ... 'enable 1', | |
| ... 'clear 1', | |
| ... 'commands 2', | |
| ... 'print 42', | |
| ... 'end', | |
| ... 'continue', # will stop at breakpoint 2 (line 4) | |
| ... 'clear', # clear all! | |
| ... 'y', | |
| ... 'tbreak 5', | |
| ... 'continue', # will stop at temporary breakpoint | |
| ... 'break', # make sure breakpoint is gone | |
| ... 'continue', | |
| ... ]): | |
| ... test_function() | |
| > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(3)test_function() | |
| -> print(1) | |
| (Pdb) break 3 | |
| Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3 | |
| (Pdb) disable 1 | |
| (Pdb) ignore 1 10 | |
| Will ignore next 10 crossings of breakpoint 1. | |
| (Pdb) condition 1 1 < 2 | |
| (Pdb) break 4 | |
| Breakpoint 2 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4 | |
| (Pdb) break 4 | |
| Breakpoint 3 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4 | |
| (Pdb) break | |
| Num Type Disp Enb Where | |
| 1 breakpoint keep no at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3 | |
| stop only if 1 < 2 | |
| ignore next 10 hits | |
| 2 breakpoint keep yes at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4 | |
| 3 breakpoint keep yes at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4 | |
| (Pdb) clear 3 | |
| Deleted breakpoint 3 | |
| (Pdb) break | |
| Num Type Disp Enb Where | |
| 1 breakpoint keep no at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3 | |
| stop only if 1 < 2 | |
| ignore next 10 hits | |
| 2 breakpoint keep yes at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4 | |
| (Pdb) condition 1 | |
| Breakpoint 1 is now unconditional. | |
| (Pdb) enable 1 | |
| (Pdb) clear 1 | |
| Deleted breakpoint 1 | |
| (Pdb) commands 2 | |
| (com) print 42 | |
| (com) end | |
| (Pdb) continue | |
| 1 | |
| 42 | |
| > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(4)test_function() | |
| -> print(2) | |
| (Pdb) clear | |
| Clear all breaks? y | |
| (Pdb) tbreak 5 | |
| Breakpoint 4 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:5 | |
| (Pdb) continue | |
| 2 | |
| Deleted breakpoint 4 | |
| > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(5)test_function() | |
| -> print(3) | |
| (Pdb) break | |
| (Pdb) continue | |
| 3 | |
| 4 | |
| """ | |
| def test_pdb_skip_modules(): | |
| """This illustrates the simple case of module skipping. | |
| >>> def skip_module(): | |
| ... import string | |
| ... import pdb; pdb.Pdb(skip=['string*']).set_trace() | |
| ... string.lower('FOO') | |
| >>> with PdbTestInput([ | |
| ... 'step', | |
| ... 'continue', | |
| ... ]): | |
| ... skip_module() | |
| > <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module() | |
| -> string.lower('FOO') | |
| (Pdb) step | |
| --Return-- | |
| > <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module()->None | |
| -> string.lower('FOO') | |
| (Pdb) continue | |
| """ | |
| # Module for testing skipping of module that makes a callback | |
| mod = imp.new_module('module_to_skip') | |
| exec 'def foo_pony(callback): x = 1; callback(); return None' in mod.__dict__ | |
| def test_pdb_skip_modules_with_callback(): | |
| """This illustrates skipping of modules that call into other code. | |
| >>> def skip_module(): | |
| ... def callback(): | |
| ... return None | |
| ... import pdb; pdb.Pdb(skip=['module_to_skip*']).set_trace() | |
| ... mod.foo_pony(callback) | |
| >>> with PdbTestInput([ | |
| ... 'step', | |
| ... 'step', | |
| ... 'step', | |
| ... 'step', | |
| ... 'step', | |
| ... 'continue', | |
| ... ]): | |
| ... skip_module() | |
| ... pass # provides something to "step" to | |
| > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module() | |
| -> mod.foo_pony(callback) | |
| (Pdb) step | |
| --Call-- | |
| > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(2)callback() | |
| -> def callback(): | |
| (Pdb) step | |
| > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(3)callback() | |
| -> return None | |
| (Pdb) step | |
| --Return-- | |
| > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(3)callback()->None | |
| -> return None | |
| (Pdb) step | |
| --Return-- | |
| > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module()->None | |
| -> mod.foo_pony(callback) | |
| (Pdb) step | |
| > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[1]>(10)<module>() | |
| -> pass # provides something to "step" to | |
| (Pdb) continue | |
| """ | |
| def test_pdb_continue_in_bottomframe(): | |
| """Test that "continue" and "next" work properly in bottom frame (issue #5294). | |
| >>> def test_function(): | |
| ... import pdb, sys; inst = pdb.Pdb() | |
| ... inst.set_trace() | |
| ... inst.botframe = sys._getframe() # hackery to get the right botframe | |
| ... print(1) | |
| ... print(2) | |
| ... print(3) | |
| ... print(4) | |
| First, need to clear bdb state that might be left over from previous tests. | |
| Otherwise, the new breakpoints might get assigned different numbers. | |
| >>> from bdb import Breakpoint | |
| >>> Breakpoint.next = 1 | |
| >>> Breakpoint.bplist = {} | |
| >>> Breakpoint.bpbynumber = [None] | |
| >>> with PdbTestInput([ | |
| ... 'next', | |
| ... 'break 7', | |
| ... 'continue', | |
| ... 'next', | |
| ... 'continue', | |
| ... 'continue', | |
| ... ]): | |
| ... test_function() | |
| > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(4)test_function() | |
| -> inst.botframe = sys._getframe() # hackery to get the right botframe | |
| (Pdb) next | |
| > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(5)test_function() | |
| -> print(1) | |
| (Pdb) break 7 | |
| Breakpoint 1 at <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>:7 | |
| (Pdb) continue | |
| 1 | |
| 2 | |
| > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(7)test_function() | |
| -> print(3) | |
| (Pdb) next | |
| 3 | |
| > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(8)test_function() | |
| -> print(4) | |
| (Pdb) continue | |
| 4 | |
| """ | |
| class ModuleInitTester(unittest.TestCase): | |
| def test_filename_correct(self): | |
| """ | |
| In issue 7750, it was found that if the filename has a sequence that | |
| resolves to an escape character in a Python string (such as \t), it | |
| will be treated as the escaped character. | |
| """ | |
| # the test_fn must contain something like \t | |
| # on Windows, this will create 'test_mod.py' in the current directory. | |
| # on Unix, this will create '.\test_mod.py' in the current directory. | |
| test_fn = '.\\test_mod.py' | |
| code = 'print("testing pdb")' | |
| with open(test_fn, 'w') as f: | |
| f.write(code) | |
| self.addCleanup(os.remove, test_fn) | |
| cmd = [sys.executable, '-m', 'pdb', test_fn,] | |
| proc = subprocess.Popen(cmd, | |
| stdout=subprocess.PIPE, | |
| stdin=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, | |
| ) | |
| stdout, stderr = proc.communicate('quit\n') | |
| self.assertIn(code, stdout, "pdb munged the filename") | |
| def test_main(): | |
| from test import test_pdb | |
| test_support.run_doctest(test_pdb, verbosity=True) | |
| test_support.run_unittest( | |
| PdbTestCase, | |
| ModuleInitTester) | |
| if __name__ == '__main__': | |
| test_main() |