1""" 2Test lldb-vscode setBreakpoints request 3""" 4 5 6import unittest2 7import vscode 8from lldbsuite.test.decorators import * 9from lldbsuite.test.lldbtest import * 10from lldbsuite.test import lldbutil 11import lldbvscode_testcase 12import time 13import os 14 15 16class TestVSCode_launch(lldbvscode_testcase.VSCodeTestCaseBase): 17 18 mydir = TestBase.compute_mydir(__file__) 19 20 @skipIfWindows 21 @skipIfDarwin # Flaky 22 @skipIfRemote 23 def test_default(self): 24 ''' 25 Tests the default launch of a simple program. No arguments, 26 environment, or anything else is specified. 27 ''' 28 program = self.getBuildArtifact("a.out") 29 self.build_and_launch(program) 30 self.continue_to_exit() 31 # Now get the STDOUT and verify our program argument is correct 32 output = self.get_stdout() 33 self.assertTrue(output and len(output) > 0, 34 "expect program output") 35 lines = output.splitlines() 36 self.assertTrue(program in lines[0], 37 "make sure program path is in first argument") 38 39 @skipIfWindows 40 @skipIfRemote 41 def test_termination(self): 42 ''' 43 Tests the correct termination of lldb-vscode upon a 'disconnect' 44 request. 45 ''' 46 self.create_debug_adaptor() 47 # The underlying lldb-vscode process must be alive 48 self.assertEqual(self.vscode.process.poll(), None) 49 50 # The lldb-vscode process should finish even though 51 # we didn't close the communication socket explicitly 52 self.vscode.request_disconnect() 53 54 # Wait until the underlying lldb-vscode process dies. 55 # We need to do this because the popen.wait function in python2.7 56 # doesn't have a timeout argument. 57 for _ in range(10): 58 time.sleep(1) 59 if self.vscode.process.poll() is not None: 60 break 61 # Check the return code 62 self.assertEqual(self.vscode.process.poll(), 0) 63 64 @skipIfWindows 65 @skipIfRemote 66 def test_stopOnEntry(self): 67 ''' 68 Tests the default launch of a simple program that stops at the 69 entry point instead of continuing. 70 ''' 71 program = self.getBuildArtifact("a.out") 72 self.build_and_launch(program, stopOnEntry=True) 73 self.set_function_breakpoints(['main']) 74 stopped_events = self.continue_to_next_stop() 75 for stopped_event in stopped_events: 76 if 'body' in stopped_event: 77 body = stopped_event['body'] 78 if 'reason' in body: 79 reason = body['reason'] 80 self.assertTrue( 81 reason != 'breakpoint', 82 'verify stop isn\'t "main" breakpoint') 83 84 @skipIfWindows 85 @skipIfRemote 86 def test_cwd(self): 87 ''' 88 Tests the default launch of a simple program with a current working 89 directory. 90 ''' 91 program = self.getBuildArtifact("a.out") 92 program_parent_dir = os.path.realpath( 93 os.path.dirname(os.path.dirname(program))) 94 self.build_and_launch(program, 95 cwd=program_parent_dir) 96 self.continue_to_exit() 97 # Now get the STDOUT and verify our program argument is correct 98 output = self.get_stdout() 99 self.assertTrue(output and len(output) > 0, 100 "expect program output") 101 lines = output.splitlines() 102 found = False 103 for line in lines: 104 if line.startswith('cwd = \"'): 105 quote_path = '"%s"' % (program_parent_dir) 106 found = True 107 self.assertTrue(quote_path in line, 108 "working directory '%s' not in '%s'" % ( 109 program_parent_dir, line)) 110 self.assertTrue(found, "verified program working directory") 111 112 @skipIfWindows 113 @skipIfRemote 114 def test_debuggerRoot(self): 115 ''' 116 Tests the "debuggerRoot" will change the working directory of 117 the lldb-vscode debug adaptor. 118 ''' 119 program = self.getBuildArtifact("a.out") 120 program_parent_dir = os.path.realpath( 121 os.path.dirname(os.path.dirname(program))) 122 commands = ['platform shell echo cwd = $PWD'] 123 self.build_and_launch(program, 124 debuggerRoot=program_parent_dir, 125 initCommands=commands) 126 output = self.get_console() 127 self.assertTrue(output and len(output) > 0, 128 "expect console output") 129 lines = output.splitlines() 130 prefix = 'cwd = ' 131 found = False 132 for line in lines: 133 if line.startswith(prefix): 134 found = True 135 self.assertEquals(program_parent_dir, line[len(prefix):], 136 "lldb-vscode working dir '%s' == '%s'" % ( 137 program_parent_dir, line[6:])) 138 self.assertTrue(found, "verified lldb-vscode working directory") 139 self.continue_to_exit() 140 141 @skipIfWindows 142 @skipIfRemote 143 def test_sourcePath(self): 144 ''' 145 Tests the "sourcePath" will set the target.source-map. 146 ''' 147 program = self.getBuildArtifact("a.out") 148 program_dir = os.path.dirname(program) 149 self.build_and_launch(program, 150 sourcePath=program_dir) 151 output = self.get_console() 152 self.assertTrue(output and len(output) > 0, 153 "expect console output") 154 lines = output.splitlines() 155 prefix = '(lldb) settings set target.source-map "." ' 156 found = False 157 for line in lines: 158 if line.startswith(prefix): 159 found = True 160 quoted_path = '"%s"' % (program_dir) 161 self.assertEquals(quoted_path, line[len(prefix):], 162 "lldb-vscode working dir %s == %s" % ( 163 quoted_path, line[6:])) 164 self.assertTrue(found, 'found "sourcePath" in console output') 165 self.continue_to_exit() 166 167 @skipIfWindows 168 @skipIfRemote 169 def test_disableSTDIO(self): 170 ''' 171 Tests the default launch of a simple program with STDIO disabled. 172 ''' 173 program = self.getBuildArtifact("a.out") 174 self.build_and_launch(program, 175 disableSTDIO=True) 176 self.continue_to_exit() 177 # Now get the STDOUT and verify our program argument is correct 178 output = self.get_stdout() 179 self.assertEquals(output, None, 180 "expect no program output") 181 182 @skipIfWindows 183 @skipIfLinux # shell argument expansion doesn't seem to work on Linux 184 @expectedFailureNetBSD 185 @skipIfRemote 186 def test_shellExpandArguments_enabled(self): 187 ''' 188 Tests the default launch of a simple program with shell expansion 189 enabled. 190 ''' 191 program = self.getBuildArtifact("a.out") 192 program_dir = os.path.dirname(program) 193 glob = os.path.join(program_dir, '*.out') 194 self.build_and_launch(program, args=[glob], shellExpandArguments=True) 195 self.continue_to_exit() 196 # Now get the STDOUT and verify our program argument is correct 197 output = self.get_stdout() 198 self.assertTrue(output and len(output) > 0, 199 "expect no program output") 200 lines = output.splitlines() 201 for line in lines: 202 quote_path = '"%s"' % (program) 203 if line.startswith("arg[1] ="): 204 self.assertTrue(quote_path in line, 205 'verify "%s" expanded to "%s"' % ( 206 glob, program)) 207 208 @skipIfWindows 209 @skipIfRemote 210 def test_shellExpandArguments_disabled(self): 211 ''' 212 Tests the default launch of a simple program with shell expansion 213 disabled. 214 ''' 215 program = self.getBuildArtifact("a.out") 216 program_dir = os.path.dirname(program) 217 glob = os.path.join(program_dir, '*.out') 218 self.build_and_launch(program, 219 args=[glob], 220 shellExpandArguments=False) 221 self.continue_to_exit() 222 # Now get the STDOUT and verify our program argument is correct 223 output = self.get_stdout() 224 self.assertTrue(output and len(output) > 0, 225 "expect no program output") 226 lines = output.splitlines() 227 for line in lines: 228 quote_path = '"%s"' % (glob) 229 if line.startswith("arg[1] ="): 230 self.assertTrue(quote_path in line, 231 'verify "%s" stayed to "%s"' % ( 232 glob, glob)) 233 234 @skipIfWindows 235 @skipIfRemote 236 def test_args(self): 237 ''' 238 Tests launch of a simple program with arguments 239 ''' 240 program = self.getBuildArtifact("a.out") 241 args = ["one", "with space", "'with single quotes'", 242 '"with double quotes"'] 243 self.build_and_launch(program, 244 args=args) 245 self.continue_to_exit() 246 247 # Now get the STDOUT and verify our arguments got passed correctly 248 output = self.get_stdout() 249 self.assertTrue(output and len(output) > 0, 250 "expect program output") 251 lines = output.splitlines() 252 # Skip the first argument that contains the program name 253 lines.pop(0) 254 # Make sure arguments we specified are correct 255 for (i, arg) in enumerate(args): 256 quoted_arg = '"%s"' % (arg) 257 self.assertTrue(quoted_arg in lines[i], 258 'arg[%i] "%s" not in "%s"' % (i+1, quoted_arg, lines[i])) 259 260 @skipIfWindows 261 @skipIfRemote 262 def test_environment(self): 263 ''' 264 Tests launch of a simple program with environment variables 265 ''' 266 program = self.getBuildArtifact("a.out") 267 env = ["NO_VALUE", "WITH_VALUE=BAR", "EMPTY_VALUE=", 268 "SPACE=Hello World"] 269 self.build_and_launch(program, 270 env=env) 271 self.continue_to_exit() 272 273 # Now get the STDOUT and verify our arguments got passed correctly 274 output = self.get_stdout() 275 self.assertTrue(output and len(output) > 0, 276 "expect program output") 277 lines = output.splitlines() 278 # Skip the all arguments so we have only environment vars left 279 while len(lines) and lines[0].startswith("arg["): 280 lines.pop(0) 281 # Make sure each environment variable in "env" is actually set in the 282 # program environment that was printed to STDOUT 283 for var in env: 284 found = False 285 for program_var in lines: 286 if var in program_var: 287 found = True 288 break 289 self.assertTrue(found, 290 '"%s" must exist in program environment (%s)' % ( 291 var, lines)) 292 293 @skipIfWindows 294 @skipIfRemote 295 def test_commands(self): 296 ''' 297 Tests the "initCommands", "preRunCommands", "stopCommands", 298 "terminateCommands" and "exitCommands" that can be passed during 299 launch. 300 301 "initCommands" are a list of LLDB commands that get executed 302 before the targt is created. 303 "preRunCommands" are a list of LLDB commands that get executed 304 after the target has been created and before the launch. 305 "stopCommands" are a list of LLDB commands that get executed each 306 time the program stops. 307 "exitCommands" are a list of LLDB commands that get executed when 308 the process exits 309 "terminateCommands" are a list of LLDB commands that get executed when 310 the debugger session terminates. 311 ''' 312 program = self.getBuildArtifact("a.out") 313 initCommands = ['target list', 'platform list'] 314 preRunCommands = ['image list a.out', 'image dump sections a.out'] 315 stopCommands = ['frame variable', 'bt'] 316 exitCommands = ['expr 2+3', 'expr 3+4'] 317 terminateCommands = ['expr 4+2'] 318 self.build_and_launch(program, 319 initCommands=initCommands, 320 preRunCommands=preRunCommands, 321 stopCommands=stopCommands, 322 exitCommands=exitCommands, 323 terminateCommands=terminateCommands) 324 325 # Get output from the console. This should contain both the 326 # "initCommands" and the "preRunCommands". 327 output = self.get_console() 328 # Verify all "initCommands" were found in console output 329 self.verify_commands('initCommands', output, initCommands) 330 # Verify all "preRunCommands" were found in console output 331 self.verify_commands('preRunCommands', output, preRunCommands) 332 333 source = 'main.c' 334 first_line = line_number(source, '// breakpoint 1') 335 second_line = line_number(source, '// breakpoint 2') 336 lines = [first_line, second_line] 337 338 # Set 2 breakpoints so we can verify that "stopCommands" get run as the 339 # breakpoints get hit 340 breakpoint_ids = self.set_source_breakpoints(source, lines) 341 self.assertEquals(len(breakpoint_ids), len(lines), 342 "expect correct number of breakpoints") 343 344 # Continue after launch and hit the first breakpoint. 345 # Get output from the console. This should contain both the 346 # "stopCommands" that were run after the first breakpoint was hit 347 self.continue_to_breakpoints(breakpoint_ids) 348 output = self.get_console(timeout=1.0) 349 self.verify_commands('stopCommands', output, stopCommands) 350 351 # Continue again and hit the second breakpoint. 352 # Get output from the console. This should contain both the 353 # "stopCommands" that were run after the second breakpoint was hit 354 self.continue_to_breakpoints(breakpoint_ids) 355 output = self.get_console(timeout=1.0) 356 self.verify_commands('stopCommands', output, stopCommands) 357 358 # Continue until the program exits 359 self.continue_to_exit() 360 # Get output from the console. This should contain both the 361 # "exitCommands" that were run after the second breakpoint was hit 362 # and the "terminateCommands" due to the debugging session ending 363 output = self.collect_console(duration=1.0) 364 self.verify_commands('exitCommands', output, exitCommands) 365 self.verify_commands('terminateCommands', output, terminateCommands) 366 367 @skipIfWindows 368 @skipIfRemote 369 def test_extra_launch_commands(self): 370 ''' 371 Tests the "luanchCommands" with extra launching settings 372 ''' 373 self.build_and_create_debug_adaptor() 374 program = self.getBuildArtifact("a.out") 375 376 source = 'main.c' 377 first_line = line_number(source, '// breakpoint 1') 378 second_line = line_number(source, '// breakpoint 2') 379 # Set target binary and 2 breakpoints 380 # then we can varify the "launchCommands" get run 381 # also we can verify that "stopCommands" get run as the 382 # breakpoints get hit 383 launchCommands = [ 384 'target create "%s"' % (program), 385 'br s -f main.c -l %d' % first_line, 386 'br s -f main.c -l %d' % second_line, 387 'process launch --stop-at-entry' 388 ] 389 390 initCommands = ['target list', 'platform list'] 391 preRunCommands = ['image list a.out', 'image dump sections a.out'] 392 stopCommands = ['frame variable', 'bt'] 393 exitCommands = ['expr 2+3', 'expr 3+4'] 394 self.launch(program, 395 initCommands=initCommands, 396 preRunCommands=preRunCommands, 397 stopCommands=stopCommands, 398 exitCommands=exitCommands, 399 launchCommands=launchCommands) 400 401 # Get output from the console. This should contain both the 402 # "initCommands" and the "preRunCommands". 403 output = self.get_console() 404 # Verify all "initCommands" were found in console output 405 self.verify_commands('initCommands', output, initCommands) 406 # Verify all "preRunCommands" were found in console output 407 self.verify_commands('preRunCommands', output, preRunCommands) 408 409 # Verify all "launchCommands" were founc in console output 410 # After execution, program should launch 411 self.verify_commands('launchCommands', output, launchCommands) 412 # Verify the "stopCommands" here 413 self.continue_to_next_stop() 414 output = self.get_console(timeout=1.0) 415 self.verify_commands('stopCommands', output, stopCommands) 416 417 # Continue and hit the second breakpoint. 418 # Get output from the console. This should contain both the 419 # "stopCommands" that were run after the first breakpoint was hit 420 self.continue_to_next_stop() 421 output = self.get_console(timeout=1.0) 422 self.verify_commands('stopCommands', output, stopCommands) 423 424 # Continue until the program exits 425 self.continue_to_exit() 426 # Get output from the console. This should contain both the 427 # "exitCommands" that were run after the second breakpoint was hit 428 output = self.get_console(timeout=1.0) 429 self.verify_commands('exitCommands', output, exitCommands) 430 431 @skipIfWindows 432 @skipIfNetBSD # Hangs on NetBSD as well 433 def test_terminate_commands(self): 434 ''' 435 Tests that the "terminateCommands", that can be passed during 436 launch, are run when the debugger is disconnected. 437 ''' 438 self.build_and_create_debug_adaptor() 439 program = self.getBuildArtifact("a.out") 440 441 terminateCommands = ['expr 4+2'] 442 self.launch(program=program, 443 terminateCommands=terminateCommands) 444 self.get_console() 445 # Once it's disconnected the console should contain the 446 # "terminateCommands" 447 self.vscode.request_disconnect(terminateDebuggee=True) 448 output = self.collect_console(duration=1.0) 449 self.verify_commands('terminateCommands', output, terminateCommands) 450