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