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