1""" 2Test lldb-vscode setBreakpoints request 3""" 4 5 6import unittest2 7import vscode 8import shutil 9from lldbsuite.test.decorators import * 10from lldbsuite.test.lldbtest import * 11from lldbsuite.test import lldbutil 12import lldbvscode_testcase 13import os 14 15 16class TestVSCode_setBreakpoints(lldbvscode_testcase.VSCodeTestCaseBase): 17 18 mydir = TestBase.compute_mydir(__file__) 19 20 def setUp(self): 21 lldbvscode_testcase.VSCodeTestCaseBase.setUp(self) 22 23 self.main_basename = 'main-copy.cpp' 24 self.main_path = os.path.realpath(self.getBuildArtifact(self.main_basename)) 25 26 @skipIfWindows 27 @skipIfRemote 28 def test_source_map(self): 29 """ 30 This test simulates building two files in a folder, and then moving 31 each source to a different folder. Then, the debug session is started 32 with the corresponding source maps to have breakpoints and frames 33 working. 34 """ 35 self.build_and_create_debug_adaptor() 36 37 other_basename = 'other-copy.c' 38 other_path = self.getBuildArtifact(other_basename) 39 40 source_folder = os.path.dirname(self.main_path) 41 42 new_main_folder = os.path.join(source_folder, 'moved_main') 43 new_other_folder = os.path.join(source_folder, 'moved_other') 44 45 new_main_path = os.path.join(new_main_folder, self.main_basename) 46 new_other_path = os.path.join(new_other_folder, other_basename) 47 48 # move the sources 49 os.mkdir(new_main_folder) 50 os.mkdir(new_other_folder) 51 shutil.move(self.main_path, new_main_path) 52 shutil.move(other_path, new_other_path) 53 54 main_line = line_number('main.cpp', 'break 12') 55 other_line = line_number('other.c', 'break other') 56 57 program = self.getBuildArtifact("a.out") 58 source_map = [ 59 [source_folder, new_main_folder], 60 [source_folder, new_other_folder], 61 ] 62 self.launch(program, sourceMap=source_map) 63 64 # breakpoint in main.cpp 65 response = self.vscode.request_setBreakpoints(new_main_path, [main_line]) 66 breakpoints = response['body']['breakpoints'] 67 self.assertEquals(len(breakpoints), 1) 68 breakpoint = breakpoints[0] 69 self.assertEqual(breakpoint['line'], main_line) 70 self.assertTrue(breakpoint['verified']) 71 self.assertEqual(self.main_basename, breakpoint['source']['name']) 72 self.assertEqual(new_main_path, breakpoint['source']['path']) 73 74 # 2nd breakpoint, which is from a dynamically loaded library 75 response = self.vscode.request_setBreakpoints(new_other_path, [other_line]) 76 breakpoints = response['body']['breakpoints'] 77 breakpoint = breakpoints[0] 78 self.assertEqual(breakpoint['line'], other_line) 79 self.assertFalse(breakpoint['verified']) 80 self.assertEqual(other_basename, breakpoint['source']['name']) 81 self.assertEqual(new_other_path, breakpoint['source']['path']) 82 other_breakpoint_id = breakpoint['id'] 83 84 self.vscode.request_continue() 85 self.verify_breakpoint_hit([other_breakpoint_id]) 86 87 # 2nd breakpoint again, which should be valid at this point 88 response = self.vscode.request_setBreakpoints(new_other_path, [other_line]) 89 breakpoints = response['body']['breakpoints'] 90 breakpoint = breakpoints[0] 91 self.assertEqual(breakpoint['line'], other_line) 92 self.assertTrue(breakpoint['verified']) 93 self.assertEqual(other_basename, breakpoint['source']['name']) 94 self.assertEqual(new_other_path, breakpoint['source']['path']) 95 96 # now we check the stack trace making sure that we got mapped source paths 97 frames = self.vscode.request_stackTrace()['body']['stackFrames'] 98 99 self.assertEqual(frames[0]['source']['name'], other_basename) 100 self.assertEqual(frames[0]['source']['path'], new_other_path) 101 102 self.assertEqual(frames[1]['source']['name'], self.main_basename) 103 self.assertEqual(frames[1]['source']['path'], new_main_path) 104 105 106 @skipIfWindows 107 @skipIfRemote 108 def test_set_and_clear(self): 109 '''Tests setting and clearing source file and line breakpoints. 110 This packet is a bit tricky on the debug adaptor side since there 111 is no "clearBreakpoints" packet. Source file and line breakpoints 112 are set by sending a "setBreakpoints" packet with a source file 113 specified and zero or more source lines. If breakpoints have been 114 set in the source file before, any existing breakpoints must remain 115 set, and any new breakpoints must be created, and any breakpoints 116 that were in previous requests and are not in the current request 117 must be removed. This function tests this setting and clearing 118 and makes sure things happen correctly. It doesn't test hitting 119 breakpoints and the functionality of each breakpoint, like 120 'conditions' and 'hitCondition' settings.''' 121 first_line = line_number('main.cpp', 'break 12') 122 second_line = line_number('main.cpp', 'break 13') 123 third_line = line_number('main.cpp', 'break 14') 124 lines = [first_line, third_line, second_line] 125 126 # Visual Studio Code Debug Adaptors have no way to specify the file 127 # without launching or attaching to a process, so we must start a 128 # process in order to be able to set breakpoints. 129 program = self.getBuildArtifact("a.out") 130 self.build_and_launch(program) 131 132 # Set 3 breakpoints and verify that they got set correctly 133 response = self.vscode.request_setBreakpoints(self.main_path, lines) 134 line_to_id = {} 135 if response: 136 breakpoints = response['body']['breakpoints'] 137 self.assertEquals(len(breakpoints), len(lines), 138 "expect %u source breakpoints" % (len(lines))) 139 for (breakpoint, index) in zip(breakpoints, range(len(lines))): 140 line = breakpoint['line'] 141 self.assertTrue(line, lines[index]) 142 # Store the "id" of the breakpoint that was set for later 143 line_to_id[line] = breakpoint['id'] 144 self.assertIn(line, lines, "line expected in lines array") 145 self.assertTrue(breakpoint['verified'], 146 "expect breakpoint verified") 147 148 # There is no breakpoint delete packet, clients just send another 149 # setBreakpoints packet with the same source file with fewer lines. 150 # Below we remove the second line entry and call the setBreakpoints 151 # function again. We want to verify that any breakpoints that were set 152 # before still have the same "id". This means we didn't clear the 153 # breakpoint and set it again at the same location. We also need to 154 # verify that the second line location was actually removed. 155 lines.remove(second_line) 156 # Set 2 breakpoints and verify that the previous breakpoints that were 157 # set above are still set. 158 response = self.vscode.request_setBreakpoints(self.main_path, lines) 159 if response: 160 breakpoints = response['body']['breakpoints'] 161 self.assertEquals(len(breakpoints), len(lines), 162 "expect %u source breakpoints" % (len(lines))) 163 for (breakpoint, index) in zip(breakpoints, range(len(lines))): 164 line = breakpoint['line'] 165 self.assertTrue(line, lines[index]) 166 # Verify the same breakpoints are still set within LLDB by 167 # making sure the breakpoint ID didn't change 168 self.assertEquals(line_to_id[line], breakpoint['id'], 169 "verify previous breakpoints stayed the same") 170 self.assertIn(line, lines, "line expected in lines array") 171 self.assertTrue(breakpoint['verified'], 172 "expect breakpoint still verified") 173 174 # Now get the full list of breakpoints set in the target and verify 175 # we have only 2 breakpoints set. The response above could have told 176 # us about 2 breakpoints, but we want to make sure we don't have the 177 # third one still set in the target 178 response = self.vscode.request_testGetTargetBreakpoints() 179 if response: 180 breakpoints = response['body']['breakpoints'] 181 self.assertEquals(len(breakpoints), len(lines), 182 "expect %u source breakpoints" % (len(lines))) 183 for breakpoint in breakpoints: 184 line = breakpoint['line'] 185 # Verify the same breakpoints are still set within LLDB by 186 # making sure the breakpoint ID didn't change 187 self.assertEquals(line_to_id[line], breakpoint['id'], 188 "verify previous breakpoints stayed the same") 189 self.assertIn(line, lines, "line expected in lines array") 190 self.assertTrue(breakpoint['verified'], 191 "expect breakpoint still verified") 192 193 # Now clear all breakpoints for the source file by passing down an 194 # empty lines array 195 lines = [] 196 response = self.vscode.request_setBreakpoints(self.main_path, lines) 197 if response: 198 breakpoints = response['body']['breakpoints'] 199 self.assertEquals(len(breakpoints), len(lines), 200 "expect %u source breakpoints" % (len(lines))) 201 202 # Verify with the target that all breakpoints have been cleared 203 response = self.vscode.request_testGetTargetBreakpoints() 204 if response: 205 breakpoints = response['body']['breakpoints'] 206 self.assertEquals(len(breakpoints), len(lines), 207 "expect %u source breakpoints" % (len(lines))) 208 209 # Now set a breakpoint again in the same source file and verify it 210 # was added. 211 lines = [second_line] 212 response = self.vscode.request_setBreakpoints(self.main_path, lines) 213 if response: 214 breakpoints = response['body']['breakpoints'] 215 self.assertEquals(len(breakpoints), len(lines), 216 "expect %u source breakpoints" % (len(lines))) 217 for breakpoint in breakpoints: 218 line = breakpoint['line'] 219 self.assertIn(line, lines, "line expected in lines array") 220 self.assertTrue(breakpoint['verified'], 221 "expect breakpoint still verified") 222 223 # Now get the full list of breakpoints set in the target and verify 224 # we have only 2 breakpoints set. The response above could have told 225 # us about 2 breakpoints, but we want to make sure we don't have the 226 # third one still set in the target 227 response = self.vscode.request_testGetTargetBreakpoints() 228 if response: 229 breakpoints = response['body']['breakpoints'] 230 self.assertEquals(len(breakpoints), len(lines), 231 "expect %u source breakpoints" % (len(lines))) 232 for breakpoint in breakpoints: 233 line = breakpoint['line'] 234 self.assertIn(line, lines, "line expected in lines array") 235 self.assertTrue(breakpoint['verified'], 236 "expect breakpoint still verified") 237 238 @skipIfWindows 239 @skipIfRemote 240 def test_clear_breakpoints_unset_breakpoints(self): 241 '''Test clearing breakpoints like test_set_and_clear, but clear 242 breakpoints by omitting the breakpoints array instead of sending an 243 empty one.''' 244 lines = [line_number('main.cpp', 'break 12'), 245 line_number('main.cpp', 'break 13')] 246 247 # Visual Studio Code Debug Adaptors have no way to specify the file 248 # without launching or attaching to a process, so we must start a 249 # process in order to be able to set breakpoints. 250 program = self.getBuildArtifact("a.out") 251 self.build_and_launch(program) 252 253 # Set one breakpoint and verify that it got set correctly. 254 response = self.vscode.request_setBreakpoints(self.main_path, lines) 255 line_to_id = {} 256 breakpoints = response['body']['breakpoints'] 257 self.assertEquals(len(breakpoints), len(lines), 258 "expect %u source breakpoints" % (len(lines))) 259 for (breakpoint, index) in zip(breakpoints, range(len(lines))): 260 line = breakpoint['line'] 261 self.assertTrue(line, lines[index]) 262 # Store the "id" of the breakpoint that was set for later 263 line_to_id[line] = breakpoint['id'] 264 self.assertIn(line, lines, "line expected in lines array") 265 self.assertTrue(breakpoint['verified'], 266 "expect breakpoint verified") 267 268 # Now clear all breakpoints for the source file by not setting the 269 # lines array. 270 lines = None 271 response = self.vscode.request_setBreakpoints(self.main_path, lines) 272 breakpoints = response['body']['breakpoints'] 273 self.assertEquals(len(breakpoints), 0, "expect no source breakpoints") 274 275 # Verify with the target that all breakpoints have been cleared. 276 response = self.vscode.request_testGetTargetBreakpoints() 277 breakpoints = response['body']['breakpoints'] 278 self.assertEquals(len(breakpoints), 0, "expect no source breakpoints") 279 280 @skipIfWindows 281 @skipIfRemote 282 def test_functionality(self): 283 '''Tests hitting breakpoints and the functionality of a single 284 breakpoint, like 'conditions' and 'hitCondition' settings.''' 285 loop_line = line_number('main.cpp', '// break loop') 286 287 program = self.getBuildArtifact("a.out") 288 self.build_and_launch(program) 289 # Set a breakpoint at the loop line with no condition and no 290 # hitCondition 291 breakpoint_ids = self.set_source_breakpoints(self.main_path, [loop_line]) 292 self.assertEquals(len(breakpoint_ids), 1, "expect one breakpoint") 293 self.vscode.request_continue() 294 295 # Verify we hit the breakpoint we just set 296 self.verify_breakpoint_hit(breakpoint_ids) 297 298 # Make sure i is zero at first breakpoint 299 i = int(self.vscode.get_local_variable_value('i')) 300 self.assertEquals(i, 0, 'i != 0 after hitting breakpoint') 301 302 # Update the condition on our breakpoint 303 new_breakpoint_ids = self.set_source_breakpoints(self.main_path, 304 [loop_line], 305 condition="i==4") 306 self.assertEquals(breakpoint_ids, new_breakpoint_ids, 307 "existing breakpoint should have its condition " 308 "updated") 309 310 self.continue_to_breakpoints(breakpoint_ids) 311 i = int(self.vscode.get_local_variable_value('i')) 312 self.assertEquals(i, 4, 313 'i != 4 showing conditional works') 314 315 new_breakpoint_ids = self.set_source_breakpoints(self.main_path, 316 [loop_line], 317 hitCondition="2") 318 319 self.assertEquals(breakpoint_ids, new_breakpoint_ids, 320 "existing breakpoint should have its condition " 321 "updated") 322 323 # Continue with a hitCondition of 2 and expect it to skip 1 value 324 self.continue_to_breakpoints(breakpoint_ids) 325 i = int(self.vscode.get_local_variable_value('i')) 326 self.assertEquals(i, 6, 327 'i != 6 showing hitCondition works') 328 329 # continue after hitting our hitCondition and make sure it only goes 330 # up by 1 331 self.continue_to_breakpoints(breakpoint_ids) 332 i = int(self.vscode.get_local_variable_value('i')) 333 self.assertEquals(i, 7, 334 'i != 7 showing post hitCondition hits every time') 335