1"""
2Test that breakpoints (reason = breakpoint) have more priority than
3plan completion (reason = step in/out/over) when reporting stop reason after step,
4in particular 'step out' and 'step over', and in addition 'step in'.
5Check for correct StopReason when stepping to the line with breakpoint,
6which should be eStopReasonBreakpoint in general,
7and eStopReasonPlanComplete when breakpoint's condition fails or it is disabled.
8"""
9
10
11import unittest
12import lldb
13from lldbsuite.test.decorators import *
14from lldbsuite.test.lldbtest import *
15from lldbsuite.test import lldbutil
16
17
18class ThreadPlanUserBreakpointsTestCase(TestBase):
19    def setUp(self):
20        TestBase.setUp(self)
21
22        # Build and run to starting breakpoint
23        self.build()
24        src = lldb.SBFileSpec("main.cpp")
25        (self.target, self.process, self.thread, _) = lldbutil.run_to_source_breakpoint(
26            self, "// Start from here", src
27        )
28
29        # Setup two more breakpoints
30        self.breakpoints = [
31            self.target.BreakpointCreateBySourceRegex("breakpoint_%i" % i, src)
32            for i in range(2)
33        ]
34        self.assertTrue(
35            all(bp and bp.GetNumLocations() == 1 for bp in self.breakpoints),
36            VALID_BREAKPOINT,
37        )
38
39    def check_correct_stop_reason(self, breakpoint_idx, condition):
40        self.assertState(self.process.GetState(), lldb.eStateStopped)
41        if condition:
42            # All breakpoints active, stop reason is breakpoint
43            thread1 = lldbutil.get_one_thread_stopped_at_breakpoint(
44                self.process, self.breakpoints[breakpoint_idx]
45            )
46            self.assertEqual(
47                self.thread, thread1, "Didn't stop at breakpoint %i." % breakpoint_idx
48            )
49        else:
50            # Breakpoints are inactive, stop reason is plan complete
51            self.assertEqual(
52                self.thread.GetStopReason(),
53                lldb.eStopReasonPlanComplete,
54                "Expected stop reason to be step into/over/out for inactive breakpoint %i line."
55                % breakpoint_idx,
56            )
57
58    def change_breakpoints(self, action):
59        for bp in self.breakpoints:
60            action(bp)
61
62    def check_thread_plan_user_breakpoint(self, condition, set_up_breakpoint_func):
63        # Make breakpoints active/inactive in different ways
64        self.change_breakpoints(lambda bp: set_up_breakpoint_func(condition, bp))
65
66        self.thread.StepInto()
67        # We should be stopped at the breakpoint_0 line with the correct stop reason
68        self.check_correct_stop_reason(0, condition)
69
70        # This step-over creates a step-out from `func_1` plan
71        self.thread.StepOver()
72        # We should be stopped at the breakpoint_1 line with the correct stop reason
73        self.check_correct_stop_reason(1, condition)
74
75        # Check explicit step-out
76        # Make sure we install the breakpoint at the right address:
77        # step-out might stop on different lines, if the compiler
78        # did or did not emit more instructions after the return
79        return_addr = self.thread.GetFrameAtIndex(1).GetPC()
80        step_out_breakpoint = self.target.BreakpointCreateByAddress(return_addr)
81        self.assertTrue(step_out_breakpoint, VALID_BREAKPOINT)
82        set_up_breakpoint_func(condition, step_out_breakpoint)
83        self.breakpoints.append(step_out_breakpoint)
84        self.thread.StepOut()
85        # We should be stopped somewhere in the main frame with the correct stop reason
86        self.check_correct_stop_reason(2, condition)
87
88        # Run the process until termination
89        self.process.Continue()
90        self.assertState(self.process.GetState(), lldb.eStateExited)
91
92    def set_up_breakpoints_condition(self, condition, bp):
93        # Set breakpoint condition to true/false
94        conditionStr = "true" if condition else "false"
95        bp.SetCondition(conditionStr)
96
97    def set_up_breakpoints_enable(self, condition, bp):
98        # Enable/disable breakpoint
99        bp.SetEnabled(condition)
100
101    def set_up_breakpoints_callback(self, condition, bp):
102        # Set breakpoint callback to return True/False
103        bp.SetScriptCallbackBody("return %s" % condition)
104
105    def test_thread_plan_user_breakpoint_conditional_active(self):
106        # Test with breakpoints having true condition
107        self.check_thread_plan_user_breakpoint(
108            condition=True, set_up_breakpoint_func=self.set_up_breakpoints_condition
109        )
110
111    def test_thread_plan_user_breakpoint_conditional_inactive(self):
112        # Test with breakpoints having false condition
113        self.check_thread_plan_user_breakpoint(
114            condition=False, set_up_breakpoint_func=self.set_up_breakpoints_condition
115        )
116
117    def test_thread_plan_user_breakpoint_unconditional_active(self):
118        # Test with breakpoints enabled unconditionally
119        self.check_thread_plan_user_breakpoint(
120            condition=True, set_up_breakpoint_func=self.set_up_breakpoints_enable
121        )
122
123    def test_thread_plan_user_breakpoint_unconditional_inactive(self):
124        # Test with breakpoints disabled unconditionally
125        self.check_thread_plan_user_breakpoint(
126            condition=False, set_up_breakpoint_func=self.set_up_breakpoints_enable
127        )
128
129    def test_thread_plan_user_breakpoint_callback_active(self):
130        # Test with breakpoints with callback that returns 'True'
131        self.check_thread_plan_user_breakpoint(
132            condition=True, set_up_breakpoint_func=self.set_up_breakpoints_callback
133        )
134
135    def test_thread_plan_user_breakpoint_callback_inactive(self):
136        # Test with breakpoints with callback that returns 'False'
137        self.check_thread_plan_user_breakpoint(
138            condition=False, set_up_breakpoint_func=self.set_up_breakpoints_callback
139        )
140