Files
clang-p2996/lldb/test/API/tools/lldb-vscode/variables/TestVSCode_variables.py
Dave Lee 4cc8f2a017 [lldb][tests] Automatically call compute_mydir (NFC)
Eliminate boilerplate of having each test manually assign to `mydir` by calling
`compute_mydir` in lldbtest.py.

Differential Revision: https://reviews.llvm.org/D128077
2022-06-17 14:34:49 -07:00

465 lines
20 KiB
Python

"""
Test lldb-vscode setBreakpoints request
"""
from __future__ import print_function
import unittest2
import vscode
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbvscode_testcase
def make_buffer_verify_dict(start_idx, count, offset=0):
verify_dict = {}
for i in range(start_idx, start_idx + count):
verify_dict['[%i]' % (i)] = {'type': 'int', 'value': str(i+offset)}
return verify_dict
class TestVSCode_variables(lldbvscode_testcase.VSCodeTestCaseBase):
def verify_values(self, verify_dict, actual, varref_dict=None, expression=None):
if 'equals' in verify_dict:
verify = verify_dict['equals']
for key in verify:
verify_value = verify[key]
actual_value = actual[key]
self.assertEqual(verify_value, actual_value,
'"%s" keys don\'t match (%s != %s) from:\n%s' % (
key, actual_value, verify_value, actual))
if 'startswith' in verify_dict:
verify = verify_dict['startswith']
for key in verify:
verify_value = verify[key]
actual_value = actual[key]
startswith = actual_value.startswith(verify_value)
self.assertTrue(startswith,
('"%s" value "%s" doesn\'t start with'
' "%s")') % (
key, actual_value,
verify_value))
if 'missing' in verify_dict:
missing = verify_dict['missing']
for key in missing:
self.assertTrue(key not in actual,
'key "%s" is not expected in %s' % (key, actual))
hasVariablesReference = 'variablesReference' in actual
varRef = None
if hasVariablesReference:
# Remember variable references in case we want to test further
# by using the evaluate name.
varRef = actual["variablesReference"]
if varRef != 0 and varref_dict is not None:
if expression is None:
evaluateName = actual["evaluateName"]
else:
evaluateName = expression
varref_dict[evaluateName] = varRef
if ('hasVariablesReference' in verify_dict and
verify_dict['hasVariablesReference']):
self.assertTrue(hasVariablesReference,
"verify variable reference")
if 'children' in verify_dict:
self.assertTrue(hasVariablesReference and varRef is not None and
varRef != 0,
("children verify values specified for "
"variable without children"))
response = self.vscode.request_variables(varRef)
self.verify_variables(verify_dict['children'],
response['body']['variables'],
varref_dict)
def verify_variables(self, verify_dict, variables, varref_dict=None):
for variable in variables:
name = variable['name']
self.assertIn(name, verify_dict,
'variable "%s" in verify dictionary' % (name))
self.verify_values(verify_dict[name], variable, varref_dict)
@skipIfWindows
@skipIfRemote
def test_scopes_variables_setVariable_evaluate(self):
'''
Tests the "scopes", "variables", "setVariable", and "evaluate"
packets.
'''
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = 'main.cpp'
breakpoint1_line = line_number(source, '// breakpoint 1')
lines = [breakpoint1_line]
# Set breakpoint in the thread function so we can step the threads
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(len(breakpoint_ids), len(lines),
"expect correct number of breakpoints")
self.continue_to_breakpoints(breakpoint_ids)
locals = self.vscode.get_local_variables()
globals = self.vscode.get_global_variables()
buffer_children = make_buffer_verify_dict(0, 32)
verify_locals = {
'argc': {
'equals': {'type': 'int', 'value': '1'}
},
'argv': {
'equals': {'type': 'const char **'},
'startswith': {'value': '0x'},
'hasVariablesReference': True
},
'pt': {
'equals': {'type': 'PointType'},
'hasVariablesReference': True,
'children': {
'x': {'equals': {'type': 'int', 'value': '11'}},
'y': {'equals': {'type': 'int', 'value': '22'}},
'buffer': {'children': buffer_children}
}
},
'x': {
'equals': {'type': 'int'}
}
}
verify_globals = {
's_local': {
'equals': {'type': 'float', 'value': '2.25'}
},
'::g_global': {
'equals': {'type': 'int', 'value': '123'}
},
's_global': {
'equals': {'type': 'int', 'value': '234'}
},
}
varref_dict = {}
self.verify_variables(verify_locals, locals, varref_dict)
self.verify_variables(verify_globals, globals, varref_dict)
# pprint.PrettyPrinter(indent=4).pprint(varref_dict)
# We need to test the functionality of the "variables" request as it
# has optional parameters like "start" and "count" to limit the number
# of variables that are fetched
varRef = varref_dict['pt.buffer']
response = self.vscode.request_variables(varRef)
self.verify_variables(buffer_children, response['body']['variables'])
# Verify setting start=0 in the arguments still gets all children
response = self.vscode.request_variables(varRef, start=0)
self.verify_variables(buffer_children, response['body']['variables'])
# Verify setting count=0 in the arguments still gets all children.
# If count is zero, it means to get all children.
response = self.vscode.request_variables(varRef, count=0)
self.verify_variables(buffer_children, response['body']['variables'])
# Verify setting count to a value that is too large in the arguments
# still gets all children, and no more
response = self.vscode.request_variables(varRef, count=1000)
self.verify_variables(buffer_children, response['body']['variables'])
# Verify setting the start index and count gets only the children we
# want
response = self.vscode.request_variables(varRef, start=5, count=5)
self.verify_variables(make_buffer_verify_dict(5, 5),
response['body']['variables'])
# Verify setting the start index to a value that is out of range
# results in an empty list
response = self.vscode.request_variables(varRef, start=32, count=1)
self.assertEqual(len(response['body']['variables']), 0,
'verify we get no variable back for invalid start')
# Test evaluate
expressions = {
'pt.x': {
'equals': {'result': '11', 'type': 'int'},
'hasVariablesReference': False
},
'pt.buffer[2]': {
'equals': {'result': '2', 'type': 'int'},
'hasVariablesReference': False
},
'pt': {
'equals': {'type': 'PointType'},
'startswith': {'result': 'PointType @ 0x'},
'hasVariablesReference': True
},
'pt.buffer': {
'equals': {'type': 'int[32]'},
'startswith': {'result': 'int[32] @ 0x'},
'hasVariablesReference': True
},
'argv': {
'equals': {'type': 'const char **'},
'startswith': {'result': '0x'},
'hasVariablesReference': True
},
'argv[0]': {
'equals': {'type': 'const char *'},
'startswith': {'result': '0x'},
'hasVariablesReference': True
},
'2+3': {
'equals': {'result': '5', 'type': 'int'},
'hasVariablesReference': False
},
}
for expression in expressions:
response = self.vscode.request_evaluate(expression)
self.verify_values(expressions[expression], response['body'])
# Test setting variables
self.set_local('argc', 123)
argc = self.get_local_as_int('argc')
self.assertEqual(argc, 123,
'verify argc was set to 123 (123 != %i)' % (argc))
self.set_local('argv', 0x1234)
argv = self.get_local_as_int('argv')
self.assertEqual(argv, 0x1234,
'verify argv was set to 0x1234 (0x1234 != %#x)' % (
argv))
# Set a variable value whose name is synthetic, like a variable index
# and verify the value by reading it
self.vscode.request_setVariable(varRef, "[0]", 100)
response = self.vscode.request_variables(varRef, start=0, count=1)
self.verify_variables(make_buffer_verify_dict(0, 1, 100),
response['body']['variables'])
# Set a variable value whose name is a real child value, like "pt.x"
# and verify the value by reading it
varRef = varref_dict['pt']
self.vscode.request_setVariable(varRef, "x", 111)
response = self.vscode.request_variables(varRef, start=0, count=1)
value = response['body']['variables'][0]['value']
self.assertEqual(value, '111',
'verify pt.x got set to 111 (111 != %s)' % (value))
# We check shadowed variables and that a new get_local_variables request
# gets the right data
breakpoint2_line = line_number(source, '// breakpoint 2')
lines = [breakpoint2_line]
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(len(breakpoint_ids), len(lines),
"expect correct number of breakpoints")
self.continue_to_breakpoints(breakpoint_ids)
verify_locals['argc']['equals']['value'] = '123'
verify_locals['pt']['children']['x']['equals']['value'] = '111'
verify_locals['x @ main.cpp:17'] = {'equals': {'type': 'int', 'value': '89'}}
verify_locals['x @ main.cpp:19'] = {'equals': {'type': 'int', 'value': '42'}}
verify_locals['x @ main.cpp:21'] = {'equals': {'type': 'int', 'value': '72'}}
self.verify_variables(verify_locals, self.vscode.get_local_variables())
# Now we verify that we correctly change the name of a variable with and without differentiator suffix
self.assertFalse(self.vscode.request_setVariable(1, "x2", 9)['success'])
self.assertFalse(self.vscode.request_setVariable(1, "x @ main.cpp:0", 9)['success'])
self.assertTrue(self.vscode.request_setVariable(1, "x @ main.cpp:17", 17)['success'])
self.assertTrue(self.vscode.request_setVariable(1, "x @ main.cpp:19", 19)['success'])
self.assertTrue(self.vscode.request_setVariable(1, "x @ main.cpp:21", 21)['success'])
# The following should have no effect
self.assertFalse(self.vscode.request_setVariable(1, "x @ main.cpp:21", "invalid")['success'])
verify_locals['x @ main.cpp:17']['equals']['value'] = '17'
verify_locals['x @ main.cpp:19']['equals']['value'] = '19'
verify_locals['x @ main.cpp:21']['equals']['value'] = '21'
self.verify_variables(verify_locals, self.vscode.get_local_variables())
# The plain x variable shold refer to the innermost x
self.assertTrue(self.vscode.request_setVariable(1, "x", 22)['success'])
verify_locals['x @ main.cpp:21']['equals']['value'] = '22'
self.verify_variables(verify_locals, self.vscode.get_local_variables())
# In breakpoint 3, there should be no shadowed variables
breakpoint3_line = line_number(source, '// breakpoint 3')
lines = [breakpoint3_line]
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(len(breakpoint_ids), len(lines),
"expect correct number of breakpoints")
self.continue_to_breakpoints(breakpoint_ids)
locals = self.vscode.get_local_variables()
names = [var['name'] for var in locals]
# The first shadowed x shouldn't have a suffix anymore
verify_locals['x'] = {'equals': {'type': 'int', 'value': '17'}}
self.assertNotIn('x @ main.cpp:17', names)
self.assertNotIn('x @ main.cpp:19', names)
self.assertNotIn('x @ main.cpp:21', names)
self.verify_variables(verify_locals, locals)
@skipIfWindows
@skipIfRemote
def test_scopes_and_evaluate_expansion(self):
"""
Tests the evaluated expression expands successfully after "scopes" packets
and permanent expressions persist.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
breakpoint1_line = line_number(source, "// breakpoint 1")
lines = [breakpoint1_line]
# Set breakpoint in the thread function so we can step the threads
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
)
self.continue_to_breakpoints(breakpoint_ids)
# Verify locals
locals = self.vscode.get_local_variables()
buffer_children = make_buffer_verify_dict(0, 32)
verify_locals = {
"argc": {
"equals": {"type": "int", "value": "1"},
"missing": ["indexedVariables"],
},
"argv": {
"equals": {"type": "const char **"},
"startswith": {"value": "0x"},
"hasVariablesReference": True,
"missing": ["indexedVariables"],
},
"pt": {
"equals": {"type": "PointType"},
"hasVariablesReference": True,
"missing": ["indexedVariables"],
"children": {
"x": {
"equals": {"type": "int", "value": "11"},
"missing": ["indexedVariables"],
},
"y": {
"equals": {"type": "int", "value": "22"},
"missing": ["indexedVariables"],
},
"buffer": {
"children": buffer_children,
"equals": {"indexedVariables": 32}
},
},
},
"x": {
"equals": {"type": "int"},
"missing": ["indexedVariables"],
},
}
self.verify_variables(verify_locals, locals)
# Evaluate expandable expression twice: once permanent (from repl)
# the other temporary (from other UI).
expandable_expression = {
"name": "pt",
"response": {
"equals": {"type": "PointType"},
"startswith": {"result": "PointType @ 0x"},
"missing": ["indexedVariables"],
"hasVariablesReference": True,
},
"children": {
"x": {"equals": {"type": "int", "value": "11"}},
"y": {"equals": {"type": "int", "value": "22"}},
"buffer": {"children": buffer_children},
},
}
# Evaluate from permanent UI.
permanent_expr_varref_dict = {}
response = self.vscode.request_evaluate(
expandable_expression["name"], frameIndex=0, threadId=None, context="repl"
)
self.verify_values(
expandable_expression["response"],
response["body"],
permanent_expr_varref_dict,
expandable_expression["name"],
)
# Evaluate from temporary UI.
temporary_expr_varref_dict = {}
response = self.vscode.request_evaluate(expandable_expression["name"])
self.verify_values(
expandable_expression["response"],
response["body"],
temporary_expr_varref_dict,
expandable_expression["name"],
)
# Evaluate locals again.
locals = self.vscode.get_local_variables()
self.verify_variables(verify_locals, locals)
# Verify the evaluated expressions before second locals evaluation
# can be expanded.
var_ref = temporary_expr_varref_dict[expandable_expression["name"]]
response = self.vscode.request_variables(var_ref)
self.verify_variables(
expandable_expression["children"], response["body"]["variables"]
)
# Continue to breakpoint 3, permanent variable should still exist
# after resume.
breakpoint3_line = line_number(source, "// breakpoint 3")
lines = [breakpoint3_line]
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
)
self.continue_to_breakpoints(breakpoint_ids)
var_ref = permanent_expr_varref_dict[expandable_expression["name"]]
response = self.vscode.request_variables(var_ref)
self.verify_variables(
expandable_expression["children"], response["body"]["variables"]
)
# Test that frame scopes have corresponding presentation hints.
frame_id = self.vscode.get_stackFrame()["id"]
scopes = self.vscode.request_scopes(frame_id)["body"]["scopes"]
scope_names = [scope["name"] for scope in scopes]
self.assertIn("Locals", scope_names)
self.assertIn("Registers", scope_names)
for scope in scopes:
if scope["name"] == "Locals":
self.assertEquals(scope.get("presentationHint"), "locals")
if scope["name"] == "Registers":
self.assertEquals(scope.get("presentationHint"), "registers")
@skipIfWindows
@skipIfRemote
def test_indexedVariables(self):
"""
Tests that arrays and lldb.SBValue objects that have synthetic child
providers have "indexedVariables" key/value pairs. This helps the IDE
not to fetch too many children all at once.
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
breakpoint1_line = line_number(source, "// breakpoint 4")
lines = [breakpoint1_line]
# Set breakpoint in the thread function so we can step the threads
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(
len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
)
self.continue_to_breakpoints(breakpoint_ids)
# Verify locals
locals = self.vscode.get_local_variables()
buffer_children = make_buffer_verify_dict(0, 32)
verify_locals = {
"small_array": {"equals": {"indexedVariables": 5}},
"large_array": {"equals": {"indexedVariables": 200}},
"small_vector": {"equals": {"indexedVariables": 5}},
"large_vector": {"equals": {"indexedVariables": 200}},
"pt": {"missing": ["indexedVariables"]},
}
self.verify_variables(verify_locals, locals)