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