1""" Test the bdb module.
2
3    A test defines a list of tuples that may be seen as paired tuples, each
4    pair being defined by 'expect_tuple, set_tuple' as follows:
5
6        ([event, [lineno[, co_name[, eargs]]]]), (set_type, [sargs])
7
8    * 'expect_tuple' describes the expected current state of the Bdb instance.
9      It may be the empty tuple and no check is done in that case.
10    * 'set_tuple' defines the set_*() method to be invoked when the Bdb
11      instance reaches this state.
12
13    Example of an 'expect_tuple, set_tuple' pair:
14
15        ('line', 2, 'tfunc_main'), ('step', )
16
17    Definitions of the members of the 'expect_tuple':
18        event:
19            Name of the trace event. The set methods that do not give back
20            control to the tracer [1] do not trigger a tracer event and in
21            that case the next 'event' may be 'None' by convention, its value
22            is not checked.
23            [1] Methods that trigger a trace event are set_step(), set_next(),
24            set_return(), set_until() and set_continue().
25        lineno:
26            Line number. Line numbers are relative to the start of the
27            function when tracing a function in the test_bdb module (i.e. this
28            module).
29        co_name:
30            Name of the function being currently traced.
31        eargs:
32            A tuple:
33            * On an 'exception' event the tuple holds a class object, the
34              current exception must be an instance of this class.
35            * On a 'line' event, the tuple holds a dictionary and a list. The
36              dictionary maps each breakpoint number that has been hit on this
37              line to its hits count. The list holds the list of breakpoint
38              number temporaries that are being deleted.
39
40    Definitions of the members of the 'set_tuple':
41        set_type:
42            The type of the set method to be invoked. This may
43            be the type of one of the Bdb set methods: 'step', 'next',
44            'until', 'return', 'continue', 'break', 'quit', or the type of one
45            of the set methods added by test_bdb.Bdb: 'ignore', 'enable',
46            'disable', 'clear', 'up', 'down'.
47        sargs:
48            The arguments of the set method if any, packed in a tuple.
49"""
50
51import bdb as _bdb
52import sys
53import os
54import unittest
55import textwrap
56import importlib
57import linecache
58from contextlib import contextmanager
59from itertools import islice, repeat
60from test.support import import_helper
61from test.support import os_helper
62from test.support import patch_list
63
64
65class BdbException(Exception): pass
66class BdbError(BdbException): """Error raised by the Bdb instance."""
67class BdbSyntaxError(BdbException): """Syntax error in the test case."""
68class BdbNotExpectedError(BdbException): """Unexpected result."""
69
70# When 'dry_run' is set to true, expect tuples are ignored and the actual
71# state of the tracer is printed after running each set_*() method of the test
72# case. The full list of breakpoints and their attributes is also printed
73# after each 'line' event where a breakpoint has been hit.
74dry_run = 0
75
76def reset_Breakpoint():
77    _bdb.Breakpoint.clearBreakpoints()
78
79def info_breakpoints():
80    bp_list = [bp for  bp in _bdb.Breakpoint.bpbynumber if bp]
81    if not bp_list:
82        return ''
83
84    header_added = False
85    for bp in bp_list:
86        if not header_added:
87            info = 'BpNum Temp Enb Hits Ignore Where\n'
88            header_added = True
89
90        disp = 'yes ' if bp.temporary else 'no  '
91        enab = 'yes' if bp.enabled else 'no '
92        info += ('%-5d %s %s %-4d %-6d at %s:%d' %
93                    (bp.number, disp, enab, bp.hits, bp.ignore,
94                     os.path.basename(bp.file), bp.line))
95        if bp.cond:
96            info += '\n\tstop only if %s' % (bp.cond,)
97        info += '\n'
98    return info
99
100class Bdb(_bdb.Bdb):
101    """Extend Bdb to enhance test coverage."""
102
103    def trace_dispatch(self, frame, event, arg):
104        self.currentbp = None
105        return super().trace_dispatch(frame, event, arg)
106
107    def set_break(self, filename, lineno, temporary=False, cond=None,
108                  funcname=None):
109        if isinstance(funcname, str):
110            if filename == __file__:
111                globals_ = globals()
112            else:
113                module = importlib.import_module(filename[:-3])
114                globals_ = module.__dict__
115            func = eval(funcname, globals_)
116            code = func.__code__
117            filename = code.co_filename
118            lineno = code.co_firstlineno
119            funcname = code.co_name
120
121        res = super().set_break(filename, lineno, temporary=temporary,
122                                 cond=cond, funcname=funcname)
123        if isinstance(res, str):
124            raise BdbError(res)
125        return res
126
127    def get_stack(self, f, t):
128        self.stack, self.index = super().get_stack(f, t)
129        self.frame = self.stack[self.index][0]
130        return self.stack, self.index
131
132    def set_ignore(self, bpnum):
133        """Increment the ignore count of Breakpoint number 'bpnum'."""
134        bp = self.get_bpbynumber(bpnum)
135        bp.ignore += 1
136
137    def set_enable(self, bpnum):
138        bp = self.get_bpbynumber(bpnum)
139        bp.enabled = True
140
141    def set_disable(self, bpnum):
142        bp = self.get_bpbynumber(bpnum)
143        bp.enabled = False
144
145    def set_clear(self, fname, lineno):
146        err = self.clear_break(fname, lineno)
147        if err:
148            raise BdbError(err)
149
150    def set_up(self):
151        """Move up in the frame stack."""
152        if not self.index:
153            raise BdbError('Oldest frame')
154        self.index -= 1
155        self.frame = self.stack[self.index][0]
156
157    def set_down(self):
158        """Move down in the frame stack."""
159        if self.index + 1 == len(self.stack):
160            raise BdbError('Newest frame')
161        self.index += 1
162        self.frame = self.stack[self.index][0]
163
164class Tracer(Bdb):
165    """A tracer for testing the bdb module."""
166
167    def __init__(self, expect_set, skip=None, dry_run=False, test_case=None):
168        super().__init__(skip=skip)
169        self.expect_set = expect_set
170        self.dry_run = dry_run
171        self.header = ('Dry-run results for %s:' % test_case if
172                       test_case is not None else None)
173        self.init_test()
174
175    def init_test(self):
176        self.cur_except = None
177        self.expect_set_no = 0
178        self.breakpoint_hits = None
179        self.expected_list = list(islice(self.expect_set, 0, None, 2))
180        self.set_list = list(islice(self.expect_set, 1, None, 2))
181
182    def trace_dispatch(self, frame, event, arg):
183        # On an 'exception' event, call_exc_trace() in Python/ceval.c discards
184        # a BdbException raised by the Tracer instance, so we raise it on the
185        # next trace_dispatch() call that occurs unless the set_quit() or
186        # set_continue() method has been invoked on the 'exception' event.
187        if self.cur_except is not None:
188            raise self.cur_except
189
190        if event == 'exception':
191            try:
192                res = super().trace_dispatch(frame, event, arg)
193                return res
194            except BdbException as e:
195                self.cur_except = e
196                return self.trace_dispatch
197        else:
198            return super().trace_dispatch(frame, event, arg)
199
200    def user_call(self, frame, argument_list):
201        # Adopt the same behavior as pdb and, as a side effect, skip also the
202        # first 'call' event when the Tracer is started with Tracer.runcall()
203        # which may be possibly considered as a bug.
204        if not self.stop_here(frame):
205            return
206        self.process_event('call', frame, argument_list)
207        self.next_set_method()
208
209    def user_line(self, frame):
210        self.process_event('line', frame)
211
212        if self.dry_run and self.breakpoint_hits:
213            info = info_breakpoints().strip('\n')
214            # Indent each line.
215            for line in info.split('\n'):
216                print('  ' + line)
217        self.delete_temporaries()
218        self.breakpoint_hits = None
219
220        self.next_set_method()
221
222    def user_return(self, frame, return_value):
223        self.process_event('return', frame, return_value)
224        self.next_set_method()
225
226    def user_exception(self, frame, exc_info):
227        self.exc_info = exc_info
228        self.process_event('exception', frame)
229        self.next_set_method()
230
231    def do_clear(self, arg):
232        # The temporary breakpoints are deleted in user_line().
233        bp_list = [self.currentbp]
234        self.breakpoint_hits = (bp_list, bp_list)
235
236    def delete_temporaries(self):
237        if self.breakpoint_hits:
238            for n in self.breakpoint_hits[1]:
239                self.clear_bpbynumber(n)
240
241    def pop_next(self):
242        self.expect_set_no += 1
243        try:
244            self.expect = self.expected_list.pop(0)
245        except IndexError:
246            raise BdbNotExpectedError(
247                'expect_set list exhausted, cannot pop item %d' %
248                self.expect_set_no)
249        self.set_tuple = self.set_list.pop(0)
250
251    def process_event(self, event, frame, *args):
252        # Call get_stack() to enable walking the stack with set_up() and
253        # set_down().
254        tb = None
255        if event == 'exception':
256            tb = self.exc_info[2]
257        self.get_stack(frame, tb)
258
259        # A breakpoint has been hit and it is not a temporary.
260        if self.currentbp is not None and not self.breakpoint_hits:
261            bp_list = [self.currentbp]
262            self.breakpoint_hits = (bp_list, [])
263
264        # Pop next event.
265        self.event= event
266        self.pop_next()
267        if self.dry_run:
268            self.print_state(self.header)
269            return
270
271        # Validate the expected results.
272        if self.expect:
273            self.check_equal(self.expect[0], event, 'Wrong event type')
274            self.check_lno_name()
275
276        if event in ('call', 'return'):
277            self.check_expect_max_size(3)
278        elif len(self.expect) > 3:
279            if event == 'line':
280                bps, temporaries = self.expect[3]
281                bpnums = sorted(bps.keys())
282                if not self.breakpoint_hits:
283                    self.raise_not_expected(
284                        'No breakpoints hit at expect_set item %d' %
285                        self.expect_set_no)
286                self.check_equal(bpnums, self.breakpoint_hits[0],
287                    'Breakpoint numbers do not match')
288                self.check_equal([bps[n] for n in bpnums],
289                    [self.get_bpbynumber(n).hits for
290                        n in self.breakpoint_hits[0]],
291                    'Wrong breakpoint hit count')
292                self.check_equal(sorted(temporaries), self.breakpoint_hits[1],
293                    'Wrong temporary breakpoints')
294
295            elif event == 'exception':
296                if not isinstance(self.exc_info[1], self.expect[3]):
297                    self.raise_not_expected(
298                        "Wrong exception at expect_set item %d, got '%s'" %
299                        (self.expect_set_no, self.exc_info))
300
301    def check_equal(self, expected, result, msg):
302        if expected == result:
303            return
304        self.raise_not_expected("%s at expect_set item %d, got '%s'" %
305                                (msg, self.expect_set_no, result))
306
307    def check_lno_name(self):
308        """Check the line number and function co_name."""
309        s = len(self.expect)
310        if s > 1:
311            lineno = self.lno_abs2rel()
312            self.check_equal(self.expect[1], lineno, 'Wrong line number')
313        if s > 2:
314            self.check_equal(self.expect[2], self.frame.f_code.co_name,
315                                                'Wrong function name')
316
317    def check_expect_max_size(self, size):
318        if len(self.expect) > size:
319            raise BdbSyntaxError('Invalid size of the %s expect tuple: %s' %
320                                 (self.event, self.expect))
321
322    def lno_abs2rel(self):
323        fname = self.canonic(self.frame.f_code.co_filename)
324        lineno = self.frame.f_lineno
325        return ((lineno - self.frame.f_code.co_firstlineno + 1)
326            if fname == self.canonic(__file__) else lineno)
327
328    def lno_rel2abs(self, fname, lineno):
329        return (self.frame.f_code.co_firstlineno + lineno - 1
330            if (lineno and self.canonic(fname) == self.canonic(__file__))
331            else lineno)
332
333    def get_state(self):
334        lineno = self.lno_abs2rel()
335        co_name = self.frame.f_code.co_name
336        state = "('%s', %d, '%s'" % (self.event, lineno, co_name)
337        if self.breakpoint_hits:
338            bps = '{'
339            for n in self.breakpoint_hits[0]:
340                if bps != '{':
341                    bps += ', '
342                bps += '%s: %s' % (n, self.get_bpbynumber(n).hits)
343            bps += '}'
344            bps = '(' + bps + ', ' + str(self.breakpoint_hits[1]) + ')'
345            state += ', ' + bps
346        elif self.event == 'exception':
347            state += ', ' + self.exc_info[0].__name__
348        state += '), '
349        return state.ljust(32) + str(self.set_tuple) + ','
350
351    def print_state(self, header=None):
352        if header is not None and self.expect_set_no == 1:
353            print()
354            print(header)
355        print('%d: %s' % (self.expect_set_no, self.get_state()))
356
357    def raise_not_expected(self, msg):
358        msg += '\n'
359        msg += '  Expected: %s\n' % str(self.expect)
360        msg += '  Got:      ' + self.get_state()
361        raise BdbNotExpectedError(msg)
362
363    def next_set_method(self):
364        set_type = self.set_tuple[0]
365        args = self.set_tuple[1] if len(self.set_tuple) == 2 else None
366        set_method = getattr(self, 'set_' + set_type)
367
368        # The following set methods give back control to the tracer.
369        if set_type in ('step', 'continue', 'quit'):
370            set_method()
371            return
372        elif set_type in ('next', 'return'):
373            set_method(self.frame)
374            return
375        elif set_type == 'until':
376            lineno = None
377            if args:
378                lineno = self.lno_rel2abs(self.frame.f_code.co_filename,
379                                          args[0])
380            set_method(self.frame, lineno)
381            return
382
383        # The following set methods do not give back control to the tracer and
384        # next_set_method() is called recursively.
385        if (args and set_type in ('break', 'clear', 'ignore', 'enable',
386                                    'disable')) or set_type in ('up', 'down'):
387            if set_type in ('break', 'clear'):
388                fname, lineno, *remain = args
389                lineno = self.lno_rel2abs(fname, lineno)
390                args = [fname, lineno]
391                args.extend(remain)
392                set_method(*args)
393            elif set_type in ('ignore', 'enable', 'disable'):
394                set_method(*args)
395            elif set_type in ('up', 'down'):
396                set_method()
397
398            # Process the next expect_set item.
399            # It is not expected that a test may reach the recursion limit.
400            self.event= None
401            self.pop_next()
402            if self.dry_run:
403                self.print_state()
404            else:
405                if self.expect:
406                    self.check_lno_name()
407                self.check_expect_max_size(3)
408            self.next_set_method()
409        else:
410            raise BdbSyntaxError('"%s" is an invalid set_tuple' %
411                                 self.set_tuple)
412
413class TracerRun():
414    """Provide a context for running a Tracer instance with a test case."""
415
416    def __init__(self, test_case, skip=None):
417        self.test_case = test_case
418        self.dry_run = test_case.dry_run
419        self.tracer = Tracer(test_case.expect_set, skip=skip,
420                             dry_run=self.dry_run, test_case=test_case.id())
421        self._original_tracer = None
422
423    def __enter__(self):
424        # test_pdb does not reset Breakpoint class attributes on exit :-(
425        reset_Breakpoint()
426        self._original_tracer = sys.gettrace()
427        return self.tracer
428
429    def __exit__(self, type_=None, value=None, traceback=None):
430        reset_Breakpoint()
431        sys.settrace(self._original_tracer)
432
433        not_empty = ''
434        if self.tracer.set_list:
435            not_empty += 'All paired tuples have not been processed, '
436            not_empty += ('the last one was number %d' %
437                          self.tracer.expect_set_no)
438
439        # Make a BdbNotExpectedError a unittest failure.
440        if type_ is not None and issubclass(BdbNotExpectedError, type_):
441            if isinstance(value, BaseException) and value.args:
442                err_msg = value.args[0]
443                if not_empty:
444                    err_msg += '\n' + not_empty
445                if self.dry_run:
446                    print(err_msg)
447                    return True
448                else:
449                    self.test_case.fail(err_msg)
450            else:
451                assert False, 'BdbNotExpectedError with empty args'
452
453        if not_empty:
454            if self.dry_run:
455                print(not_empty)
456            else:
457                self.test_case.fail(not_empty)
458
459def run_test(modules, set_list, skip=None):
460    """Run a test and print the dry-run results.
461
462    'modules':  A dictionary mapping module names to their source code as a
463                string. The dictionary MUST include one module named
464                'test_module' with a main() function.
465    'set_list': A list of set_type tuples to be run on the module.
466
467    For example, running the following script outputs the following results:
468
469    *****************************   SCRIPT   ********************************
470
471    from test.test_bdb import run_test, break_in_func
472
473    code = '''
474        def func():
475            lno = 3
476
477        def main():
478            func()
479            lno = 7
480    '''
481
482    set_list = [
483                break_in_func('func', 'test_module.py'),
484                ('continue', ),
485                ('step', ),
486                ('step', ),
487                ('step', ),
488                ('quit', ),
489            ]
490
491    modules = { 'test_module': code }
492    run_test(modules, set_list)
493
494    ****************************   results   ********************************
495
496    1: ('line', 2, 'tfunc_import'),    ('next',),
497    2: ('line', 3, 'tfunc_import'),    ('step',),
498    3: ('call', 5, 'main'),            ('break', ('test_module.py', None, False, None, 'func')),
499    4: ('None', 5, 'main'),            ('continue',),
500    5: ('line', 3, 'func', ({1: 1}, [])), ('step',),
501      BpNum Temp Enb Hits Ignore Where
502      1     no   yes 1    0      at test_module.py:2
503    6: ('return', 3, 'func'),          ('step',),
504    7: ('line', 7, 'main'),            ('step',),
505    8: ('return', 7, 'main'),          ('quit',),
506
507    *************************************************************************
508
509    """
510    def gen(a, b):
511        try:
512            while 1:
513                x = next(a)
514                y = next(b)
515                yield x
516                yield y
517        except StopIteration:
518            return
519
520    # Step over the import statement in tfunc_import using 'next' and step
521    # into main() in test_module.
522    sl = [('next', ), ('step', )]
523    sl.extend(set_list)
524
525    test = BaseTestCase()
526    test.dry_run = True
527    test.id = lambda : None
528    test.expect_set = list(gen(repeat(()), iter(sl)))
529    with create_modules(modules):
530        with TracerRun(test, skip=skip) as tracer:
531            tracer.runcall(tfunc_import)
532
533@contextmanager
534def create_modules(modules):
535    with os_helper.temp_cwd():
536        sys.path.append(os.getcwd())
537        try:
538            for m in modules:
539                fname = m + '.py'
540                with open(fname, 'w', encoding="utf-8") as f:
541                    f.write(textwrap.dedent(modules[m]))
542                linecache.checkcache(fname)
543            importlib.invalidate_caches()
544            yield
545        finally:
546            for m in modules:
547                import_helper.forget(m)
548            sys.path.pop()
549
550def break_in_func(funcname, fname=__file__, temporary=False, cond=None):
551    return 'break', (fname, None, temporary, cond, funcname)
552
553TEST_MODULE = 'test_module_for_bdb'
554TEST_MODULE_FNAME = TEST_MODULE + '.py'
555def tfunc_import():
556    import test_module_for_bdb
557    test_module_for_bdb.main()
558
559def tfunc_main():
560    lno = 2
561    tfunc_first()
562    tfunc_second()
563    lno = 5
564    lno = 6
565    lno = 7
566
567def tfunc_first():
568    lno = 2
569    lno = 3
570    lno = 4
571
572def tfunc_second():
573    lno = 2
574
575class BaseTestCase(unittest.TestCase):
576    """Base class for all tests."""
577
578    dry_run = dry_run
579
580    def fail(self, msg=None):
581        # Override fail() to use 'raise from None' to avoid repetition of the
582        # error message and traceback.
583        raise self.failureException(msg) from None
584
585class StateTestCase(BaseTestCase):
586    """Test the step, next, return, until and quit 'set_' methods."""
587
588    def test_step(self):
589        self.expect_set = [
590            ('line', 2, 'tfunc_main'),  ('step', ),
591            ('line', 3, 'tfunc_main'),  ('step', ),
592            ('call', 1, 'tfunc_first'), ('step', ),
593            ('line', 2, 'tfunc_first'), ('quit', ),
594        ]
595        with TracerRun(self) as tracer:
596            tracer.runcall(tfunc_main)
597
598    def test_step_next_on_last_statement(self):
599        for set_type in ('step', 'next'):
600            with self.subTest(set_type=set_type):
601                self.expect_set = [
602                    ('line', 2, 'tfunc_main'),               ('step', ),
603                    ('line', 3, 'tfunc_main'),               ('step', ),
604                    ('call', 1, 'tfunc_first'),              ('break', (__file__, 3)),
605                    ('None', 1, 'tfunc_first'),              ('continue', ),
606                    ('line', 3, 'tfunc_first', ({1:1}, [])), (set_type, ),
607                    ('line', 4, 'tfunc_first'),              ('quit', ),
608                ]
609                with TracerRun(self) as tracer:
610                    tracer.runcall(tfunc_main)
611
612    def test_next(self):
613        self.expect_set = [
614            ('line', 2, 'tfunc_main'),   ('step', ),
615            ('line', 3, 'tfunc_main'),   ('next', ),
616            ('line', 4, 'tfunc_main'),   ('step', ),
617            ('call', 1, 'tfunc_second'), ('step', ),
618            ('line', 2, 'tfunc_second'), ('quit', ),
619        ]
620        with TracerRun(self) as tracer:
621            tracer.runcall(tfunc_main)
622
623    def test_next_over_import(self):
624        code = """
625            def main():
626                lno = 3
627        """
628        modules = { TEST_MODULE: code }
629        with create_modules(modules):
630            self.expect_set = [
631                ('line', 2, 'tfunc_import'), ('next', ),
632                ('line', 3, 'tfunc_import'), ('quit', ),
633            ]
634            with TracerRun(self) as tracer:
635                tracer.runcall(tfunc_import)
636
637    def test_next_on_plain_statement(self):
638        # Check that set_next() is equivalent to set_step() on a plain
639        # statement.
640        self.expect_set = [
641            ('line', 2, 'tfunc_main'),  ('step', ),
642            ('line', 3, 'tfunc_main'),  ('step', ),
643            ('call', 1, 'tfunc_first'), ('next', ),
644            ('line', 2, 'tfunc_first'), ('quit', ),
645        ]
646        with TracerRun(self) as tracer:
647            tracer.runcall(tfunc_main)
648
649    def test_next_in_caller_frame(self):
650        # Check that set_next() in the caller frame causes the tracer
651        # to stop next in the caller frame.
652        self.expect_set = [
653            ('line', 2, 'tfunc_main'),  ('step', ),
654            ('line', 3, 'tfunc_main'),  ('step', ),
655            ('call', 1, 'tfunc_first'), ('up', ),
656            ('None', 3, 'tfunc_main'),  ('next', ),
657            ('line', 4, 'tfunc_main'),  ('quit', ),
658        ]
659        with TracerRun(self) as tracer:
660            tracer.runcall(tfunc_main)
661
662    def test_return(self):
663        self.expect_set = [
664            ('line', 2, 'tfunc_main'),    ('step', ),
665            ('line', 3, 'tfunc_main'),    ('step', ),
666            ('call', 1, 'tfunc_first'),   ('step', ),
667            ('line', 2, 'tfunc_first'),   ('return', ),
668            ('return', 4, 'tfunc_first'), ('step', ),
669            ('line', 4, 'tfunc_main'),    ('quit', ),
670        ]
671        with TracerRun(self) as tracer:
672            tracer.runcall(tfunc_main)
673
674    def test_return_in_caller_frame(self):
675        self.expect_set = [
676            ('line', 2, 'tfunc_main'),   ('step', ),
677            ('line', 3, 'tfunc_main'),   ('step', ),
678            ('call', 1, 'tfunc_first'),  ('up', ),
679            ('None', 3, 'tfunc_main'),   ('return', ),
680            ('return', 7, 'tfunc_main'), ('quit', ),
681        ]
682        with TracerRun(self) as tracer:
683            tracer.runcall(tfunc_main)
684
685    def test_until(self):
686        self.expect_set = [
687            ('line', 2, 'tfunc_main'),  ('step', ),
688            ('line', 3, 'tfunc_main'),  ('step', ),
689            ('call', 1, 'tfunc_first'), ('step', ),
690            ('line', 2, 'tfunc_first'), ('until', (4, )),
691            ('line', 4, 'tfunc_first'), ('quit', ),
692        ]
693        with TracerRun(self) as tracer:
694            tracer.runcall(tfunc_main)
695
696    def test_until_with_too_large_count(self):
697        self.expect_set = [
698            ('line', 2, 'tfunc_main'),               break_in_func('tfunc_first'),
699            ('None', 2, 'tfunc_main'),               ('continue', ),
700            ('line', 2, 'tfunc_first', ({1:1}, [])), ('until', (9999, )),
701            ('return', 4, 'tfunc_first'),            ('quit', ),
702        ]
703        with TracerRun(self) as tracer:
704            tracer.runcall(tfunc_main)
705
706    def test_until_in_caller_frame(self):
707        self.expect_set = [
708            ('line', 2, 'tfunc_main'),  ('step', ),
709            ('line', 3, 'tfunc_main'),  ('step', ),
710            ('call', 1, 'tfunc_first'), ('up', ),
711            ('None', 3, 'tfunc_main'),  ('until', (6, )),
712            ('line', 6, 'tfunc_main'),  ('quit', ),
713        ]
714        with TracerRun(self) as tracer:
715            tracer.runcall(tfunc_main)
716
717    @patch_list(sys.meta_path)
718    def test_skip(self):
719        # Check that tracing is skipped over the import statement in
720        # 'tfunc_import()'.
721
722        # Remove all but the standard importers.
723        sys.meta_path[:] = (
724            item
725            for item in sys.meta_path
726            if item.__module__.startswith('_frozen_importlib')
727        )
728
729        code = """
730            def main():
731                lno = 3
732        """
733        modules = { TEST_MODULE: code }
734        with create_modules(modules):
735            self.expect_set = [
736                ('line', 2, 'tfunc_import'), ('step', ),
737                ('line', 3, 'tfunc_import'), ('quit', ),
738            ]
739            skip = ('importlib*', 'zipimport', 'encodings.*', TEST_MODULE)
740            with TracerRun(self, skip=skip) as tracer:
741                tracer.runcall(tfunc_import)
742
743    def test_skip_with_no_name_module(self):
744        # some frames have `globals` with no `__name__`
745        # for instance the second frame in this traceback
746        # exec(compile('raise ValueError()', '', 'exec'), {})
747        bdb = Bdb(skip=['anything*'])
748        self.assertIs(bdb.is_skipped_module(None), False)
749
750    def test_down(self):
751        # Check that set_down() raises BdbError at the newest frame.
752        self.expect_set = [
753            ('line', 2, 'tfunc_main'), ('down', ),
754        ]
755        with TracerRun(self) as tracer:
756            self.assertRaises(BdbError, tracer.runcall, tfunc_main)
757
758    def test_up(self):
759        self.expect_set = [
760            ('line', 2, 'tfunc_main'),  ('step', ),
761            ('line', 3, 'tfunc_main'),  ('step', ),
762            ('call', 1, 'tfunc_first'), ('up', ),
763            ('None', 3, 'tfunc_main'),  ('quit', ),
764        ]
765        with TracerRun(self) as tracer:
766            tracer.runcall(tfunc_main)
767
768class BreakpointTestCase(BaseTestCase):
769    """Test the breakpoint set method."""
770
771    def test_bp_on_non_existent_module(self):
772        self.expect_set = [
773            ('line', 2, 'tfunc_import'), ('break', ('/non/existent/module.py', 1))
774        ]
775        with TracerRun(self) as tracer:
776            self.assertRaises(BdbError, tracer.runcall, tfunc_import)
777
778    def test_bp_after_last_statement(self):
779        code = """
780            def main():
781                lno = 3
782        """
783        modules = { TEST_MODULE: code }
784        with create_modules(modules):
785            self.expect_set = [
786                ('line', 2, 'tfunc_import'), ('break', (TEST_MODULE_FNAME, 4))
787            ]
788            with TracerRun(self) as tracer:
789                self.assertRaises(BdbError, tracer.runcall, tfunc_import)
790
791    def test_temporary_bp(self):
792        code = """
793            def func():
794                lno = 3
795
796            def main():
797                for i in range(2):
798                    func()
799        """
800        modules = { TEST_MODULE: code }
801        with create_modules(modules):
802            self.expect_set = [
803                ('line', 2, 'tfunc_import'),
804                    break_in_func('func', TEST_MODULE_FNAME, True),
805                ('None', 2, 'tfunc_import'),
806                    break_in_func('func', TEST_MODULE_FNAME, True),
807                ('None', 2, 'tfunc_import'),       ('continue', ),
808                ('line', 3, 'func', ({1:1}, [1])), ('continue', ),
809                ('line', 3, 'func', ({2:1}, [2])), ('quit', ),
810            ]
811            with TracerRun(self) as tracer:
812                tracer.runcall(tfunc_import)
813
814    def test_disabled_temporary_bp(self):
815        code = """
816            def func():
817                lno = 3
818
819            def main():
820                for i in range(3):
821                    func()
822        """
823        modules = { TEST_MODULE: code }
824        with create_modules(modules):
825            self.expect_set = [
826                ('line', 2, 'tfunc_import'),
827                    break_in_func('func', TEST_MODULE_FNAME),
828                ('None', 2, 'tfunc_import'),
829                    break_in_func('func', TEST_MODULE_FNAME, True),
830                ('None', 2, 'tfunc_import'),       ('disable', (2, )),
831                ('None', 2, 'tfunc_import'),       ('continue', ),
832                ('line', 3, 'func', ({1:1}, [])),  ('enable', (2, )),
833                ('None', 3, 'func'),               ('disable', (1, )),
834                ('None', 3, 'func'),               ('continue', ),
835                ('line', 3, 'func', ({2:1}, [2])), ('enable', (1, )),
836                ('None', 3, 'func'),               ('continue', ),
837                ('line', 3, 'func', ({1:2}, [])),  ('quit', ),
838            ]
839            with TracerRun(self) as tracer:
840                tracer.runcall(tfunc_import)
841
842    def test_bp_condition(self):
843        code = """
844            def func(a):
845                lno = 3
846
847            def main():
848                for i in range(3):
849                    func(i)
850        """
851        modules = { TEST_MODULE: code }
852        with create_modules(modules):
853            self.expect_set = [
854                ('line', 2, 'tfunc_import'),
855                    break_in_func('func', TEST_MODULE_FNAME, False, 'a == 2'),
856                ('None', 2, 'tfunc_import'),       ('continue', ),
857                ('line', 3, 'func', ({1:3}, [])),  ('quit', ),
858            ]
859            with TracerRun(self) as tracer:
860                tracer.runcall(tfunc_import)
861
862    def test_bp_exception_on_condition_evaluation(self):
863        code = """
864            def func(a):
865                lno = 3
866
867            def main():
868                func(0)
869        """
870        modules = { TEST_MODULE: code }
871        with create_modules(modules):
872            self.expect_set = [
873                ('line', 2, 'tfunc_import'),
874                    break_in_func('func', TEST_MODULE_FNAME, False, '1 / 0'),
875                ('None', 2, 'tfunc_import'),       ('continue', ),
876                ('line', 3, 'func', ({1:1}, [])),  ('quit', ),
877            ]
878            with TracerRun(self) as tracer:
879                tracer.runcall(tfunc_import)
880
881    def test_bp_ignore_count(self):
882        code = """
883            def func():
884                lno = 3
885
886            def main():
887                for i in range(2):
888                    func()
889        """
890        modules = { TEST_MODULE: code }
891        with create_modules(modules):
892            self.expect_set = [
893                ('line', 2, 'tfunc_import'),
894                    break_in_func('func', TEST_MODULE_FNAME),
895                ('None', 2, 'tfunc_import'),      ('ignore', (1, )),
896                ('None', 2, 'tfunc_import'),      ('continue', ),
897                ('line', 3, 'func', ({1:2}, [])), ('quit', ),
898            ]
899            with TracerRun(self) as tracer:
900                tracer.runcall(tfunc_import)
901
902    def test_ignore_count_on_disabled_bp(self):
903        code = """
904            def func():
905                lno = 3
906
907            def main():
908                for i in range(3):
909                    func()
910        """
911        modules = { TEST_MODULE: code }
912        with create_modules(modules):
913            self.expect_set = [
914                ('line', 2, 'tfunc_import'),
915                    break_in_func('func', TEST_MODULE_FNAME),
916                ('None', 2, 'tfunc_import'),
917                    break_in_func('func', TEST_MODULE_FNAME),
918                ('None', 2, 'tfunc_import'),      ('ignore', (1, )),
919                ('None', 2, 'tfunc_import'),      ('disable', (1, )),
920                ('None', 2, 'tfunc_import'),      ('continue', ),
921                ('line', 3, 'func', ({2:1}, [])), ('enable', (1, )),
922                ('None', 3, 'func'),              ('continue', ),
923                ('line', 3, 'func', ({2:2}, [])), ('continue', ),
924                ('line', 3, 'func', ({1:2}, [])), ('quit', ),
925            ]
926            with TracerRun(self) as tracer:
927                tracer.runcall(tfunc_import)
928
929    def test_clear_two_bp_on_same_line(self):
930        code = """
931            def func():
932                lno = 3
933                lno = 4
934
935            def main():
936                for i in range(3):
937                    func()
938        """
939        modules = { TEST_MODULE: code }
940        with create_modules(modules):
941            self.expect_set = [
942                ('line', 2, 'tfunc_import'),      ('break', (TEST_MODULE_FNAME, 3)),
943                ('None', 2, 'tfunc_import'),      ('break', (TEST_MODULE_FNAME, 3)),
944                ('None', 2, 'tfunc_import'),      ('break', (TEST_MODULE_FNAME, 4)),
945                ('None', 2, 'tfunc_import'),      ('continue', ),
946                ('line', 3, 'func', ({1:1}, [])), ('continue', ),
947                ('line', 4, 'func', ({3:1}, [])), ('clear', (TEST_MODULE_FNAME, 3)),
948                ('None', 4, 'func'),              ('continue', ),
949                ('line', 4, 'func', ({3:2}, [])), ('quit', ),
950            ]
951            with TracerRun(self) as tracer:
952                tracer.runcall(tfunc_import)
953
954    def test_clear_at_no_bp(self):
955        self.expect_set = [
956            ('line', 2, 'tfunc_import'), ('clear', (__file__, 1))
957        ]
958        with TracerRun(self) as tracer:
959            self.assertRaises(BdbError, tracer.runcall, tfunc_import)
960
961    def test_load_bps_from_previous_Bdb_instance(self):
962        reset_Breakpoint()
963        db1 = Bdb()
964        fname = db1.canonic(__file__)
965        db1.set_break(__file__, 1)
966        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
967
968        db2 = Bdb()
969        db2.set_break(__file__, 2)
970        db2.set_break(__file__, 3)
971        db2.set_break(__file__, 4)
972        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
973        self.assertEqual(db2.get_all_breaks(), {fname: [1, 2, 3, 4]})
974        db2.clear_break(__file__, 1)
975        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
976        self.assertEqual(db2.get_all_breaks(), {fname: [2, 3, 4]})
977
978        db3 = Bdb()
979        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
980        self.assertEqual(db2.get_all_breaks(), {fname: [2, 3, 4]})
981        self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
982        db2.clear_break(__file__, 2)
983        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
984        self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
985        self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
986
987        db4 = Bdb()
988        db4.set_break(__file__, 5)
989        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
990        self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
991        self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
992        self.assertEqual(db4.get_all_breaks(), {fname: [3, 4, 5]})
993        reset_Breakpoint()
994
995        db5 = Bdb()
996        db5.set_break(__file__, 6)
997        self.assertEqual(db1.get_all_breaks(), {fname: [1]})
998        self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
999        self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
1000        self.assertEqual(db4.get_all_breaks(), {fname: [3, 4, 5]})
1001        self.assertEqual(db5.get_all_breaks(), {fname: [6]})
1002
1003
1004class RunTestCase(BaseTestCase):
1005    """Test run, runeval and set_trace."""
1006
1007    def test_run_step(self):
1008        # Check that the bdb 'run' method stops at the first line event.
1009        code = """
1010            lno = 2
1011        """
1012        self.expect_set = [
1013            ('line', 2, '<module>'),   ('step', ),
1014            ('return', 2, '<module>'), ('quit', ),
1015        ]
1016        with TracerRun(self) as tracer:
1017            tracer.run(compile(textwrap.dedent(code), '<string>', 'exec'))
1018
1019    def test_runeval_step(self):
1020        # Test bdb 'runeval'.
1021        code = """
1022            def main():
1023                lno = 3
1024        """
1025        modules = { TEST_MODULE: code }
1026        with create_modules(modules):
1027            self.expect_set = [
1028                ('line', 1, '<module>'),   ('step', ),
1029                ('call', 2, 'main'),       ('step', ),
1030                ('line', 3, 'main'),       ('step', ),
1031                ('return', 3, 'main'),     ('step', ),
1032                ('return', 1, '<module>'), ('quit', ),
1033            ]
1034            import test_module_for_bdb
1035            with TracerRun(self) as tracer:
1036                tracer.runeval('test_module_for_bdb.main()', globals(), locals())
1037
1038class IssuesTestCase(BaseTestCase):
1039    """Test fixed bdb issues."""
1040
1041    def test_step_at_return_with_no_trace_in_caller(self):
1042        # Issue #13183.
1043        # Check that the tracer does step into the caller frame when the
1044        # trace function is not set in that frame.
1045        code_1 = """
1046            from test_module_for_bdb_2 import func
1047            def main():
1048                func()
1049                lno = 5
1050        """
1051        code_2 = """
1052            def func():
1053                lno = 3
1054        """
1055        modules = {
1056            TEST_MODULE: code_1,
1057            'test_module_for_bdb_2': code_2,
1058        }
1059        with create_modules(modules):
1060            self.expect_set = [
1061                ('line', 2, 'tfunc_import'),
1062                    break_in_func('func', 'test_module_for_bdb_2.py'),
1063                ('None', 2, 'tfunc_import'),      ('continue', ),
1064                ('line', 3, 'func', ({1:1}, [])), ('step', ),
1065                ('return', 3, 'func'),            ('step', ),
1066                ('line', 5, 'main'),              ('quit', ),
1067            ]
1068            with TracerRun(self) as tracer:
1069                tracer.runcall(tfunc_import)
1070
1071    def test_next_until_return_in_generator(self):
1072        # Issue #16596.
1073        # Check that set_next(), set_until() and set_return() do not treat the
1074        # `yield` and `yield from` statements as if they were returns and stop
1075        # instead in the current frame.
1076        code = """
1077            def test_gen():
1078                yield 0
1079                lno = 4
1080                return 123
1081
1082            def main():
1083                it = test_gen()
1084                next(it)
1085                next(it)
1086                lno = 11
1087        """
1088        modules = { TEST_MODULE: code }
1089        for set_type in ('next', 'until', 'return'):
1090            with self.subTest(set_type=set_type):
1091                with create_modules(modules):
1092                    self.expect_set = [
1093                        ('line', 2, 'tfunc_import'),
1094                            break_in_func('test_gen', TEST_MODULE_FNAME),
1095                        ('None', 2, 'tfunc_import'),          ('continue', ),
1096                        ('line', 3, 'test_gen', ({1:1}, [])), (set_type, ),
1097                    ]
1098
1099                    if set_type == 'return':
1100                        self.expect_set.extend(
1101                            [('exception', 10, 'main', StopIteration), ('step',),
1102                             ('return', 10, 'main'),                   ('quit', ),
1103                            ]
1104                        )
1105                    else:
1106                        self.expect_set.extend(
1107                            [('line', 4, 'test_gen'), ('quit', ),]
1108                        )
1109                    with TracerRun(self) as tracer:
1110                        tracer.runcall(tfunc_import)
1111
1112    def test_next_command_in_generator_for_loop(self):
1113        # Issue #16596.
1114        code = """
1115            def test_gen():
1116                yield 0
1117                lno = 4
1118                yield 1
1119                return 123
1120
1121            def main():
1122                for i in test_gen():
1123                    lno = 10
1124                lno = 11
1125        """
1126        modules = { TEST_MODULE: code }
1127        with create_modules(modules):
1128            self.expect_set = [
1129                ('line', 2, 'tfunc_import'),
1130                    break_in_func('test_gen', TEST_MODULE_FNAME),
1131                ('None', 2, 'tfunc_import'),             ('continue', ),
1132                ('line', 3, 'test_gen', ({1:1}, [])),    ('next', ),
1133                ('line', 4, 'test_gen'),                 ('next', ),
1134                ('line', 5, 'test_gen'),                 ('next', ),
1135                ('line', 6, 'test_gen'),                 ('next', ),
1136                ('exception', 9, 'main', StopIteration), ('step', ),
1137                ('line', 11, 'main'),                    ('quit', ),
1138
1139            ]
1140            with TracerRun(self) as tracer:
1141                tracer.runcall(tfunc_import)
1142
1143    def test_next_command_in_generator_with_subiterator(self):
1144        # Issue #16596.
1145        code = """
1146            def test_subgen():
1147                yield 0
1148                return 123
1149
1150            def test_gen():
1151                x = yield from test_subgen()
1152                return 456
1153
1154            def main():
1155                for i in test_gen():
1156                    lno = 12
1157                lno = 13
1158        """
1159        modules = { TEST_MODULE: code }
1160        with create_modules(modules):
1161            self.expect_set = [
1162                ('line', 2, 'tfunc_import'),
1163                    break_in_func('test_gen', TEST_MODULE_FNAME),
1164                ('None', 2, 'tfunc_import'),              ('continue', ),
1165                ('line', 7, 'test_gen', ({1:1}, [])),     ('next', ),
1166                ('line', 8, 'test_gen'),                  ('next', ),
1167                ('exception', 11, 'main', StopIteration), ('step', ),
1168                ('line', 13, 'main'),                     ('quit', ),
1169
1170            ]
1171            with TracerRun(self) as tracer:
1172                tracer.runcall(tfunc_import)
1173
1174    def test_return_command_in_generator_with_subiterator(self):
1175        # Issue #16596.
1176        code = """
1177            def test_subgen():
1178                yield 0
1179                return 123
1180
1181            def test_gen():
1182                x = yield from test_subgen()
1183                return 456
1184
1185            def main():
1186                for i in test_gen():
1187                    lno = 12
1188                lno = 13
1189        """
1190        modules = { TEST_MODULE: code }
1191        with create_modules(modules):
1192            self.expect_set = [
1193                ('line', 2, 'tfunc_import'),
1194                    break_in_func('test_subgen', TEST_MODULE_FNAME),
1195                ('None', 2, 'tfunc_import'),                  ('continue', ),
1196                ('line', 3, 'test_subgen', ({1:1}, [])),      ('return', ),
1197                ('exception', 7, 'test_gen', StopIteration),  ('return', ),
1198                ('exception', 11, 'main', StopIteration),     ('step', ),
1199                ('line', 13, 'main'),                         ('quit', ),
1200
1201            ]
1202            with TracerRun(self) as tracer:
1203                tracer.runcall(tfunc_import)
1204
1205
1206class TestRegressions(unittest.TestCase):
1207    def test_format_stack_entry_no_lineno(self):
1208        # See gh-101517
1209        self.assertIn('Warning: lineno is None',
1210                      Bdb().format_stack_entry((sys._getframe(), None)))
1211
1212
1213if __name__ == "__main__":
1214    unittest.main()
1215