"""
Test dwim-print with variables, variable paths, and expressions.
"""
import re
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
class TestCase(TestBase):
def _run_cmd(self, cmd: str) -> str:
"""Run the given lldb command and return its output."""
result = lldb.SBCommandReturnObject()
self.ci.HandleCommand(cmd, result)
return result.GetOutput().rstrip()
VAR_IDENT = re.compile(r"(?:\$\d+|\w+) = ")
def _strip_result_var(self, string: str) -> str:
"""
Strip (persistent) result variables (ex '$0 = ', or 'someVar = ', etc).
This allows for using the output of `expression`/`frame variable`, to
compare it to `dwim-print` output, which disables result variables.
"""
return self.VAR_IDENT.subn("", string, 1)[0]
def _expect_cmd(
self,
dwim_cmd: str,
actual_cmd: str,
) -> None:
"""Run dwim-print and verify the output against the expected command."""
# Resolve the dwim-print command to either `expression` or `frame variable`.
substitute_cmd = dwim_cmd.replace("dwim-print", actual_cmd, 1)
interp = self.dbg.GetCommandInterpreter()
result = lldb.SBCommandReturnObject()
interp.ResolveCommand(substitute_cmd, result)
self.assertTrue(result.Succeeded(), result.GetError())
resolved_cmd = result.GetOutput()
if actual_cmd == "frame variable":
resolved_cmd = resolved_cmd.replace(" -- ", " ", 1)
resolved_cmd_output = self._run_cmd(resolved_cmd)
dwim_cmd_output = self._strip_result_var(resolved_cmd_output)
# Verify dwim-print chose the expected command.
self.runCmd("settings set dwim-print-verbosity full")
self.expect(
dwim_cmd,
substrs=[
f"note: ran `{resolved_cmd}`",
dwim_cmd_output,
],
)
def test_variables(self):
"""Test dwim-print with variables."""
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
vars = ("argc", "argv")
for var in vars:
self._expect_cmd(f"dwim-print {var}", "frame variable")
def test_variable_paths(self):
"""Test dwim-print with variable path expressions."""
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
exprs = ("&argc", "*argv", "argv[0]")
for expr in exprs:
self._expect_cmd(f"dwim-print {expr}", "expression")
def test_expressions(self):
"""Test dwim-print with expressions."""
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
exprs = ("argc + 1", "(void)argc", "(int)abs(argc)")
for expr in exprs:
self._expect_cmd(f"dwim-print {expr}", "expression")
def test_dummy_target_expressions(self):
"""Test dwim-print's ability to evaluate expressions without a target."""
self._expect_cmd("dwim-print 1 + 2", "expression")
def test_gdb_format(self):
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
self._expect_cmd(f"dwim-print/x argc", "frame variable")
self._expect_cmd(f"dwim-print/x argc + 1", "expression")
def test_format_flags(self):
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
self._expect_cmd(f"dwim-print -fx -- argc", "frame variable")
self._expect_cmd(f"dwim-print -fx -- argc + 1", "expression")
def test_display_flags(self):
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
self._expect_cmd(f"dwim-print -T -- argc", "frame variable")
self._expect_cmd(f"dwim-print -T -- argc + 1", "expression")
def test_expression_language(self):
"""Test that the language flag doesn't affect the choice of command."""
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
self._expect_cmd(f"dwim-print -l c++ -- argc", "frame variable")
self._expect_cmd(f"dwim-print -l c++ -- argc + 1", "expression")
def test_empty_expression(self):
self.build()
lldbutil.run_to_name_breakpoint(self, "main")
error_msg = "error: 'dwim-print' takes a variable or expression"
self.expect(f"dwim-print", error=True, startstr=error_msg)
self.expect(f"dwim-print -- ", error=True, startstr=error_msg)
def test_nested_values(self):
"""Test dwim-print with nested values (structs, etc)."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.runCmd("settings set auto-one-line-summaries false")
self._expect_cmd(f"dwim-print s", "frame variable")
self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
def test_summary_strings(self):
"""Test dwim-print with nested values (structs, etc)."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.runCmd("settings set auto-one-line-summaries false")
self.runCmd("type summary add -e -s 'stub summary' Structure")
self._expect_cmd(f"dwim-print s", "frame variable")
self._expect_cmd(f"dwim-print (struct Structure)s", "expression")
def test_void_result(self):
"""Test dwim-print does not surface an error message for void expressions."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.expect("dwim-print (void)15", matching=False, patterns=["(?i)error"])
def test_preserves_persistent_variables(self):
"""Test dwim-print does not delete persistent variables."""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.expect("dwim-print int $i = 15")
# Run the same expression twice and verify success. This ensures the
# first command does not delete the persistent variable.
for _ in range(2):
self.expect("dwim-print $i", startstr="(int) 15")
def test_missing_type(self):
"""The expected output of po opaque is its address (no error)"""
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.expect("dwim-print -O -- opaque", substrs=["0x"])