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