1"""
2Test the "process continue -b" option.
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class TestContinueToBkpts(TestBase):
13
14    NO_DEBUG_INFO_TESTCASE = True
15    mydir = TestBase.compute_mydir(__file__)
16
17    @add_test_categories(['pyapi'])
18    def test_continue_to_breakpoints(self):
19        """Test that the continue to breakpoints feature works correctly."""
20        self.build()
21        self.do_test_continue_to_breakpoint()
22
23    def setUp(self):
24        # Call super's setUp().
25        TestBase.setUp(self)
26        self.main_source_spec = lldb.SBFileSpec("main.c")
27
28    def continue_and_check(self, stop_list, bkpt_to_hit, loc_to_hit = 0):
29        """Build up a command that will run a continue -b commands using the breakpoints on stop_list, and
30           ensure that we hit bkpt_to_hit.
31           If loc_to_hit is not 0, also verify that we hit that location."""
32        command = "process continue"
33        for elem in stop_list:
34            command += " -b {0}".format(elem)
35        self.expect(command)
36        self.assertEqual(self.thread.stop_reason, lldb.eStopReasonBreakpoint, "Hit a breakpoint")
37        self.assertEqual(self.thread.GetStopReasonDataAtIndex(0), bkpt_to_hit, "Hit the right breakpoint")
38        if loc_to_hit != 0:
39            self.assertEqual(self.thread.GetStopReasonDataAtIndex(1), loc_to_hit, "Hit the right location")
40        for bkpt_id in self.bkpt_list:
41            bkpt = self.target.FindBreakpointByID(bkpt_id)
42            self.assertTrue(bkpt.IsValid(), "Breakpoint id's round trip")
43            if bkpt.MatchesName("disabled"):
44                self.assertFalse(bkpt.IsEnabled(), "Disabled breakpoints stay disabled: {0}".format(bkpt.GetID()))
45            else:
46                self.assertTrue(bkpt.IsEnabled(), "Enabled breakpoints stay enabled: {0}".format(bkpt.GetID()))
47        # Also do our multiple location one:
48        bkpt = self.target.FindBreakpointByID(self.multiple_loc_id)
49        self.assertTrue(bkpt.IsValid(), "Breakpoint with locations round trip")
50        for i in range(1,3):
51            loc = bkpt.FindLocationByID(i)
52            self.assertTrue(loc.IsValid(), "Locations round trip")
53            if i == 2:
54                self.assertTrue(loc.IsEnabled(), "Locations that were enabled stay enabled")
55            else:
56                self.assertFalse(loc.IsEnabled(), "Locations that were disabled stay disabled")
57
58    def do_test_continue_to_breakpoint(self):
59        """Test the continue to breakpoint feature."""
60        (self.target, process, self.thread, bkpt) = lldbutil.run_to_source_breakpoint(self,
61                                   "Stop here to get started", self.main_source_spec)
62
63        # Now set up all our breakpoints:
64        bkpt_pattern = "This is the {0} stop"
65        bkpt_elements = ["zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "nineth"]
66        disabled_bkpts = ["first", "eigth"]
67        bkpts_for_MyBKPT = ["first", "sixth", "nineth"]
68        self.bkpt_list = []
69        for elem in bkpt_elements:
70            bkpt = self.target.BreakpointCreateBySourceRegex(bkpt_pattern.format(elem), self.main_source_spec)
71            self.assertGreater(bkpt.GetNumLocations(), 0, "Found a bkpt match")
72            self.bkpt_list.append(bkpt.GetID())
73            bkpt.AddName(elem)
74            if elem in disabled_bkpts:
75                bkpt.AddName("disabled")
76                bkpt.SetEnabled(False)
77            if elem in bkpts_for_MyBKPT:
78                bkpt.AddName("MyBKPT")
79        # Also make one that has several locations, so we can test locations:
80        mult_bkpt = self.target.BreakpointCreateBySourceRegex(bkpt_pattern.format("(seventh|eighth|nineth)"), self.main_source_spec)
81        self.assertEqual(mult_bkpt.GetNumLocations(), 3, "Got three matches")
82        mult_bkpt.AddName("Locations")
83        # Disable all of these:
84        for i in range(1,4):
85            loc = mult_bkpt.FindLocationByID(i)
86            self.assertTrue(loc.IsValid(), "Location {0} is valid".format(i))
87            loc.SetEnabled(False)
88            self.assertFalse(loc.IsEnabled(), "Loc {0} wasn't disabled".format(i))
89        self.multiple_loc_id = mult_bkpt.GetID()
90
91        # First test out various error conditions
92
93        # All locations of the multiple_loc_id are disabled, so running to this should be an error:
94        self.expect("process continue -b {0}".format(self.multiple_loc_id), error=True, msg="Running to a disabled breakpoint by number")
95
96        # Now re-enable the middle one so we can run to it:
97        loc = mult_bkpt.FindLocationByID(2)
98        loc.SetEnabled(True)
99
100        self.expect("process continue -b {0}".format(self.bkpt_list[1]), error=True, msg="Running to a disabled breakpoint by number")
101        self.expect("process continue -b {0}.1".format(self.bkpt_list[1]), error=True, msg="Running to a location of a disabled breakpoint")
102        self.expect("process continue -b disabled", error=True, msg="Running to a disabled set of breakpoints")
103        self.expect("process continue -b {0}.{1}".format(self.multiple_loc_id, 1), error=True, msg="Running to a disabled breakpoint location")
104        self.expect("process continue -b {0}".format("THERE_ARE_NO_BREAKPOINTS_BY_THIS_NAME"), error=True, msg="Running to no such name")
105        self.expect("process continue -b {0}".format(1000), error=True, msg="Running to no such breakpoint")
106        self.expect("process continue -b {0}.{1}".format(self.multiple_loc_id, 1000), error=True, msg="Running to no such location")
107
108        # Now move forward, this time with breakpoint numbers.  First time we don't skip other bkpts.
109        bkpt = self.bkpt_list[0]
110        self.continue_and_check([str(bkpt)], bkpt)
111
112        # Now skip to the third stop, do it by name and supply one of the later breakpoints as well:
113        # This continue has to muck with the sync mode of the debugger, so let's make sure we
114        # put it back.  First try if it was in sync mode:
115        orig_async = self.dbg.GetAsync()
116        self.dbg.SetAsync(True)
117        self.continue_and_check([bkpt_elements[2], bkpt_elements[7]], self.bkpt_list[2])
118        after_value = self.dbg.GetAsync()
119        self.dbg.SetAsync(orig_async)
120        self.assertTrue(after_value, "Preserve async as True if it started that way")
121
122        # Now try a name that has several breakpoints.
123        # This time I'm also going to check that we put the debugger async mode back if
124        # if was False to begin with:
125        self.dbg.SetAsync(False)
126        self.continue_and_check(["MyBKPT"], self.bkpt_list[6])
127        after_value = self.dbg.GetAsync()
128        self.dbg.SetAsync(orig_async)
129        self.assertFalse(after_value, "Preserve async as False if it started that way")
130
131        # Now let's run to a particular location.  Also specify a breakpoint we've already hit:
132        self.continue_and_check([self.bkpt_list[0], self.multiple_loc_id], self.multiple_loc_id, 2)
133