| """ | |
| Test the lldb command line completion mechanism for the 'expr' command. | |
| """ | |
| import lldb | |
| from lldbsuite.test.decorators import * | |
| from lldbsuite.test.lldbtest import * | |
| from lldbsuite.test import lldbplatform | |
| from lldbsuite.test import lldbutil | |
| class CommandLineExprCompletionTestCase(TestBase): | |
| NO_DEBUG_INFO_TESTCASE = True | |
| def test_expr_completion(self): | |
| self.build() | |
| self.main_source = "main.cpp" | |
| self.main_source_spec = lldb.SBFileSpec(self.main_source) | |
| self.createTestTarget() | |
| # Try the completion before we have a context to complete on. | |
| self.assume_no_completions("expr some_expr") | |
| self.assume_no_completions("expr ") | |
| self.assume_no_completions("expr f") | |
| (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( | |
| self, "// Break here", self.main_source_spec | |
| ) | |
| # Completing member functions | |
| self.complete_from_to( | |
| "expr some_expr.FooNoArgs", "expr some_expr.FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.FooWithArgs", "expr some_expr.FooWithArgsBar(" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.FooWithMultipleArgs", | |
| "expr some_expr.FooWithMultipleArgsBar(", | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.FooUnderscore", "expr some_expr.FooUnderscoreBar_()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.FooNumbers", "expr some_expr.FooNumbersBar1()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.StaticMemberMethod", | |
| "expr some_expr.StaticMemberMethodBar()", | |
| ) | |
| # Completing static functions | |
| self.complete_from_to( | |
| "expr Expr::StaticMemberMethod", "expr Expr::StaticMemberMethodBar()" | |
| ) | |
| # Completing member variables | |
| self.complete_from_to( | |
| "expr some_expr.MemberVariab", "expr some_expr.MemberVariableBar" | |
| ) | |
| # Multiple completions | |
| self.completions_contain( | |
| "expr some_expr.", | |
| [ | |
| "some_expr.FooNumbersBar1()", | |
| "some_expr.FooUnderscoreBar_()", | |
| "some_expr.FooWithArgsBar(", | |
| "some_expr.MemberVariableBar", | |
| ], | |
| ) | |
| self.completions_contain( | |
| "expr some_expr.Foo", | |
| [ | |
| "some_expr.FooNumbersBar1()", | |
| "some_expr.FooUnderscoreBar_()", | |
| "some_expr.FooWithArgsBar(", | |
| ], | |
| ) | |
| self.completions_contain( | |
| "expr ", ["static_cast", "reinterpret_cast", "dynamic_cast"] | |
| ) | |
| self.completions_contain( | |
| "expr 1 + ", ["static_cast", "reinterpret_cast", "dynamic_cast"] | |
| ) | |
| # Completion expr without spaces | |
| # This is a bit awkward looking for the user, but that's how | |
| # the completion API works at the moment. | |
| self.completions_contain("expr 1+", ["1+some_expr", "1+static_cast"]) | |
| # Test with spaces | |
| self.complete_from_to( | |
| "expr some_expr .FooNoArgs", "expr some_expr .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr .FooNoArgs", "expr some_expr .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr .FooNoArgs", "expr some_expr .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr. FooNoArgs", "expr some_expr. FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr . FooNoArgs", "expr some_expr . FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr Expr :: StaticMemberMethod", "expr Expr :: StaticMemberMethodBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr Expr ::StaticMemberMethod", "expr Expr ::StaticMemberMethodBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr Expr:: StaticMemberMethod", "expr Expr:: StaticMemberMethodBar()" | |
| ) | |
| # Test that string literals don't break our parsing logic. | |
| self.complete_from_to( | |
| 'expr const char *cstr = "some_e"; char c = *cst', | |
| 'expr const char *cstr = "some_e"; char c = *cstr', | |
| ) | |
| self.complete_from_to( | |
| 'expr const char *cstr = "some_e" ; char c = *cst', | |
| 'expr const char *cstr = "some_e" ; char c = *cstr', | |
| ) | |
| # Requesting completions inside an incomplete string doesn't provide any | |
| # completions. | |
| self.complete_from_to( | |
| 'expr const char *cstr = "some_e', 'expr const char *cstr = "some_e' | |
| ) | |
| # Completing inside double dash should do nothing | |
| self.assume_no_completions("expr -i0 -- some_expr.", 10) | |
| self.assume_no_completions("expr -i0 -- some_expr.", 11) | |
| # Test with expr arguments | |
| self.complete_from_to( | |
| "expr -i0 -- some_expr .FooNoArgs", "expr -i0 -- some_expr .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr -i0 -- some_expr .FooNoArgs", | |
| "expr -i0 -- some_expr .FooNoArgsBar()", | |
| ) | |
| # Addrof and deref | |
| self.complete_from_to( | |
| "expr (*(&some_expr)).FooNoArgs", "expr (*(&some_expr)).FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr (*(&some_expr)) .FooNoArgs", "expr (*(&some_expr)) .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr (* (&some_expr)) .FooNoArgs", "expr (* (&some_expr)) .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr (* (& some_expr)) .FooNoArgs", | |
| "expr (* (& some_expr)) .FooNoArgsBar()", | |
| ) | |
| # Addrof and deref (part 2) | |
| self.complete_from_to( | |
| "expr (&some_expr)->FooNoArgs", "expr (&some_expr)->FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr (&some_expr) ->FooNoArgs", "expr (&some_expr) ->FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr (&some_expr) -> FooNoArgs", "expr (&some_expr) -> FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr (&some_expr)-> FooNoArgs", "expr (&some_expr)-> FooNoArgsBar()" | |
| ) | |
| # Builtin arg | |
| self.complete_from_to("expr static_ca", "expr static_cast") | |
| # From other files | |
| self.complete_from_to( | |
| "expr fwd_decl_ptr->Hidden", "expr fwd_decl_ptr->HiddenMember" | |
| ) | |
| # Types | |
| self.complete_from_to("expr LongClassNa", "expr LongClassName") | |
| self.complete_from_to( | |
| "expr LongNamespaceName::NestedCla", "expr LongNamespaceName::NestedClass" | |
| ) | |
| # Namespaces | |
| self.complete_from_to("expr LongNamespaceNa", "expr LongNamespaceName::") | |
| # Multiple arguments | |
| self.complete_from_to( | |
| "expr &some_expr + &some_e", "expr &some_expr + &some_expr" | |
| ) | |
| self.complete_from_to( | |
| "expr SomeLongVarNameWithCapitals + SomeLongVarName", | |
| "expr SomeLongVarNameWithCapitals + SomeLongVarNameWithCapitals", | |
| ) | |
| self.complete_from_to( | |
| "expr SomeIntVar + SomeIntV", "expr SomeIntVar + SomeIntVar" | |
| ) | |
| # Multiple statements | |
| self.complete_from_to( | |
| "expr long LocalVariable = 0; LocalVaria", | |
| "expr long LocalVariable = 0; LocalVariable", | |
| ) | |
| # Custom Decls | |
| self.complete_from_to( | |
| "expr auto l = [](int LeftHandSide, int bx){ return LeftHandS", | |
| "expr auto l = [](int LeftHandSide, int bx){ return LeftHandSide", | |
| ) | |
| self.complete_from_to( | |
| "expr struct LocalStruct { long MemberName; } ; LocalStruct S; S.Mem", | |
| "expr struct LocalStruct { long MemberName; } ; LocalStruct S; S.MemberName", | |
| ) | |
| # Completing function call arguments | |
| self.complete_from_to( | |
| "expr some_expr.FooWithArgsBar(some_exp", | |
| "expr some_expr.FooWithArgsBar(some_expr", | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.FooWithArgsBar(SomeIntV", | |
| "expr some_expr.FooWithArgsBar(SomeIntVar", | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.FooWithMultipleArgsBar(SomeIntVar, SomeIntVa", | |
| "expr some_expr.FooWithMultipleArgsBar(SomeIntVar, SomeIntVar", | |
| ) | |
| # Function return values | |
| self.complete_from_to( | |
| "expr some_expr.Self().FooNoArgs", "expr some_expr.Self().FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.Self() .FooNoArgs", "expr some_expr.Self() .FooNoArgsBar()" | |
| ) | |
| self.complete_from_to( | |
| "expr some_expr.Self(). FooNoArgs", "expr some_expr.Self(). FooNoArgsBar()" | |
| ) | |
| self.complete_from_to("expr myVec.__f", "expr myVec.__func()") | |
| self.complete_from_to("expr myVec._F", "expr myVec._Func()") | |
| self.complete_from_to("expr myVec.__m", "expr myVec.__mem") | |
| self.complete_from_to("expr myVec._M", "expr myVec._Mem") | |
| def test_expr_completion_with_descriptions(self): | |
| self.build() | |
| self.main_source = "main.cpp" | |
| self.main_source_spec = lldb.SBFileSpec(self.main_source) | |
| self.createTestTarget() | |
| (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( | |
| self, "// Break here", self.main_source_spec | |
| ) | |
| self.check_completion_with_desc( | |
| "expr ", | |
| [ | |
| # builtin types have no description. | |
| ["int", ""], | |
| ["float", ""], | |
| # VarDecls have their type as description. | |
| ["some_expr", "Expr &"], | |
| ], | |
| enforce_order=True, | |
| ) | |
| self.check_completion_with_desc( | |
| "expr some_expr.", | |
| [ | |
| # Functions have their signature as description. | |
| ["some_expr.~Expr()", "inline ~Expr()"], | |
| ["some_expr.operator=(", "inline Expr &operator=(const Expr &)"], | |
| # FieldDecls have their type as description. | |
| ["some_expr.MemberVariableBar", "int"], | |
| [ | |
| "some_expr.StaticMemberMethodBar()", | |
| "static int StaticMemberMethodBar()", | |
| ], | |
| ["some_expr.Self()", "Expr &Self()"], | |
| ["some_expr.FooNoArgsBar()", "int FooNoArgsBar()"], | |
| ["some_expr.FooWithArgsBar(", "int FooWithArgsBar(int)"], | |
| ["some_expr.FooNumbersBar1()", "int FooNumbersBar1()"], | |
| ["some_expr.FooUnderscoreBar_()", "int FooUnderscoreBar_()"], | |
| [ | |
| "some_expr.FooWithMultipleArgsBar(", | |
| "int FooWithMultipleArgsBar(int, int)", | |
| ], | |
| ], | |
| enforce_order=True, | |
| ) | |
| def test_expr_completion_max_results(self): | |
| self.build() | |
| self.main_source = "main.cpp" | |
| self.main_source_spec = lldb.SBFileSpec(self.main_source) | |
| self.createTestTarget() | |
| (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( | |
| self, "// Break here", self.main_source_spec | |
| ) | |
| expected_completions = [ | |
| "some_expr.~Expr()", | |
| "some_expr.operator=(", # Copy operator | |
| "some_expr.operator=(", # Move operator | |
| "some_expr.MemberVariableBar", | |
| "some_expr.StaticMemberMethodBar()", | |
| "some_expr.Self()", | |
| "some_expr.FooNoArgsBar()", | |
| "some_expr.FooWithArgsBar(", | |
| "some_expr.FooNumbersBar1()", | |
| "some_expr.FooUnderscoreBar_()", | |
| "some_expr.FooWithMultipleArgsBar(", | |
| ] | |
| for i in range(1, len(expected_completions)): | |
| self.completions_match( | |
| "expr some_expr.", | |
| expected_completions[:i], | |
| max_completions=i, | |
| ) | |
| def assume_no_completions(self, str_input, cursor_pos=None): | |
| interp = self.dbg.GetCommandInterpreter() | |
| match_strings = lldb.SBStringList() | |
| if cursor_pos is None: | |
| cursor_pos = len(str_input) | |
| num_matches = interp.HandleCompletion( | |
| str_input, cursor_pos, 0, -1, match_strings | |
| ) | |
| available_completions = [] | |
| for m in match_strings: | |
| available_completions.append(m) | |
| self.assertEqual( | |
| num_matches, | |
| 0, | |
| "Got matches, but didn't expect any: " + str(available_completions), | |
| ) | |
| def completions_contain(self, str_input, items): | |
| interp = self.dbg.GetCommandInterpreter() | |
| match_strings = lldb.SBStringList() | |
| num_matches = interp.HandleCompletion( | |
| str_input, len(str_input), 0, -1, match_strings | |
| ) | |
| common_match = match_strings.GetStringAtIndex(0) | |
| for item in items: | |
| found = False | |
| for m in match_strings: | |
| if m == item: | |
| found = True | |
| if not found: | |
| # Transform match_strings to a python list with strings | |
| available_completions = [] | |
| for m in match_strings: | |
| available_completions.append(m) | |
| self.assertTrue( | |
| found, | |
| "Couldn't find completion " | |
| + item | |
| + " in completions " | |
| + str(available_completions), | |
| ) |