• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# A test suite for pdb; at the moment, this only validates skipping of
2# specified test modules (RFE #5142).
3
4import imp
5import sys
6import os
7import unittest
8import subprocess
9import textwrap
10
11from test import test_support
12# This little helper class is essential for testing pdb under doctest.
13from test_doctest import _FakeInput
14
15
16class PdbTestCase(unittest.TestCase):
17
18    def run_pdb(self, script, commands):
19        """Run 'script' lines with pdb and the pdb 'commands'."""
20        filename = 'main.py'
21        with open(filename, 'w') as f:
22            f.write(textwrap.dedent(script))
23        self.addCleanup(test_support.unlink, filename)
24        cmd = [sys.executable, '-m', 'pdb', filename]
25        stdout = stderr = None
26        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
27                                   stdin=subprocess.PIPE,
28                                   stderr=subprocess.STDOUT,
29                                   )
30        stdout, stderr = proc.communicate(commands)
31        proc.stdout.close()
32        proc.stdin.close()
33        return stdout, stderr
34
35    def test_issue13183(self):
36        script = """
37            from bar import bar
38
39            def foo():
40                bar()
41
42            def nope():
43                pass
44
45            def foobar():
46                foo()
47                nope()
48
49            foobar()
50        """
51        commands = """
52            from bar import bar
53            break bar
54            continue
55            step
56            step
57            quit
58        """
59        bar = """
60            def bar():
61                pass
62        """
63        with open('bar.py', 'w') as f:
64            f.write(textwrap.dedent(bar))
65        self.addCleanup(test_support.unlink, 'bar.py')
66        self.addCleanup(test_support.unlink, 'bar.pyc')
67        stdout, stderr = self.run_pdb(script, commands)
68        self.assertTrue(
69            any('main.py(5)foo()->None' in l for l in stdout.splitlines()),
70            'Fail to step into the caller after a return')
71
72    def test_issue16180(self):
73        # A syntax error in the debuggee.
74        script = "def f: pass\n"
75        commands = ''
76        expected = "SyntaxError:"
77        stdout, stderr = self.run_pdb(script, commands)
78        self.assertIn(expected, stdout,
79            '\n\nExpected:\n{}\nGot:\n{}\n'
80            'Fail to handle a syntax error in the debuggee.'
81            .format(expected, stdout))
82
83
84class PdbTestInput(object):
85    """Context manager that makes testing Pdb in doctests easier."""
86
87    def __init__(self, input):
88        self.input = input
89
90    def __enter__(self):
91        self.real_stdin = sys.stdin
92        sys.stdin = _FakeInput(self.input)
93
94    def __exit__(self, *exc):
95        sys.stdin = self.real_stdin
96
97
98def write(x):
99    print x
100
101def test_pdb_displayhook():
102    """This tests the custom displayhook for pdb.
103
104    >>> def test_function(foo, bar):
105    ...     import pdb; pdb.Pdb().set_trace()
106    ...     pass
107
108    >>> with PdbTestInput([
109    ...     'foo',
110    ...     'bar',
111    ...     'for i in range(5): write(i)',
112    ...     'continue',
113    ... ]):
114    ...     test_function(1, None)
115    > <doctest test.test_pdb.test_pdb_displayhook[0]>(3)test_function()
116    -> pass
117    (Pdb) foo
118    1
119    (Pdb) bar
120    (Pdb) for i in range(5): write(i)
121    0
122    1
123    2
124    3
125    4
126    (Pdb) continue
127    """
128
129def test_pdb_breakpoint_commands():
130    """Test basic commands related to breakpoints.
131
132    >>> def test_function():
133    ...     import pdb; pdb.Pdb().set_trace()
134    ...     print(1)
135    ...     print(2)
136    ...     print(3)
137    ...     print(4)
138
139    First, need to clear bdb state that might be left over from previous tests.
140    Otherwise, the new breakpoints might get assigned different numbers.
141
142    >>> from bdb import Breakpoint
143    >>> Breakpoint.next = 1
144    >>> Breakpoint.bplist = {}
145    >>> Breakpoint.bpbynumber = [None]
146
147    Now test the breakpoint commands.  NORMALIZE_WHITESPACE is needed because
148    the breakpoint list outputs a tab for the "stop only" and "ignore next"
149    lines, which we don't want to put in here.
150
151    >>> with PdbTestInput([  # doctest: +NORMALIZE_WHITESPACE
152    ...     'break 3',
153    ...     'disable 1',
154    ...     'ignore 1 10',
155    ...     'condition 1 1 < 2',
156    ...     'break 4',
157    ...     'break 4',
158    ...     'break',
159    ...     'clear 3',
160    ...     'break',
161    ...     'condition 1',
162    ...     'enable 1',
163    ...     'clear 1',
164    ...     'commands 2',
165    ...     'print 42',
166    ...     'end',
167    ...     'continue',  # will stop at breakpoint 2 (line 4)
168    ...     'clear',     # clear all!
169    ...     'y',
170    ...     'tbreak 5',
171    ...     'continue',  # will stop at temporary breakpoint
172    ...     'break',     # make sure breakpoint is gone
173    ...     'continue',
174    ... ]):
175    ...    test_function()
176    > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(3)test_function()
177    -> print(1)
178    (Pdb) break 3
179    Breakpoint 1 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
180    (Pdb) disable 1
181    (Pdb) ignore 1 10
182    Will ignore next 10 crossings of breakpoint 1.
183    (Pdb) condition 1 1 < 2
184    (Pdb) break 4
185    Breakpoint 2 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
186    (Pdb) break 4
187    Breakpoint 3 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
188    (Pdb) break
189    Num Type         Disp Enb   Where
190    1   breakpoint   keep no    at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
191            stop only if 1 < 2
192            ignore next 10 hits
193    2   breakpoint   keep yes   at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
194    3   breakpoint   keep yes   at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
195    (Pdb) clear 3
196    Deleted breakpoint 3
197    (Pdb) break
198    Num Type         Disp Enb   Where
199    1   breakpoint   keep no    at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:3
200            stop only if 1 < 2
201            ignore next 10 hits
202    2   breakpoint   keep yes   at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:4
203    (Pdb) condition 1
204    Breakpoint 1 is now unconditional.
205    (Pdb) enable 1
206    (Pdb) clear 1
207    Deleted breakpoint 1
208    (Pdb) commands 2
209    (com) print 42
210    (com) end
211    (Pdb) continue
212    1
213    42
214    > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(4)test_function()
215    -> print(2)
216    (Pdb) clear
217    Clear all breaks? y
218    (Pdb) tbreak 5
219    Breakpoint 4 at <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>:5
220    (Pdb) continue
221    2
222    Deleted breakpoint 4
223    > <doctest test.test_pdb.test_pdb_breakpoint_commands[0]>(5)test_function()
224    -> print(3)
225    (Pdb) break
226    (Pdb) continue
227    3
228    4
229    """
230
231
232def test_pdb_skip_modules():
233    """This illustrates the simple case of module skipping.
234
235    >>> def skip_module():
236    ...     import string
237    ...     import pdb; pdb.Pdb(skip=['string*']).set_trace()
238    ...     string.lower('FOO')
239
240    >>> with PdbTestInput([
241    ...     'step',
242    ...     'continue',
243    ... ]):
244    ...     skip_module()
245    > <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module()
246    -> string.lower('FOO')
247    (Pdb) step
248    --Return--
249    > <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module()->None
250    -> string.lower('FOO')
251    (Pdb) continue
252    """
253
254
255# Module for testing skipping of module that makes a callback
256mod = imp.new_module('module_to_skip')
257exec 'def foo_pony(callback): x = 1; callback(); return None' in mod.__dict__
258
259
260def test_pdb_skip_modules_with_callback():
261    """This illustrates skipping of modules that call into other code.
262
263    >>> def skip_module():
264    ...     def callback():
265    ...         return None
266    ...     import pdb; pdb.Pdb(skip=['module_to_skip*']).set_trace()
267    ...     mod.foo_pony(callback)
268
269    >>> with PdbTestInput([
270    ...     'step',
271    ...     'step',
272    ...     'step',
273    ...     'step',
274    ...     'step',
275    ...     'continue',
276    ... ]):
277    ...     skip_module()
278    ...     pass  # provides something to "step" to
279    > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module()
280    -> mod.foo_pony(callback)
281    (Pdb) step
282    --Call--
283    > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(2)callback()
284    -> def callback():
285    (Pdb) step
286    > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(3)callback()
287    -> return None
288    (Pdb) step
289    --Return--
290    > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(3)callback()->None
291    -> return None
292    (Pdb) step
293    --Return--
294    > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module()->None
295    -> mod.foo_pony(callback)
296    (Pdb) step
297    > <doctest test.test_pdb.test_pdb_skip_modules_with_callback[1]>(10)<module>()
298    -> pass  # provides something to "step" to
299    (Pdb) continue
300    """
301
302
303def test_pdb_continue_in_bottomframe():
304    """Test that "continue" and "next" work properly in bottom frame (issue #5294).
305
306    >>> def test_function():
307    ...     import pdb, sys; inst = pdb.Pdb()
308    ...     inst.set_trace()
309    ...     inst.botframe = sys._getframe()  # hackery to get the right botframe
310    ...     print(1)
311    ...     print(2)
312    ...     print(3)
313    ...     print(4)
314
315    First, need to clear bdb state that might be left over from previous tests.
316    Otherwise, the new breakpoints might get assigned different numbers.
317
318    >>> from bdb import Breakpoint
319    >>> Breakpoint.next = 1
320    >>> Breakpoint.bplist = {}
321    >>> Breakpoint.bpbynumber = [None]
322
323    >>> with PdbTestInput([
324    ...     'next',
325    ...     'break 7',
326    ...     'continue',
327    ...     'next',
328    ...     'continue',
329    ...     'continue',
330    ... ]):
331    ...    test_function()
332    > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(4)test_function()
333    -> inst.botframe = sys._getframe()  # hackery to get the right botframe
334    (Pdb) next
335    > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(5)test_function()
336    -> print(1)
337    (Pdb) break 7
338    Breakpoint 1 at <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>:7
339    (Pdb) continue
340    1
341    2
342    > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(7)test_function()
343    -> print(3)
344    (Pdb) next
345    3
346    > <doctest test.test_pdb.test_pdb_continue_in_bottomframe[0]>(8)test_function()
347    -> print(4)
348    (Pdb) continue
349    4
350    """
351
352class ModuleInitTester(unittest.TestCase):
353
354    def test_filename_correct(self):
355        """
356        In issue 7750, it was found that if the filename has a sequence that
357        resolves to an escape character in a Python string (such as \t), it
358        will be treated as the escaped character.
359        """
360        # the test_fn must contain something like \t
361        # on Windows, this will create 'test_mod.py' in the current directory.
362        # on Unix, this will create '.\test_mod.py' in the current directory.
363        test_fn = '.\\test_mod.py'
364        code = 'print("testing pdb")'
365        with open(test_fn, 'w') as f:
366            f.write(code)
367        self.addCleanup(os.remove, test_fn)
368        cmd = [sys.executable, '-m', 'pdb', test_fn,]
369        proc = subprocess.Popen(cmd,
370            stdout=subprocess.PIPE,
371            stdin=subprocess.PIPE,
372            stderr=subprocess.STDOUT,
373            )
374        stdout, stderr = proc.communicate('quit\n')
375        self.assertIn(code, stdout, "pdb munged the filename")
376
377
378def test_main():
379    from test import test_pdb
380    test_support.run_doctest(test_pdb, verbosity=True)
381    test_support.run_unittest(
382        PdbTestCase,
383        ModuleInitTester)
384
385if __name__ == '__main__':
386    test_main()
387