1"""
2Test that we handle breakpoints on consecutive instructions correctly.
3"""
4
5
6import lldb
7from lldbsuite.test.decorators import *
8from lldbsuite.test.lldbtest import *
9from lldbsuite.test import lldbutil
10
11
12class ConsecutiveBreakpointsTestCase(TestBase):
13    def prepare_test(self):
14        self.build()
15
16        (
17            self.target,
18            self.process,
19            self.thread,
20            bkpt,
21        ) = lldbutil.run_to_source_breakpoint(
22            self, "Set breakpoint here", lldb.SBFileSpec("main.cpp")
23        )
24
25        # Set breakpoint to the next instruction
26        frame = self.thread.GetFrameAtIndex(0)
27
28        address = frame.GetPCAddress()
29        instructions = self.target.ReadInstructions(address, 2)
30        self.assertEqual(len(instructions), 2)
31        self.bkpt_address = instructions[1].GetAddress()
32        self.breakpoint2 = self.target.BreakpointCreateByAddress(
33            self.bkpt_address.GetLoadAddress(self.target)
34        )
35        self.assertTrue(
36            self.breakpoint2 and self.breakpoint2.GetNumLocations() == 1,
37            VALID_BREAKPOINT,
38        )
39
40    def finish_test(self):
41        # Run the process until termination
42        self.process.Continue()
43        self.assertState(self.process.GetState(), lldb.eStateExited)
44
45    @no_debug_info_test
46    def test_continue(self):
47        """Test that continue stops at the second breakpoint."""
48        self.prepare_test()
49
50        self.process.Continue()
51        self.assertState(self.process.GetState(), lldb.eStateStopped)
52        # We should be stopped at the second breakpoint
53        self.thread = lldbutil.get_one_thread_stopped_at_breakpoint(
54            self.process, self.breakpoint2
55        )
56        self.assertIsNotNone(
57            self.thread, "Expected one thread to be stopped at breakpoint 2"
58        )
59
60        self.finish_test()
61
62    @no_debug_info_test
63    def test_single_step(self):
64        """Test that single step stops at the second breakpoint."""
65        self.prepare_test()
66
67        step_over = False
68        self.thread.StepInstruction(step_over)
69
70        self.assertState(self.process.GetState(), lldb.eStateStopped)
71        self.assertEqual(
72            self.thread.GetFrameAtIndex(0).GetPCAddress().GetLoadAddress(self.target),
73            self.bkpt_address.GetLoadAddress(self.target),
74        )
75        self.thread = lldbutil.get_one_thread_stopped_at_breakpoint(
76            self.process, self.breakpoint2
77        )
78        self.assertIsNotNone(
79            self.thread, "Expected one thread to be stopped at breakpoint 2"
80        )
81
82        self.finish_test()
83
84    @no_debug_info_test
85    def test_single_step_thread_specific(self):
86        """Test that single step stops, even though the second breakpoint is not valid."""
87        self.prepare_test()
88
89        # Choose a thread other than the current one. A non-existing thread is
90        # fine.
91        thread_index = self.process.GetNumThreads() + 1
92        self.assertFalse(self.process.GetThreadAtIndex(thread_index).IsValid())
93        self.breakpoint2.SetThreadIndex(thread_index)
94
95        step_over = False
96        self.thread.StepInstruction(step_over)
97
98        self.assertState(self.process.GetState(), lldb.eStateStopped)
99        self.assertEqual(
100            self.thread.GetFrameAtIndex(0).GetPCAddress().GetLoadAddress(self.target),
101            self.bkpt_address.GetLoadAddress(self.target),
102        )
103        self.assertEqual(
104            self.thread.GetStopReason(),
105            lldb.eStopReasonPlanComplete,
106            "Stop reason should be 'plan complete'",
107        )
108
109        self.finish_test()
110