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