blob: 95ad0f06d9a06001e08192afc5c08d20734fc191 [file] [log] [blame]
"""
Test lldb-dap evaluate request
"""
import re
import lldbdap_testcase
from lldbsuite.test.decorators import skipIfWindows
from lldbsuite.test.lldbtest import line_number
from typing import TypedDict, Optional
class EvaluateResponseBody(TypedDict, total=False):
result: str
variablesReference: int
type: Optional[str]
memoryReference: Optional[str]
valueLocationReference: Optional[int]
class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):
def assertEvaluate(
self,
expression,
result: str,
want_type="",
want_varref=False,
want_memref=True,
want_locref=False,
is_hex=None,
):
resp = self.dap_server.request_evaluate(
expression, context=self.context, is_hex=is_hex
)
self.assertTrue(
resp["success"], f"Failed to evaluate expression {expression!r}"
)
body: EvaluateResponseBody = resp["body"]
self.assertRegex(
body["result"],
result,
f"Unexpected 'result' for expression {expression!r} in response body {body}",
)
if want_varref:
self.assertNotEqual(
body["variablesReference"],
0,
f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",
)
else:
self.assertEqual(
body["variablesReference"],
0,
f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",
)
if want_type:
self.assertEqual(
body["type"],
want_type,
f"Unexpected 'type' for expression {expression!r} in response body {body}",
)
if want_memref:
self.assertIn(
"memoryReference",
body,
f"Unexpected 'memoryReference' for expression {expression!r} in response body {body}",
)
if want_locref:
self.assertIn(
"valueLocationReference",
body,
f"Unexpected 'valueLocationReference' for expression {expression!r} in response body {body}",
)
def assertEvaluateFailure(self, expression):
self.assertNotIn(
"result",
self.dap_server.request_evaluate(expression, context=self.context)["body"],
)
def isResultExpandedDescription(self):
return self.context == "repl"
def isExpressionParsedExpected(self):
return self.context != "hover"
def run_test_evaluate_expressions(
self, context=None, enableAutoVariableSummaries=False
):
"""
Tests the evaluate expression request at different breakpoints
"""
self.context = context
program = self.getBuildArtifact("a.out")
self.build_and_launch(
program,
enableAutoVariableSummaries=enableAutoVariableSummaries,
)
source = "main.cpp"
breakpoint_lines = [
line_number(source, "// breakpoint 1"),
line_number(source, "// breakpoint 2"),
line_number(source, "// breakpoint 3"),
line_number(source, "// breakpoint 4"),
line_number(source, "// breakpoint 5"),
line_number(source, "// breakpoint 6"),
line_number(source, "// breakpoint 7"),
line_number(source, "// breakpoint 8"),
]
breakpoint_ids = self.set_source_breakpoints(source, breakpoint_lines)
self.assertEqual(
len(breakpoint_ids),
len(breakpoint_lines),
"Did not resolve all the breakpoints.",
)
breakpoint_1 = breakpoint_ids[0]
breakpoint_2 = breakpoint_ids[1]
breakpoint_3 = breakpoint_ids[2]
breakpoint_4 = breakpoint_ids[3]
breakpoint_5 = breakpoint_ids[4]
breakpoint_6 = breakpoint_ids[5]
breakpoint_7 = breakpoint_ids[6]
breakpoint_8 = breakpoint_ids[7]
self.continue_to_breakpoint(breakpoint_1)
# Expressions at breakpoint 1, which is in main
self.assertEvaluate("var1", "20", want_type="int")
# Empty expression should equate to the previous expression.
if context == "repl":
self.assertEvaluate("", "20")
else:
self.assertEvaluateFailure("")
self.assertEvaluate("var2", "21", want_type="int")
if context == "repl":
self.assertEvaluate("", "21", want_type="int")
self.assertEvaluate("", "21", want_type="int")
self.assertEvaluate("static_int", "0x0000002a", want_type="int", is_hex=True)
self.assertEvaluate(
"non_static_int", "0x0000002b", want_type="int", is_hex=True
)
self.assertEvaluate("struct1.foo", "0x0000000f", want_type="int", is_hex=True)
self.assertEvaluate("struct2->foo", "0x00000010", want_type="int", is_hex=True)
self.assertEvaluate("static_int", "42", want_type="int")
self.assertEvaluate("non_static_int", "43", want_type="int")
self.assertEvaluate("struct1.foo", "15", want_type="int")
self.assertEvaluate("struct2->foo", "16", want_type="int")
if self.isResultExpandedDescription():
self.assertEvaluate(
"struct1",
r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
want_type="my_struct",
want_varref=True,
)
self.assertEvaluate(
"struct2",
r"\(my_struct \*\) (struct2|\$\d+) = 0x.*",
want_type="my_struct *",
want_varref=True,
)
self.assertEvaluate(
"struct3",
r"\(my_struct \*\) (struct3|\$\d+) = nullptr",
want_type="my_struct *",
want_varref=True,
)
else:
self.assertEvaluate(
"struct1",
(re.escape("{foo:15}") if enableAutoVariableSummaries else "my_struct"),
want_varref=True,
)
self.assertEvaluate(
"struct2",
"0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*",
want_varref=True,
want_type="my_struct *",
)
self.assertEvaluate(
"struct3", "0x.*0", want_varref=True, want_type="my_struct *"
)
if context == "repl":
# In the repl context expressions may be interpreted as lldb
# commands since no variables have the same name as the command.
self.assertEvaluate("list", r".*", want_memref=False)
else:
self.assertEvaluateFailure("list") # local variable of a_function
self.assertEvaluateFailure("my_struct") # type name
self.assertEvaluateFailure("int") # type name
self.assertEvaluateFailure("foo") # member of my_struct
if self.isExpressionParsedExpected():
self.assertEvaluate(
"a_function",
"0x.*a.out`a_function.*",
want_type="int (*)(int)",
want_varref=True,
want_memref=False,
want_locref=True,
)
self.assertEvaluate(
"a_function(1)", "1", want_memref=False, want_type="int"
)
self.assertEvaluate("var2 + struct1.foo", "36", want_memref=False)
self.assertEvaluate(
"foo_func",
"0x.*a.out`foo_func.*",
want_type="int (*)()",
want_varref=True,
want_memref=False,
want_locref=True,
)
self.assertEvaluate("foo_var", "44")
else:
self.assertEvaluateFailure("a_function")
self.assertEvaluateFailure("a_function(1)")
self.assertEvaluateFailure("var2 + struct1.foo")
self.assertEvaluateFailure("foo_func")
self.assertEvaluate("foo_var", "44")
# Expressions at breakpoint 2, which is an anonymous block
self.continue_to_breakpoint(breakpoint_2)
self.assertEvaluate("var1", "20")
self.assertEvaluate("var2", "2") # different variable with the same name
self.assertEvaluate("static_int", "42")
self.assertEvaluate(
"non_static_int", "10"
) # different variable with the same name
if self.isResultExpandedDescription():
self.assertEvaluate(
"struct1",
r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",
want_type="my_struct",
want_varref=True,
)
else:
self.assertEvaluate(
"struct1",
(re.escape("{foo:15}") if enableAutoVariableSummaries else "my_struct"),
want_type="my_struct",
want_varref=True,
)
self.assertEvaluate("struct1.foo", "15")
self.assertEvaluate("struct2->foo", "16")
if self.isExpressionParsedExpected():
self.assertEvaluate(
"a_function",
"0x.*a.out`a_function.*",
want_type="int (*)(int)",
want_varref=True,
want_memref=False,
want_locref=True,
)
self.assertEvaluate("a_function(1)", "1", want_memref=False)
self.assertEvaluate("var2 + struct1.foo", "17", want_memref=False)
self.assertEvaluate(
"foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False
)
self.assertEvaluate("foo_var", "44")
else:
self.assertEvaluateFailure("a_function")
self.assertEvaluateFailure("a_function(1)")
self.assertEvaluateFailure("var2 + struct1.foo")
self.assertEvaluateFailure("foo_func")
self.assertEvaluate("foo_var", "44")
# Expressions at breakpoint 3, which is inside a_function
self.continue_to_breakpoint(breakpoint_3)
self.assertEvaluate("list", "42")
self.assertEvaluate("static_int", "42")
self.assertEvaluate("non_static_int", "43")
self.assertEvaluateFailure("var1")
self.assertEvaluateFailure("var2")
self.assertEvaluateFailure("struct1")
self.assertEvaluateFailure("struct1.foo")
self.assertEvaluateFailure("struct2->foo")
self.assertEvaluateFailure("var2 + struct1.foo")
if self.isExpressionParsedExpected():
self.assertEvaluate(
"a_function",
"0x.*a.out`a_function.*",
want_varref=True,
want_memref=False,
want_locref=True,
)
self.assertEvaluate("a_function(1)", "1", want_memref=False)
self.assertEvaluate("list + 1", "43", want_memref=False)
self.assertEvaluate(
"foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False
)
self.assertEvaluate("foo_var", "44")
else:
self.assertEvaluateFailure("a_function")
self.assertEvaluateFailure("a_function(1)")
self.assertEvaluateFailure("list + 1")
self.assertEvaluateFailure("foo_func")
self.assertEvaluate("foo_var", "44")
# Now we check that values are updated after stepping
self.continue_to_breakpoint(breakpoint_4)
self.assertEvaluate("my_vec", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_5)
self.assertEvaluate("my_vec", "size=3", want_varref=True)
self.assertEvaluate("my_map", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_6)
self.assertEvaluate("my_map", "size=3", want_varref=True)
self.assertEvaluate("my_bool_vec", "size=1", want_varref=True)
self.continue_to_breakpoint(breakpoint_7)
self.assertEvaluate("my_bool_vec", "size=2", want_varref=True)
self.continue_to_breakpoint(breakpoint_8)
# Test memory read, especially with 'empty' repeat commands.
if context == "repl":
self.assertEvaluate(
"memory read -c 1 &my_ints", ".* 05 .*\n", want_memref=False
)
self.assertEvaluate("", ".* 0a .*\n", want_memref=False)
self.assertEvaluate("", ".* 0f .*\n", want_memref=False)
self.assertEvaluate("", ".* 14 .*\n", want_memref=False)
self.assertEvaluate("", ".* 19 .*\n", want_memref=False)
self.continue_to_exit()
@skipIfWindows
def test_generic_evaluate_expressions(self):
# Tests context-less expression evaluations
self.run_test_evaluate_expressions(enableAutoVariableSummaries=False)
@skipIfWindows
def test_repl_evaluate_expressions(self):
# Tests expression evaluations that are triggered from the Debug Console
self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=False)
@skipIfWindows
def test_watch_evaluate_expressions(self):
# Tests expression evaluations that are triggered from a watch expression
self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=True)
@skipIfWindows
def test_hover_evaluate_expressions(self):
# Tests expression evaluations that are triggered when hovering on the editor
self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=False)
@skipIfWindows
def test_variable_evaluate_expressions(self):
# Tests expression evaluations that are triggered in the variable explorer
self.run_test_evaluate_expressions(
"variables", enableAutoVariableSummaries=True
)