1"""
2Test lldb-vscode setBreakpoints request
3"""
4
5from __future__ import print_function
6
7import unittest2
8import vscode
9from lldbsuite.test.decorators import *
10from lldbsuite.test.lldbtest import *
11from lldbsuite.test import lldbutil
12import lldbvscode_testcase
13
14
15def make_buffer_verify_dict(start_idx, count, offset=0):
16    verify_dict = {}
17    for i in range(start_idx, start_idx + count):
18        verify_dict['[%i]' % (i)] = {'type': 'int', 'value': str(i+offset)}
19    return verify_dict
20
21
22class TestVSCode_variables(lldbvscode_testcase.VSCodeTestCaseBase):
23
24    mydir = TestBase.compute_mydir(__file__)
25
26    def verify_values(self, verify_dict, actual, varref_dict=None, expression=None):
27        if 'equals' in verify_dict:
28            verify = verify_dict['equals']
29            for key in verify:
30                verify_value = verify[key]
31                actual_value = actual[key]
32                self.assertEqual(verify_value, actual_value,
33                                '"%s" keys don\'t match (%s != %s)' % (
34                                    key, actual_value, verify_value))
35        if 'startswith' in verify_dict:
36            verify = verify_dict['startswith']
37            for key in verify:
38                verify_value = verify[key]
39                actual_value = actual[key]
40                startswith = actual_value.startswith(verify_value)
41                self.assertTrue(startswith,
42                                ('"%s" value "%s" doesn\'t start with'
43                                 ' "%s")') % (
44                                    key, actual_value,
45                                    verify_value))
46        hasVariablesReference = 'variablesReference' in actual
47        varRef = None
48        if hasVariablesReference:
49            # Remember variable references in case we want to test further
50            # by using the evaluate name.
51            varRef = actual["variablesReference"]
52            if varRef != 0 and varref_dict is not None:
53                if expression is None:
54                    evaluateName = actual["evaluateName"]
55                else:
56                    evaluateName = expression
57                varref_dict[evaluateName] = varRef
58        if ('hasVariablesReference' in verify_dict and
59                verify_dict['hasVariablesReference']):
60            self.assertTrue(hasVariablesReference,
61                            "verify variable reference")
62        if 'children' in verify_dict:
63            self.assertTrue(hasVariablesReference and varRef is not None and
64                            varRef != 0,
65                            ("children verify values specified for "
66                             "variable without children"))
67
68            response = self.vscode.request_variables(varRef)
69            self.verify_variables(verify_dict['children'],
70                                  response['body']['variables'],
71                                  varref_dict)
72
73    def verify_variables(self, verify_dict, variables, varref_dict=None):
74        for variable in variables:
75            name = variable['name']
76            self.assertIn(name, verify_dict,
77                          'variable "%s" in verify dictionary' % (name))
78            self.verify_values(verify_dict[name], variable, varref_dict)
79
80    @skipIfWindows
81    @skipIfRemote
82    def test_scopes_variables_setVariable_evaluate(self):
83        '''
84            Tests the "scopes", "variables", "setVariable", and "evaluate"
85            packets.
86        '''
87        program = self.getBuildArtifact("a.out")
88        self.build_and_launch(program)
89        source = 'main.cpp'
90        breakpoint1_line = line_number(source, '// breakpoint 1')
91        lines = [breakpoint1_line]
92        # Set breakpoint in the thread function so we can step the threads
93        breakpoint_ids = self.set_source_breakpoints(source, lines)
94        self.assertEqual(len(breakpoint_ids), len(lines),
95                        "expect correct number of breakpoints")
96        self.continue_to_breakpoints(breakpoint_ids)
97        locals = self.vscode.get_local_variables()
98        globals = self.vscode.get_global_variables()
99        buffer_children = make_buffer_verify_dict(0, 32)
100        verify_locals = {
101            'argc': {
102                'equals': {'type': 'int', 'value': '1'}
103            },
104            'argv': {
105                'equals': {'type': 'const char **'},
106                'startswith': {'value': '0x'},
107                'hasVariablesReference': True
108            },
109            'pt': {
110                'equals': {'type': 'PointType'},
111                'hasVariablesReference': True,
112                'children': {
113                    'x': {'equals': {'type': 'int', 'value': '11'}},
114                    'y': {'equals': {'type': 'int', 'value': '22'}},
115                    'buffer': {'children': buffer_children}
116                }
117            },
118            'x': {
119                'equals': {'type': 'int'}
120            }
121        }
122        verify_globals = {
123            's_local': {
124                'equals': {'type': 'float', 'value': '2.25'}
125            },
126            '::g_global': {
127                'equals': {'type': 'int', 'value': '123'}
128            },
129            's_global': {
130                'equals': {'type': 'int', 'value': '234'}
131            },
132        }
133        varref_dict = {}
134        self.verify_variables(verify_locals, locals, varref_dict)
135        self.verify_variables(verify_globals, globals, varref_dict)
136        # pprint.PrettyPrinter(indent=4).pprint(varref_dict)
137        # We need to test the functionality of the "variables" request as it
138        # has optional parameters like "start" and "count" to limit the number
139        # of variables that are fetched
140        varRef = varref_dict['pt.buffer']
141        response = self.vscode.request_variables(varRef)
142        self.verify_variables(buffer_children, response['body']['variables'])
143        # Verify setting start=0 in the arguments still gets all children
144        response = self.vscode.request_variables(varRef, start=0)
145        self.verify_variables(buffer_children, response['body']['variables'])
146        # Verify setting count=0 in the arguments still gets all children.
147        # If count is zero, it means to get all children.
148        response = self.vscode.request_variables(varRef, count=0)
149        self.verify_variables(buffer_children, response['body']['variables'])
150        # Verify setting count to a value that is too large in the arguments
151        # still gets all children, and no more
152        response = self.vscode.request_variables(varRef, count=1000)
153        self.verify_variables(buffer_children, response['body']['variables'])
154        # Verify setting the start index and count gets only the children we
155        # want
156        response = self.vscode.request_variables(varRef, start=5, count=5)
157        self.verify_variables(make_buffer_verify_dict(5, 5),
158                              response['body']['variables'])
159        # Verify setting the start index to a value that is out of range
160        # results in an empty list
161        response = self.vscode.request_variables(varRef, start=32, count=1)
162        self.assertEqual(len(response['body']['variables']), 0,
163                        'verify we get no variable back for invalid start')
164
165        # Test evaluate
166        expressions = {
167            'pt.x': {
168                'equals': {'result': '11', 'type': 'int'},
169                'hasVariablesReference': False
170            },
171            'pt.buffer[2]': {
172                'equals': {'result': '2', 'type': 'int'},
173                'hasVariablesReference': False
174            },
175            'pt': {
176                'equals': {'type': 'PointType'},
177                'startswith': {'result': 'PointType @ 0x'},
178                'hasVariablesReference': True
179            },
180            'pt.buffer': {
181                'equals': {'type': 'int[32]'},
182                'startswith': {'result': 'int[32] @ 0x'},
183                'hasVariablesReference': True
184            },
185            'argv': {
186                'equals': {'type': 'const char **'},
187                'startswith': {'result': '0x'},
188                'hasVariablesReference': True
189            },
190            'argv[0]': {
191                'equals': {'type': 'const char *'},
192                'startswith': {'result': '0x'},
193                'hasVariablesReference': True
194            },
195            '2+3': {
196                'equals': {'result': '5', 'type': 'int'},
197                'hasVariablesReference': False
198            },
199        }
200        for expression in expressions:
201            response = self.vscode.request_evaluate(expression)
202            self.verify_values(expressions[expression], response['body'])
203
204        # Test setting variables
205        self.set_local('argc', 123)
206        argc = self.get_local_as_int('argc')
207        self.assertEqual(argc, 123,
208                        'verify argc was set to 123 (123 != %i)' % (argc))
209
210        self.set_local('argv', 0x1234)
211        argv = self.get_local_as_int('argv')
212        self.assertEqual(argv, 0x1234,
213                        'verify argv was set to 0x1234 (0x1234 != %#x)' % (
214                            argv))
215
216        # Set a variable value whose name is synthetic, like a variable index
217        # and verify the value by reading it
218        self.vscode.request_setVariable(varRef, "[0]", 100)
219        response = self.vscode.request_variables(varRef, start=0, count=1)
220        self.verify_variables(make_buffer_verify_dict(0, 1, 100),
221                              response['body']['variables'])
222
223        # Set a variable value whose name is a real child value, like "pt.x"
224        # and verify the value by reading it
225        varRef = varref_dict['pt']
226        self.vscode.request_setVariable(varRef, "x", 111)
227        response = self.vscode.request_variables(varRef, start=0, count=1)
228        value = response['body']['variables'][0]['value']
229        self.assertEqual(value, '111',
230                        'verify pt.x got set to 111 (111 != %s)' % (value))
231
232        # We check shadowed variables and that a new get_local_variables request
233        # gets the right data
234        breakpoint2_line = line_number(source, '// breakpoint 2')
235        lines = [breakpoint2_line]
236        breakpoint_ids = self.set_source_breakpoints(source, lines)
237        self.assertEqual(len(breakpoint_ids), len(lines),
238                        "expect correct number of breakpoints")
239        self.continue_to_breakpoints(breakpoint_ids)
240
241        verify_locals['argc']['equals']['value'] = '123'
242        verify_locals['pt']['children']['x']['equals']['value'] = '111'
243        verify_locals['x @ main.cpp:17'] = {'equals': {'type': 'int', 'value': '89'}}
244        verify_locals['x @ main.cpp:19'] = {'equals': {'type': 'int', 'value': '42'}}
245        verify_locals['x @ main.cpp:21'] = {'equals': {'type': 'int', 'value': '72'}}
246
247        self.verify_variables(verify_locals, self.vscode.get_local_variables())
248
249        # Now we verify that we correctly change the name of a variable with and without differentiator suffix
250        self.assertFalse(self.vscode.request_setVariable(1, "x2", 9)['success'])
251        self.assertFalse(self.vscode.request_setVariable(1, "x @ main.cpp:0", 9)['success'])
252
253        self.assertTrue(self.vscode.request_setVariable(1, "x @ main.cpp:17", 17)['success'])
254        self.assertTrue(self.vscode.request_setVariable(1, "x @ main.cpp:19", 19)['success'])
255        self.assertTrue(self.vscode.request_setVariable(1, "x @ main.cpp:21", 21)['success'])
256
257        # The following should have no effect
258        self.assertFalse(self.vscode.request_setVariable(1, "x @ main.cpp:21", "invalid")['success'])
259
260        verify_locals['x @ main.cpp:17']['equals']['value'] = '17'
261        verify_locals['x @ main.cpp:19']['equals']['value'] = '19'
262        verify_locals['x @ main.cpp:21']['equals']['value'] = '21'
263
264        self.verify_variables(verify_locals, self.vscode.get_local_variables())
265
266        # The plain x variable shold refer to the innermost x
267        self.assertTrue(self.vscode.request_setVariable(1, "x", 22)['success'])
268        verify_locals['x @ main.cpp:21']['equals']['value'] = '22'
269
270        self.verify_variables(verify_locals, self.vscode.get_local_variables())
271
272        # In breakpoint 3, there should be no shadowed variables
273        breakpoint3_line = line_number(source, '// breakpoint 3')
274        lines = [breakpoint3_line]
275        breakpoint_ids = self.set_source_breakpoints(source, lines)
276        self.assertEqual(len(breakpoint_ids), len(lines),
277                        "expect correct number of breakpoints")
278        self.continue_to_breakpoints(breakpoint_ids)
279
280        locals = self.vscode.get_local_variables()
281        names = [var['name'] for var in locals]
282        # The first shadowed x shouldn't have a suffix anymore
283        verify_locals['x'] = {'equals': {'type': 'int', 'value': '17'}}
284        self.assertNotIn('x @ main.cpp:17', names)
285        self.assertNotIn('x @ main.cpp:19', names)
286        self.assertNotIn('x @ main.cpp:21', names)
287
288        self.verify_variables(verify_locals, locals)
289
290    @skipIfWindows
291    @skipIfRemote
292    def test_scopes_and_evaluate_expansion(self):
293        """
294        Tests the evaluated expression expands successfully after "scopes" packets
295        and permanent expressions persist.
296        """
297        program = self.getBuildArtifact("a.out")
298        self.build_and_launch(program)
299        source = "main.cpp"
300        breakpoint1_line = line_number(source, "// breakpoint 1")
301        lines = [breakpoint1_line]
302        # Set breakpoint in the thread function so we can step the threads
303        breakpoint_ids = self.set_source_breakpoints(source, lines)
304        self.assertEqual(
305            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
306        )
307        self.continue_to_breakpoints(breakpoint_ids)
308
309        # Verify locals
310        locals = self.vscode.get_local_variables()
311        buffer_children = make_buffer_verify_dict(0, 32)
312        verify_locals = {
313            "argc": {"equals": {"type": "int", "value": "1"}},
314            "argv": {
315                "equals": {"type": "const char **"},
316                "startswith": {"value": "0x"},
317                "hasVariablesReference": True,
318            },
319            "pt": {
320                "equals": {"type": "PointType"},
321                "hasVariablesReference": True,
322                "children": {
323                    "x": {"equals": {"type": "int", "value": "11"}},
324                    "y": {"equals": {"type": "int", "value": "22"}},
325                    "buffer": {"children": buffer_children},
326                },
327            },
328            "x": {"equals": {"type": "int"}},
329        }
330        self.verify_variables(verify_locals, locals)
331
332        # Evaluate expandable expression twice: once permanent (from repl)
333        # the other temporary (from other UI).
334        expandable_expression = {
335            "name": "pt",
336            "response": {
337                "equals": {"type": "PointType"},
338                "startswith": {"result": "PointType @ 0x"},
339                "hasVariablesReference": True,
340            },
341            "children": {
342                "x": {"equals": {"type": "int", "value": "11"}},
343                "y": {"equals": {"type": "int", "value": "22"}},
344                "buffer": {"children": buffer_children},
345            },
346        }
347
348        # Evaluate from permanent UI.
349        permanent_expr_varref_dict = {}
350        response = self.vscode.request_evaluate(
351            expandable_expression["name"], frameIndex=0, threadId=None, context="repl"
352        )
353        self.verify_values(
354            expandable_expression["response"],
355            response["body"],
356            permanent_expr_varref_dict,
357            expandable_expression["name"],
358        )
359
360        # Evaluate from temporary UI.
361        temporary_expr_varref_dict = {}
362        response = self.vscode.request_evaluate(expandable_expression["name"])
363        self.verify_values(
364            expandable_expression["response"],
365            response["body"],
366            temporary_expr_varref_dict,
367            expandable_expression["name"],
368        )
369
370        # Evaluate locals again.
371        locals = self.vscode.get_local_variables()
372        self.verify_variables(verify_locals, locals)
373
374        # Verify the evaluated expressions before second locals evaluation
375        # can be expanded.
376        var_ref = temporary_expr_varref_dict[expandable_expression["name"]]
377        response = self.vscode.request_variables(var_ref)
378        self.verify_variables(
379            expandable_expression["children"], response["body"]["variables"]
380        )
381
382        # Continue to breakpoint 3, permanent variable should still exist
383        # after resume.
384        breakpoint3_line = line_number(source, "// breakpoint 3")
385        lines = [breakpoint3_line]
386        breakpoint_ids = self.set_source_breakpoints(source, lines)
387        self.assertEqual(
388            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
389        )
390        self.continue_to_breakpoints(breakpoint_ids)
391
392        var_ref = permanent_expr_varref_dict[expandable_expression["name"]]
393        response = self.vscode.request_variables(var_ref)
394        self.verify_variables(
395            expandable_expression["children"], response["body"]["variables"]
396        )
397
398        # Test that frame scopes have corresponding presentation hints.
399        frame_id = self.vscode.get_stackFrame()["id"]
400        scopes = self.vscode.request_scopes(frame_id)["body"]["scopes"]
401
402        scope_names = [scope["name"] for scope in scopes]
403        self.assertIn("Locals", scope_names)
404        self.assertIn("Registers", scope_names)
405
406        for scope in scopes:
407            if scope["name"] == "Locals":
408                self.assertEquals(scope.get("presentationHint"), "locals")
409            if scope["name"] == "Registers":
410                self.assertEquals(scope.get("presentationHint"), "registers")
411