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