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