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