327 lines · python
1"""2Test option and argument definitions in parsed script commands3"""4 5 6import sys7import os8import lldb9from lldbsuite.test.decorators import *10from lldbsuite.test.lldbtest import *11 12 13class ParsedCommandTestCase(TestBase):14 NO_DEBUG_INFO_TESTCASE = True15 16 def test(self):17 self.pycmd_tests()18 19 def setUp(self):20 TestBase.setUp(self)21 self.stdin_path = self.getBuildArtifact("stdin.txt")22 self.stdout_path = self.getBuildArtifact("stdout.txt")23 24 def check_help_options(self, cmd_name, opt_list, substrs=[]):25 """26 Pass the command name in cmd_name and a vector of the short option, type & long option.27 This will append the checks for all the options and test "help command".28 Any strings already in substrs will also be checked.29 Any element in opt list that begin with "+" will be added to the checked strings as is.30 """31 for elem in opt_list:32 if elem[0] == "+":33 substrs.append(elem[1:])34 else:35 (short_opt, type, long_opt) = elem36 substrs.append(f"-{short_opt} <{type}> ( --{long_opt} <{type}> )")37 38 self.expect("help " + cmd_name, substrs=substrs)39 40 def run_one_repeat(self, commands, expected_num_errors):41 with open(self.stdin_path, "w") as input_handle:42 input_handle.write(commands)43 44 in_fileH = open(self.stdin_path, "r")45 self.dbg.SetInputFileHandle(in_fileH, False)46 47 out_fileH = open(self.stdout_path, "w")48 self.dbg.SetOutputFileHandle(out_fileH, False)49 self.dbg.SetErrorFileHandle(out_fileH, False)50 51 options = lldb.SBCommandInterpreterRunOptions()52 options.SetEchoCommands(False)53 options.SetPrintResults(True)54 options.SetPrintErrors(True)55 options.SetAllowRepeats(True)56 57 n_errors, quit_requested, has_crashed = self.dbg.RunCommandInterpreter(58 True, False, options, 0, False, False59 )60 61 in_fileH.close()62 out_fileH.close()63 64 results = None65 with open(self.stdout_path, "r") as out_fileH:66 results = out_fileH.read()67 68 self.assertEqual(n_errors, expected_num_errors)69 70 return results71 72 def handle_completion(73 self,74 cmd_str,75 exp_num_completions,76 exp_matches,77 exp_descriptions,78 match_description,79 ):80 matches = lldb.SBStringList()81 descriptions = lldb.SBStringList()82 83 interp = self.dbg.GetCommandInterpreter()84 num_completions = interp.HandleCompletionWithDescriptions(85 cmd_str, len(cmd_str), 0, 1000, matches, descriptions86 )87 self.assertEqual(88 num_completions, exp_num_completions, "Number of completions is right."89 )90 num_matches = matches.GetSize()91 self.assertEqual(92 num_matches,93 exp_matches.GetSize(),94 "matches and expected matches of different lengths",95 )96 num_descriptions = descriptions.GetSize()97 if match_description:98 self.assertEqual(99 num_descriptions,100 exp_descriptions.GetSize(),101 "descriptions and expected of different lengths",102 )103 104 self.assertEqual(105 matches.GetSize(),106 num_completions + 1,107 "The first element is the complete additional text",108 )109 110 for idx in range(0, num_matches):111 match = matches.GetStringAtIndex(idx)112 exp_match = exp_matches.GetStringAtIndex(idx)113 self.assertEqual(114 match, exp_match, f"{match} did not match expectation: {exp_match}"115 )116 if match_description:117 desc = descriptions.GetStringAtIndex(idx)118 exp_desc = exp_descriptions.GetStringAtIndex(idx)119 self.assertEqual(120 desc, exp_desc, f"{desc} didn't match expectation: {exp_desc}"121 )122 123 def pycmd_tests(self):124 source_dir = self.getSourceDir()125 test_file_path = os.path.join(source_dir, "test_commands.py")126 self.runCmd("command script import " + test_file_path)127 self.expect("help", substrs=["no-args", "one-arg-no-opt", "two-args"])128 129 # Test that we did indeed add these commands as user commands:130 131 # This is the function to remove the custom commands in order to have a132 # clean slate for the next test case.133 def cleanup():134 self.runCmd(135 "command script delete no-args one-arg-no-opt two-args", check=False136 )137 138 # Execute the cleanup function during test case tear down.139 self.addTearDownHook(cleanup)140 141 # First test the no arguments command. Make sure the help is right:142 no_arg_opts = [143 ["b", "boolean", "bool-arg"],144 "+a boolean arg, defaults to True",145 ["d", "filename", "disk-file-name"],146 "+An on disk filename",147 ["e", "none", "enum-option"],148 "+An enum, doesn't actually do anything",149 "+Values: foo | bar | baz",150 ["l", "linenum", "line-num"],151 "+A line number",152 ["s", "shlib-name", "shlib-name"],153 "+A shared library name",154 ]155 substrs = [156 "Example command for use in debugging",157 "Syntax: no-args <cmd-options>",158 ]159 160 self.check_help_options("no-args", no_arg_opts, substrs)161 162 # Make sure the command doesn't accept arguments:163 self.expect(164 "no-args an-arg",165 substrs=["'no-args' doesn't take any arguments."],166 error=True,167 )168 169 # Try setting the bool with the wrong value:170 self.expect(171 "no-args -b Something",172 substrs=["Error setting option: bool-arg to Something"],173 error=True,174 )175 # Try setting the enum to an illegal value as well:176 self.expect(177 "no-args --enum-option Something",178 substrs=["error: Error setting option: enum-option to Something"],179 error=True,180 )181 182 # Check some of the command groups:183 self.expect(184 "no-args -b true -s Something -l 10",185 substrs=["error: invalid combination of options for the given command"],186 error=True,187 )188 189 # Now set the bool arg correctly, note only the first option was set:190 self.expect(191 "no-args -b true",192 substrs=[193 "bool-arg (set: True): True",194 "shlib-name (set: False):",195 "disk-file-name (set: False):",196 "line-num (set: False):",197 "enum-option (set: False):",198 ],199 )200 201 # Now set the enum arg correctly, note only the first option was set:202 self.expect(203 "no-args -e foo",204 substrs=[205 "bool-arg (set: False):",206 "shlib-name (set: False):",207 "disk-file-name (set: False):",208 "line-num (set: False):",209 "enum-option (set: True): foo",210 ],211 )212 # Try a pair together:213 self.expect(214 "no-args -b false -s Something",215 substrs=[216 "bool-arg (set: True): False",217 "shlib-name (set: True): Something",218 "disk-file-name (set: False):",219 "flag-value (set: False):",220 "line-num (set: False):",221 "enum-option (set: False):",222 ],223 )224 # Make sure flag values work:225 self.expect(226 "no-args -b false -s Something -f",227 substrs=[228 "bool-arg (set: True): False",229 "shlib-name (set: True): Something",230 "disk-file-name (set: False):",231 "flag-value (set: True):",232 "line-num (set: False):",233 "enum-option (set: False):",234 ],235 )236 237 # Next try some completion tests:238 239 interp = self.dbg.GetCommandInterpreter()240 matches = lldb.SBStringList()241 descriptions = lldb.SBStringList()242 243 # First try an enum completion:244 # Note - this is an enum so all the values are returned:245 matches.AppendList(["oo ", "foo"], 2)246 247 self.handle_completion("no-args -e f", 1, matches, descriptions, False)248 249 # Now try an internal completer, the on disk file one is handy:250 partial_name = os.path.join(source_dir, "test_")251 cmd_str = f"no-args -d '{partial_name}'"252 253 matches.Clear()254 descriptions.Clear()255 matches.AppendList(["commands.py' ", test_file_path], 2)256 # We don't have descriptions for the file path completer:257 self.handle_completion(cmd_str, 1, matches, descriptions, False)258 259 # Try a command with arguments.260 # FIXME: It should be enough to define an argument and it's type to get the completer261 # wired up for that argument type if it is a known type. But that isn't wired up in the262 # command parser yet, so I don't have any tests for that. We also don't currently check263 # that the arguments passed match the argument specifications, so here I just pass a couple264 # sets of arguments and make sure we get back what we put in:265 self.expect(266 "two-args 'First Argument' 'Second Argument'",267 substrs=["0: First Argument", "1: Second Argument"],268 )269 270 # Now test custom completions - two-args has both option and arg completers. In both271 # completers we return different values if the -p option is set, so we can test that too:272 matches.Clear()273 descriptions.Clear()274 cmd_str = "two-args -p something -c other_"275 matches.AppendString("something ")276 matches.AppendString("other_something")277 # This is a full match so no descriptions:278 self.handle_completion(cmd_str, 1, matches, descriptions, False)279 280 matches.Clear()281 descriptions.Clear()282 cmd_str = "two-args -c other_"283 matches.AppendList(["", "other_nice", "other_not_nice", "other_mediocre"], 4)284 # The option doesn't return descriptions either:285 self.handle_completion(cmd_str, 3, matches, descriptions, False)286 287 # Now try the argument - it says "no completions" if the proc_name was set:288 matches.Clear()289 descriptions.Clear()290 cmd_str = "two-args -p something arg"291 matches.AppendString("")292 self.handle_completion(cmd_str, 0, matches, descriptions, False)293 294 cmd_str = "two-args arg_"295 matches.Clear()296 descriptions.Clear()297 matches.AppendList(["", "arg_cool", "arg_yuck"], 3)298 descriptions.AppendList(["", "good idea", "bad idea"], 3)299 self.handle_completion(cmd_str, 2, matches, descriptions, True)300 301 # This one gets a single unique match:302 cmd_str = "two-args correct_"303 matches.Clear()304 descriptions.Clear()305 matches.AppendList(["answer ", "correct_answer"], 2)306 self.handle_completion(cmd_str, 1, matches, descriptions, False)307 308 # Now make sure get_repeat_command works properly:309 310 # no-args turns off auto-repeat311 results = self.run_one_repeat("no-args\n\n", 1)312 self.assertIn("no auto repeat", results, "Got auto-repeat error")313 314 # one-args does the normal repeat315 results = self.run_one_repeat("one-arg-no-opt ONE_ARG\n\n", 0)316 self.assertEqual(results.count("ONE_ARG"), 2, "We did a normal repeat")317 318 # two-args adds an argument:319 results = self.run_one_repeat("two-args FIRST_ARG SECOND_ARG\n\n", 0)320 self.assertEqual(321 results.count("FIRST_ARG"), 2, "Passed first arg to both commands"322 )323 self.assertEqual(324 results.count("SECOND_ARG"), 2, "Passed second arg to both commands"325 )326 self.assertEqual(results.count("THIRD_ARG"), 1, "Passed third arg in repeat")327