366 lines · python
1"""2Test lldb-dap evaluate request3"""4 5import re6 7import lldbdap_testcase8from lldbsuite.test.decorators import skipIfWindows9from lldbsuite.test.lldbtest import line_number10from typing import TypedDict, Optional11 12 13class EvaluateResponseBody(TypedDict, total=False):14 result: str15 variablesReference: int16 type: Optional[str]17 memoryReference: Optional[str]18 valueLocationReference: Optional[int]19 20 21class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase):22 def assertEvaluate(23 self,24 expression,25 result: str,26 want_type="",27 want_varref=False,28 want_memref=True,29 want_locref=False,30 is_hex=None,31 ):32 resp = self.dap_server.request_evaluate(33 expression, context=self.context, is_hex=is_hex34 )35 self.assertTrue(36 resp["success"], f"Failed to evaluate expression {expression!r}"37 )38 body: EvaluateResponseBody = resp["body"]39 self.assertRegex(40 body["result"],41 result,42 f"Unexpected 'result' for expression {expression!r} in response body {body}",43 )44 if want_varref:45 self.assertNotEqual(46 body["variablesReference"],47 0,48 f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",49 )50 else:51 self.assertEqual(52 body["variablesReference"],53 0,54 f"Unexpected 'variablesReference' for expression {expression!r} in response body {body}",55 )56 if want_type:57 self.assertEqual(58 body["type"],59 want_type,60 f"Unexpected 'type' for expression {expression!r} in response body {body}",61 )62 if want_memref:63 self.assertIn(64 "memoryReference",65 body,66 f"Unexpected 'memoryReference' for expression {expression!r} in response body {body}",67 )68 if want_locref:69 self.assertIn(70 "valueLocationReference",71 body,72 f"Unexpected 'valueLocationReference' for expression {expression!r} in response body {body}",73 )74 75 def assertEvaluateFailure(self, expression):76 self.assertNotIn(77 "result",78 self.dap_server.request_evaluate(expression, context=self.context)["body"],79 )80 81 def isResultExpandedDescription(self):82 return self.context == "repl"83 84 def isExpressionParsedExpected(self):85 return self.context != "hover"86 87 def run_test_evaluate_expressions(88 self, context=None, enableAutoVariableSummaries=False89 ):90 """91 Tests the evaluate expression request at different breakpoints92 """93 self.context = context94 program = self.getBuildArtifact("a.out")95 self.build_and_launch(96 program,97 enableAutoVariableSummaries=enableAutoVariableSummaries,98 )99 source = "main.cpp"100 breakpoint_lines = [101 line_number(source, "// breakpoint 1"),102 line_number(source, "// breakpoint 2"),103 line_number(source, "// breakpoint 3"),104 line_number(source, "// breakpoint 4"),105 line_number(source, "// breakpoint 5"),106 line_number(source, "// breakpoint 6"),107 line_number(source, "// breakpoint 7"),108 line_number(source, "// breakpoint 8"),109 ]110 breakpoint_ids = self.set_source_breakpoints(source, breakpoint_lines)111 112 self.assertEqual(113 len(breakpoint_ids),114 len(breakpoint_lines),115 "Did not resolve all the breakpoints.",116 )117 breakpoint_1 = breakpoint_ids[0]118 breakpoint_2 = breakpoint_ids[1]119 breakpoint_3 = breakpoint_ids[2]120 breakpoint_4 = breakpoint_ids[3]121 breakpoint_5 = breakpoint_ids[4]122 breakpoint_6 = breakpoint_ids[5]123 breakpoint_7 = breakpoint_ids[6]124 breakpoint_8 = breakpoint_ids[7]125 self.continue_to_breakpoint(breakpoint_1)126 127 # Expressions at breakpoint 1, which is in main128 self.assertEvaluate("var1", "20", want_type="int")129 # Empty expression should equate to the previous expression.130 if context == "repl":131 self.assertEvaluate("", "20")132 else:133 self.assertEvaluateFailure("")134 self.assertEvaluate("var2", "21", want_type="int")135 if context == "repl":136 self.assertEvaluate("", "21", want_type="int")137 self.assertEvaluate("", "21", want_type="int")138 self.assertEvaluate("static_int", "0x0000002a", want_type="int", is_hex=True)139 self.assertEvaluate(140 "non_static_int", "0x0000002b", want_type="int", is_hex=True141 )142 self.assertEvaluate("struct1.foo", "0x0000000f", want_type="int", is_hex=True)143 self.assertEvaluate("struct2->foo", "0x00000010", want_type="int", is_hex=True)144 self.assertEvaluate("static_int", "42", want_type="int")145 self.assertEvaluate("non_static_int", "43", want_type="int")146 self.assertEvaluate("struct1.foo", "15", want_type="int")147 self.assertEvaluate("struct2->foo", "16", want_type="int")148 149 if self.isResultExpandedDescription():150 self.assertEvaluate(151 "struct1",152 r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",153 want_type="my_struct",154 want_varref=True,155 )156 self.assertEvaluate(157 "struct2",158 r"\(my_struct \*\) (struct2|\$\d+) = 0x.*",159 want_type="my_struct *",160 want_varref=True,161 )162 self.assertEvaluate(163 "struct3",164 r"\(my_struct \*\) (struct3|\$\d+) = nullptr",165 want_type="my_struct *",166 want_varref=True,167 )168 else:169 self.assertEvaluate(170 "struct1",171 (172 re.escape("{foo:15}")173 if enableAutoVariableSummaries174 else "my_struct @ 0x"175 ),176 want_varref=True,177 )178 self.assertEvaluate(179 "struct2",180 "0x.* {foo:16}" if enableAutoVariableSummaries else "0x.*",181 want_varref=True,182 want_type="my_struct *",183 )184 self.assertEvaluate(185 "struct3", "0x.*0", want_varref=True, want_type="my_struct *"186 )187 188 if context == "repl":189 # In the repl context expressions may be interpreted as lldb190 # commands since no variables have the same name as the command.191 self.assertEvaluate("list", r".*", want_memref=False)192 else:193 self.assertEvaluateFailure("list") # local variable of a_function194 195 self.assertEvaluateFailure("my_struct") # type name196 self.assertEvaluateFailure("int") # type name197 self.assertEvaluateFailure("foo") # member of my_struct198 199 if self.isExpressionParsedExpected():200 self.assertEvaluate(201 "a_function",202 "0x.*a.out`a_function.*",203 want_type="int (*)(int)",204 want_varref=True,205 want_memref=False,206 want_locref=True,207 )208 self.assertEvaluate(209 "a_function(1)", "1", want_memref=False, want_type="int"210 )211 self.assertEvaluate("var2 + struct1.foo", "36", want_memref=False)212 self.assertEvaluate(213 "foo_func",214 "0x.*a.out`foo_func.*",215 want_type="int (*)()",216 want_varref=True,217 want_memref=False,218 want_locref=True,219 )220 self.assertEvaluate("foo_var", "44")221 else:222 self.assertEvaluateFailure("a_function")223 self.assertEvaluateFailure("a_function(1)")224 self.assertEvaluateFailure("var2 + struct1.foo")225 self.assertEvaluateFailure("foo_func")226 self.assertEvaluate("foo_var", "44")227 228 # Expressions at breakpoint 2, which is an anonymous block229 self.continue_to_breakpoint(breakpoint_2)230 self.assertEvaluate("var1", "20")231 self.assertEvaluate("var2", "2") # different variable with the same name232 self.assertEvaluate("static_int", "42")233 self.assertEvaluate(234 "non_static_int", "10"235 ) # different variable with the same name236 if self.isResultExpandedDescription():237 self.assertEvaluate(238 "struct1",239 r"\(my_struct\) (struct1|\$\d+) = \(foo = 15\)",240 want_type="my_struct",241 want_varref=True,242 )243 else:244 self.assertEvaluate(245 "struct1",246 (247 re.escape("{foo:15}")248 if enableAutoVariableSummaries249 else "my_struct @ 0x"250 ),251 want_type="my_struct",252 want_varref=True,253 )254 self.assertEvaluate("struct1.foo", "15")255 self.assertEvaluate("struct2->foo", "16")256 257 if self.isExpressionParsedExpected():258 self.assertEvaluate(259 "a_function",260 "0x.*a.out`a_function.*",261 want_type="int (*)(int)",262 want_varref=True,263 want_memref=False,264 want_locref=True,265 )266 self.assertEvaluate("a_function(1)", "1", want_memref=False)267 self.assertEvaluate("var2 + struct1.foo", "17", want_memref=False)268 self.assertEvaluate(269 "foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False270 )271 self.assertEvaluate("foo_var", "44")272 else:273 self.assertEvaluateFailure("a_function")274 self.assertEvaluateFailure("a_function(1)")275 self.assertEvaluateFailure("var2 + struct1.foo")276 self.assertEvaluateFailure("foo_func")277 self.assertEvaluate("foo_var", "44")278 279 # Expressions at breakpoint 3, which is inside a_function280 self.continue_to_breakpoint(breakpoint_3)281 self.assertEvaluate("list", "42")282 self.assertEvaluate("static_int", "42")283 self.assertEvaluate("non_static_int", "43")284 285 self.assertEvaluateFailure("var1")286 self.assertEvaluateFailure("var2")287 self.assertEvaluateFailure("struct1")288 self.assertEvaluateFailure("struct1.foo")289 self.assertEvaluateFailure("struct2->foo")290 self.assertEvaluateFailure("var2 + struct1.foo")291 292 if self.isExpressionParsedExpected():293 self.assertEvaluate(294 "a_function",295 "0x.*a.out`a_function.*",296 want_varref=True,297 want_memref=False,298 want_locref=True,299 )300 self.assertEvaluate("a_function(1)", "1", want_memref=False)301 self.assertEvaluate("list + 1", "43", want_memref=False)302 self.assertEvaluate(303 "foo_func", "0x.*a.out`foo_func.*", want_varref=True, want_memref=False304 )305 self.assertEvaluate("foo_var", "44")306 else:307 self.assertEvaluateFailure("a_function")308 self.assertEvaluateFailure("a_function(1)")309 self.assertEvaluateFailure("list + 1")310 self.assertEvaluateFailure("foo_func")311 self.assertEvaluate("foo_var", "44")312 313 # Now we check that values are updated after stepping314 self.continue_to_breakpoint(breakpoint_4)315 self.assertEvaluate("my_vec", "size=2", want_varref=True)316 self.continue_to_breakpoint(breakpoint_5)317 self.assertEvaluate("my_vec", "size=3", want_varref=True)318 319 self.assertEvaluate("my_map", "size=2", want_varref=True)320 self.continue_to_breakpoint(breakpoint_6)321 self.assertEvaluate("my_map", "size=3", want_varref=True)322 323 self.assertEvaluate("my_bool_vec", "size=1", want_varref=True)324 self.continue_to_breakpoint(breakpoint_7)325 self.assertEvaluate("my_bool_vec", "size=2", want_varref=True)326 327 self.continue_to_breakpoint(breakpoint_8)328 # Test memory read, especially with 'empty' repeat commands.329 if context == "repl":330 self.assertEvaluate(331 "memory read -c 1 &my_ints", ".* 05 .*\n", want_memref=False332 )333 self.assertEvaluate("", ".* 0a .*\n", want_memref=False)334 self.assertEvaluate("", ".* 0f .*\n", want_memref=False)335 self.assertEvaluate("", ".* 14 .*\n", want_memref=False)336 self.assertEvaluate("", ".* 19 .*\n", want_memref=False)337 338 self.continue_to_exit()339 340 @skipIfWindows341 def test_generic_evaluate_expressions(self):342 # Tests context-less expression evaluations343 self.run_test_evaluate_expressions(enableAutoVariableSummaries=False)344 345 @skipIfWindows346 def test_repl_evaluate_expressions(self):347 # Tests expression evaluations that are triggered from the Debug Console348 self.run_test_evaluate_expressions("repl", enableAutoVariableSummaries=False)349 350 @skipIfWindows351 def test_watch_evaluate_expressions(self):352 # Tests expression evaluations that are triggered from a watch expression353 self.run_test_evaluate_expressions("watch", enableAutoVariableSummaries=True)354 355 @skipIfWindows356 def test_hover_evaluate_expressions(self):357 # Tests expression evaluations that are triggered when hovering on the editor358 self.run_test_evaluate_expressions("hover", enableAutoVariableSummaries=False)359 360 @skipIfWindows361 def test_variable_evaluate_expressions(self):362 # Tests expression evaluations that are triggered in the variable explorer363 self.run_test_evaluate_expressions(364 "variables", enableAutoVariableSummaries=True365 )366