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" and
298            "exitCommands" that can be passed during launch.
299
300            "initCommands" are a list of LLDB commands that get executed
301            before the targt is created.
302            "preRunCommands" are a list of LLDB commands that get executed
303            after the target has been created and before the launch.
304            "stopCommands" are a list of LLDB commands that get executed each
305            time the program stops.
306            "exitCommands" are a list of LLDB commands that get executed when
307            the process exits
308        '''
309        program = self.getBuildArtifact("a.out")
310        initCommands = ['target list', 'platform list']
311        preRunCommands = ['image list a.out', 'image dump sections a.out']
312        stopCommands = ['frame variable', 'bt']
313        exitCommands = ['expr 2+3', 'expr 3+4']
314        self.build_and_launch(program,
315                              initCommands=initCommands,
316                              preRunCommands=preRunCommands,
317                              stopCommands=stopCommands,
318                              exitCommands=exitCommands)
319
320        # Get output from the console. This should contain both the
321        # "initCommands" and the "preRunCommands".
322        output = self.get_console()
323        # Verify all "initCommands" were found in console output
324        self.verify_commands('initCommands', output, initCommands)
325        # Verify all "preRunCommands" were found in console output
326        self.verify_commands('preRunCommands', output, preRunCommands)
327
328        source = 'main.c'
329        first_line = line_number(source, '// breakpoint 1')
330        second_line = line_number(source, '// breakpoint 2')
331        lines = [first_line, second_line]
332
333        # Set 2 breakpoints so we can verify that "stopCommands" get run as the
334        # breakpoints get hit
335        breakpoint_ids = self.set_source_breakpoints(source, lines)
336        self.assertEquals(len(breakpoint_ids), len(lines),
337                        "expect correct number of breakpoints")
338
339        # Continue after launch and hit the first breakpoint.
340        # Get output from the console. This should contain both the
341        # "stopCommands" that were run after the first breakpoint was hit
342        self.continue_to_breakpoints(breakpoint_ids)
343        output = self.get_console(timeout=1.0)
344        self.verify_commands('stopCommands', output, stopCommands)
345
346        # Continue again and hit the second breakpoint.
347        # Get output from the console. This should contain both the
348        # "stopCommands" that were run after the second breakpoint was hit
349        self.continue_to_breakpoints(breakpoint_ids)
350        output = self.get_console(timeout=1.0)
351        self.verify_commands('stopCommands', output, stopCommands)
352
353        # Continue until the program exits
354        self.continue_to_exit()
355        # Get output from the console. This should contain both the
356        # "exitCommands" that were run after the second breakpoint was hit
357        output = self.get_console(timeout=1.0)
358        self.verify_commands('exitCommands', output, exitCommands)
359
360    @skipIfWindows
361    @skipIfRemote
362    def test_extra_launch_commands(self):
363        '''
364            Tests the "luanchCommands" with extra launching settings
365        '''
366        self.build_and_create_debug_adaptor()
367        program = self.getBuildArtifact("a.out")
368
369        source = 'main.c'
370        first_line = line_number(source, '// breakpoint 1')
371        second_line = line_number(source, '// breakpoint 2')
372        # Set target binary and 2 breakpoints
373        # then we can varify the "launchCommands" get run
374        # also we can verify that "stopCommands" get run as the
375        # breakpoints get hit
376        launchCommands = [
377            'target create "%s"' % (program),
378            'br s -f main.c -l %d' % first_line,
379            'br s -f main.c -l %d' % second_line,
380            'process launch --stop-at-entry'
381        ]
382
383        initCommands = ['target list', 'platform list']
384        preRunCommands = ['image list a.out', 'image dump sections a.out']
385        stopCommands = ['frame variable', 'bt']
386        exitCommands = ['expr 2+3', 'expr 3+4']
387        self.launch(program,
388                    initCommands=initCommands,
389                    preRunCommands=preRunCommands,
390                    stopCommands=stopCommands,
391                    exitCommands=exitCommands,
392                    launchCommands=launchCommands)
393
394        # Get output from the console. This should contain both the
395        # "initCommands" and the "preRunCommands".
396        output = self.get_console()
397        # Verify all "initCommands" were found in console output
398        self.verify_commands('initCommands', output, initCommands)
399        # Verify all "preRunCommands" were found in console output
400        self.verify_commands('preRunCommands', output, preRunCommands)
401
402        # Verify all "launchCommands" were founc in console output
403        # After execution, program should launch
404        self.verify_commands('launchCommands', output, launchCommands)
405        # Verify the "stopCommands" here
406        self.continue_to_next_stop()
407        output = self.get_console(timeout=1.0)
408        self.verify_commands('stopCommands', output, stopCommands)
409
410        # Continue and hit the second breakpoint.
411        # Get output from the console. This should contain both the
412        # "stopCommands" that were run after the first breakpoint was hit
413        self.continue_to_next_stop()
414        output = self.get_console(timeout=1.0)
415        self.verify_commands('stopCommands', output, stopCommands)
416
417        # Continue until the program exits
418        self.continue_to_exit()
419        # Get output from the console. This should contain both the
420        # "exitCommands" that were run after the second breakpoint was hit
421        output = self.get_console(timeout=1.0)
422        self.verify_commands('exitCommands', output, exitCommands)
423