Index: lldb/packages/Python/lldbsuite/test/commands/expression/call-function/TestCallBuiltinFunction.py =================================================================== --- lldb/packages/Python/lldbsuite/test/commands/expression/call-function/TestCallBuiltinFunction.py +++ lldb/packages/Python/lldbsuite/test/commands/expression/call-function/TestCallBuiltinFunction.py @@ -39,7 +39,7 @@ # Test different builtin functions. - self.expect("expr __builtin_isinf(0.0f)", substrs=["(int) $", " = 0\n"]) - self.expect("expr __builtin_isnormal(0.0f)", substrs=["(int) $", " = 0\n"]) - self.expect("expr __builtin_constant_p(1)", substrs=["(int) $", " = 1\n"]) - self.expect("expr __builtin_abs(-14)", substrs=["(int) $", " = 14\n"]) + self.expect_expr("__builtin_isinf(0.0f)", result_type="int", result_value="0") + self.expect_expr("__builtin_isnormal(0.0f)", result_type="int", result_value="0") + self.expect_expr("__builtin_constant_p(1)", result_type="int", result_value="1") + self.expect_expr("__builtin_abs(-14)", result_type="int", result_value="14") Index: lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py =================================================================== --- lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py +++ lldb/packages/Python/lldbsuite/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py @@ -51,28 +51,11 @@ self.addTearDownHook(cleanup) ns = self.namespace - self.expect('p ii', - substrs=['%s::map' % ns, - 'size=0', - '{}']) - self.expect('frame var ii', - substrs=['%s::map' % ns, - 'size=0', - '{}']) + self.expect_simple_expr('ii', result_type=['%s::map' % ns], result_value=['size=0', "{}"]) lldbutil.continue_to_breakpoint(self.process(), bkpt) - self.expect('p ii', - substrs=['%s::map' % ns, 'size=2', - '[0] = ', - 'first = 0', - 'second = 0', - '[1] = ', - 'first = 1', - 'second = 1']) - - self.expect('frame variable ii', - substrs=['%s::map' % ns, 'size=2', + self.expect_simple_expr('ii', result_type=['%s::map' % ns], result_value=['size=2', '[0] = ', 'first = 0', 'second = 0', @@ -82,8 +65,7 @@ lldbutil.continue_to_breakpoint(self.process(), bkpt) - self.expect('frame variable ii', - substrs=['%s::map' % ns, 'size=4', + self.expect_simple_expr('ii', result_type=['%s::map' % ns], result_value=['size=4', '[2] = ', 'first = 2', 'second = 0', @@ -93,17 +75,7 @@ lldbutil.continue_to_breakpoint(self.process(), bkpt) - self.expect("p ii", - substrs=['%s::map' % ns, 'size=8', - '[5] = ', - 'first = 5', - 'second = 0', - '[7] = ', - 'first = 7', - 'second = 1']) - - self.expect("frame var ii", - substrs=['%s::map' % ns, 'size=8', + self.expect_simple_expr('ii', result_type=['%s::map' % ns], result_value=['size=8', '[5] = ', 'first = 5', 'second = 0', Index: lldb/packages/Python/lldbsuite/test/lldbtest.py =================================================================== --- lldb/packages/Python/lldbsuite/test/lldbtest.py +++ lldb/packages/Python/lldbsuite/test/lldbtest.py @@ -2366,6 +2366,95 @@ self.assertTrue(matched if matching else not matched, msg if msg else EXP_MSG(str, output, exe)) + class ExpressionRunMode: + # Run the expression in the expression evaluator (similar to 'expression'). + EXPRESSION = object() + # Interpret the expression as just a variable that should be printed (similar to 'frame var'). + PRINT_VAR = object() + + def expect_expr( + self, + expr, + result_value=None, + result_type=None, + error_msg=None, + run_mode=ExpressionRunMode.EXPRESSION + ): + """ + Evaluates the given expression and verifies the result. + :param expr: The expression as a string. + :param result_value: The value that the expression should have. None if the value should not be checked. + :param result_type: The type that the expression result should have. None if the type should not be checked. + :param error_msg: The error message the expression should return. None if the error output should not be checked. + :param run_type: How the expression should be run. See ExpressionRunMode. + + result_value, result_type and error_message can have the following types which influences how + their values are compared to their respective output: + * A list of strings: expect_expr will search for the list of strings in the respective output. + The output is expected to contain these strings in the listed order. + * Any string type: expect_expr will assume that the respective output is equal to the given string. + """ + # Utility method that checks result_value, result_type and error_message. + def check_str(outer_self, expected, got): + self.assertIsNotNone(expected) + self.assertIsNotNone(got) + # We got a list, so treat is as a list of needles we need to find in the given order. + if type(expected) is list: + remaining = got + for expected_part in expected: + # Find the expected string. + i = remaining.find(expected_part) + # Assert that we found the string. + outer_self.assertTrue(i != -1, "Couldn't find '" + expected_part + + "' in remaining output '" + remaining + + "'.\nFull string was: '" + got + "'") + # Keep searching only the rest of the string to ensure the + # strings are found in the given order. + remaining = remaining[i + len(expected_part):] + else: # Otherwise we probably got one of Python's many string classes. + outer_self.assertEqual(got, expected) + + self.assertTrue(expr.strip() == expr, "Expression contains trailing/leading whitespace: '" + expr + "'") + + frame = self.dbg.GetTargetAtIndex(0).GetProcess().GetThreadAtIndex(0).GetFrameAtIndex(0) + + if run_mode is self.ExpressionRunMode.PRINT_VAR: + eval_result = frame.FindVariable(expr) + elif run_mode is self.ExpressionRunMode.EXPRESSION: + eval_result = frame.EvaluateExpression(expr) + else: + self.fail("Unknown run mode " + str(run_mode)) + + if error_msg: + self.assertFalse(eval_result.IsValid()) + check_str(self, error_msg, eval_result.GetError().GetCString()) + return + + if not eval_result.GetError().Success(): + self.assertTrue(eval_result.GetError().Success(), + "Unexpected failure with msg: " + eval_result.GetError().GetCString()) + + if result_type: + check_str(self, result_type, eval_result.GetTypeName()) + if result_value: + ss = lldb.SBStream() + eval_result.GetDescription(ss) + description = ss.GetData() + # Strip out the value from the description text that looks like this: + # (the_type) $434 = VALUE\n + value_desc = description.split(" = ", 1)[1].rstrip("\n") + check_str(self, result_value, value_desc) + + def expect_simple_expr(self, var, result_value=None, result_type=None): + """ + Takes a trivial expression such as a plain variable name or accessing a member ('instance.member') + and verifies that all ways LLDB can evaluate these expressions do it correctly + (e.g., 'frame var', 'expression' commands) + See expect_expr for documentation for result_value and result_type. + """ + self.expect_expr(var, result_value=result_value, result_type=result_type, run_mode=self.ExpressionRunMode.EXPRESSION) + self.expect_expr(var, result_value=result_value, result_type=result_type, run_mode=self.ExpressionRunMode.PRINT_VAR) + def invoke(self, obj, name, trace=False): """Use reflection to call a method dynamically with no argument.""" trace = (True if traceAlways else trace)