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