You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
11 KiB
387 lines
11 KiB
# 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()
|