1""" 2Test lldb-dap setBreakpoints request 3""" 4 5 6import dap_server 7from lldbsuite.test.decorators import * 8from lldbsuite.test.lldbtest import * 9from lldbsuite.test import lldbutil 10import lldbdap_testcase 11import os 12 13 14class TestDAP_breakpointEvents(lldbdap_testcase.DAPTestCaseBase): 15 @skipIfWindows 16 @skipUnlessDarwin 17 @expectedFailureAll(macos_version=[">=", "13.0"]) 18 def test_breakpoint_events(self): 19 """ 20 This test sets a breakpoint in a shared library and runs and stops 21 at the entry point of a program. When we stop at the entry point, 22 the shared library won't be loaded yet. At this point the 23 breakpoint should set itself, but not be verified because no 24 locations are resolved. We will then continue and expect to get a 25 breakpoint event that informs us that the breakpoint in the shared 26 library is "changed" and the correct line number should be 27 supplied. We also set a breakpoint using a LLDB command using the 28 "preRunCommands" when launching our program. Any breakpoints set via 29 the command interpreter should not be have breakpoint events sent 30 back to VS Code as the UI isn't able to add new breakpoints to 31 their UI. Code has been added that tags breakpoints set from VS Code 32 DAP packets so we know the IDE knows about them. If VS Code is ever 33 able to register breakpoints that aren't initially set in the GUI, 34 then we will need to revise this. 35 """ 36 main_source_basename = "main.cpp" 37 main_source_path = os.path.join(os.getcwd(), main_source_basename) 38 foo_source_basename = "foo.cpp" 39 foo_source_path = os.path.join(os.getcwd(), foo_source_basename) 40 main_bp_line = line_number("main.cpp", "main breakpoint 1") 41 foo_bp1_line = line_number("foo.cpp", "foo breakpoint 1") 42 foo_bp2_line = line_number("foo.cpp", "foo breakpoint 2") 43 44 # Visual Studio Code Debug Adaptors have no way to specify the file 45 # without launching or attaching to a process, so we must start a 46 # process in order to be able to set breakpoints. 47 program = self.getBuildArtifact("a.out") 48 49 # Set a breakpoint after creating the target by running a command line 50 # command. It will eventually resolve and cause a breakpoint changed 51 # event to be sent to lldb-dap. We want to make sure we don't send a 52 # breakpoint any breakpoints that were set from the command line. 53 # Breakpoints that are set via the VS code DAP packets will be 54 # registered and marked with a special keyword to ensure we deliver 55 # breakpoint events for these breakpoints but not for ones that are not 56 # set via the command interpreter. 57 bp_command = "breakpoint set --file foo.cpp --line %u" % (foo_bp2_line) 58 self.build_and_launch(program, stopOnEntry=True, preRunCommands=[bp_command]) 59 main_bp_id = 0 60 foo_bp_id = 0 61 # Set breakpoints and verify that they got set correctly 62 dap_breakpoint_ids = [] 63 response = self.dap_server.request_setBreakpoints( 64 main_source_path, [main_bp_line] 65 ) 66 if response: 67 breakpoints = response["body"]["breakpoints"] 68 for breakpoint in breakpoints: 69 main_bp_id = breakpoint["id"] 70 dap_breakpoint_ids.append("%i" % (main_bp_id)) 71 # line = breakpoint['line'] 72 self.assertTrue( 73 breakpoint["verified"], "expect main breakpoint to be verified" 74 ) 75 76 response = self.dap_server.request_setBreakpoints( 77 foo_source_path, [foo_bp1_line] 78 ) 79 if response: 80 breakpoints = response["body"]["breakpoints"] 81 for breakpoint in breakpoints: 82 foo_bp_id = breakpoint["id"] 83 dap_breakpoint_ids.append("%i" % (foo_bp_id)) 84 self.assertFalse( 85 breakpoint["verified"], "expect foo breakpoint to not be verified" 86 ) 87 88 # Get the stop at the entry point 89 self.continue_to_next_stop() 90 91 # We are now stopped at the entry point to the program. Shared 92 # libraries are not loaded yet (at least on macOS they aren't) and any 93 # breakpoints set in foo.cpp should not be resolved. 94 self.assertEqual( 95 len(self.dap_server.breakpoint_events), 96 0, 97 "no breakpoint events when stopped at entry point", 98 ) 99 100 # Continue to the breakpoint 101 self.continue_to_breakpoints(dap_breakpoint_ids) 102 103 # Make sure we only get an event for the breakpoint we set via a call 104 # to self.dap_server.request_setBreakpoints(...), not the breakpoint 105 # we set with with a LLDB command in preRunCommands. 106 self.assertEqual( 107 len(self.dap_server.breakpoint_events), 108 1, 109 "make sure we got a breakpoint event", 110 ) 111 event = self.dap_server.breakpoint_events[0] 112 # Verify the details of the breakpoint changed notification. 113 body = event["body"] 114 self.assertEqual( 115 body["reason"], "changed", "breakpoint event is says breakpoint is changed" 116 ) 117 breakpoint = body["breakpoint"] 118 self.assertTrue( 119 breakpoint["verified"], "breakpoint event is says it is verified" 120 ) 121 self.assertEqual( 122 breakpoint["id"], 123 foo_bp_id, 124 "breakpoint event is for breakpoint %i" % (foo_bp_id), 125 ) 126 self.assertTrue( 127 "line" in breakpoint and breakpoint["line"] > 0, 128 "breakpoint event is has a line number", 129 ) 130 self.assertNotIn( 131 "source", breakpoint, "breakpoint event should not return a source object" 132 ) 133