OILS / stdlib / ysh / args-test.ysh View on Github | oilshell.org

352 lines, 222 significant
1#!bin/ysh
2
3# TODO: you should only have to pick parser
4# and you can use 'args parser' I guess
5
6use $LIB_YSH/args.ysh --pick parser parseArgs
7
8source $LIB_YSH/yblocks.ysh
9
10# TODO: why doesn't this work? Is there a buffering problem wtih read --all?
11# Why would it not show up with source though?
12#use $LIB_YSH/yblocks.ysh --pick yb-capture
13
14# Can't be 'use' because we're using shell functions?
15source $LIB_OSH/byo-server.sh
16
17proc test-basic {
18 parser (&spec) {
19 flag -v --verbose (help="Verbosely") # default is Bool, false
20
21 flag -P --max-procs (Int, default=-1, help='''
22 Run at most P processes at a time
23 ''')
24
25 flag -i --invert (Bool, default=true, help='''
26 Long multiline
27 Description
28 ''')
29
30 flag -n --name (Str)
31
32 flag -s --scale (Float, default=0.0)
33
34 arg src (help='Source')
35 arg dest (help='Dest')
36
37 rest files
38 }
39
40 var args = parseArgs(spec, :| -n test --scale 1.0 mysrc -P 12 mydest a b c |)
41
42 assert [false === args.verbose]
43
44 assert [floatsEqual(args.scale, 1.0)]
45 call args->erase('scale') # remove Float key for subsequent equality check
46
47 var expected = {
48 "name": "test",
49 "src": "mysrc",
50 "max-procs": 12,
51 "dest": "mydest",
52 "files": :| a b c |,
53 "verbose": false,
54 "invert":true,
55 }
56
57 assert [expected === args]
58}
59
60proc test-2 {
61 ### Bool flag, positional args, more positional
62
63 parser (&spec) {
64 flag -v --verbose (Bool)
65 arg src
66 arg dst
67
68 rest more # allow more args
69 }
70
71 var argv = ['-v', 'src/path', 'dst/path', 'x', 'y', 'z']
72
73 var args = parseArgs(spec, argv)
74
75 #pp test_ (args)
76
77 assert [true === args.verbose]
78 assert ['src/path' === args.src]
79 assert ['dst/path' === args.dst]
80 assert [ :| x y z | === args.more]
81}
82
83proc test-default-values {
84
85 parser (&spec) {
86 flag -S --sanitize (Bool, default=false)
87 flag -v --verbose (Bool, default=false)
88 flag -P --max-procs (Int) # Will set to null (the default default)
89 }
90
91 var args = parseArgs(spec, [])
92
93 #pp test_ (args)
94 var expected = {
95 "sanitize": false,
96 "verbose": false,
97 "max-procs": null,
98 }
99 assert [expected === args]
100}
101
102proc test-multiple-argv-arrays {
103 parser (&spec) {
104 flag -v --verbose (Bool, default=false)
105 flag -c --count (Int, default=120)
106 arg file
107 }
108
109 # TODO: argCases should go above
110 var argsCases = [
111 :| -v --count 120 example.sh |,
112 :| -v --count 120 example.sh -v |, # duplicate flags are ignored
113 :| -v --count 120 example.sh -v --count 150 |, # the last duplicate has precedence
114 ]
115
116 yb-capture (&r) {
117 for args in (argsCases) {
118 var args_str = join(args, ' ')
119 echo "---------- $args_str ----------"
120 echo "\$ bin/ysh example.sh $args_str"
121 pp test_ (parseArgs(spec, args))
122
123 echo
124 }
125 }
126
127 #pp (r.stdout)
128
129 var expected = '''
130 ---------- -v --count 120 example.sh ----------
131 $ bin/ysh example.sh -v --count 120 example.sh
132 (Dict) {"verbose":true,"count":120,"file":"example.sh"}
133
134 ---------- -v --count 120 example.sh -v ----------
135 $ bin/ysh example.sh -v --count 120 example.sh -v
136 (Dict) {"verbose":true,"count":120,"file":"example.sh"}
137
138 ---------- -v --count 120 example.sh -v --count 150 ----------
139 $ bin/ysh example.sh -v --count 120 example.sh -v --count 150
140 (Dict) {"verbose":true,"count":150,"file":"example.sh"}
141
142 '''
143
144 assert [expected === r.stdout]
145}
146
147proc test-duplicate-names-are-errors {
148 try {
149 parser (&spec) {
150 flag -n --name
151 flag -N --name
152 }
153 }
154 assert [3 === _error.code]
155
156 try {
157 parser (&spec) {
158 flag -n --name
159 arg name
160 }
161 }
162 assert [3 === _error.code]
163
164 try {
165 parser (&spec) {
166 arg name
167 flag -o --other
168 arg name
169 }
170 }
171 assert [3 === _error.code]
172}
173
174proc test-more-errors {
175
176 parser (&spec) {
177 flag -v --verbose
178 flag -n --num (Int, required=true)
179
180 arg action
181 arg other (required=false)
182 }
183
184 try { call parseArgs(spec, :| -n 10 action other extra |) }
185 assert [2 === _error.code]
186
187 try { call parseArgs(spec, :| -n |) }
188 assert [2 === _error.code]
189
190 try { call parseArgs(spec, :| -n -v |) }
191 assert [2 === _error.code]
192
193 try { = parseArgs(spec, :| -n 10 |) }
194 assert [2 === _error.code]
195
196 try { call parseArgs(spec, :| -v action |) }
197 assert [2 === _error.code]
198
199 try { call parseArgs(spec, :| --unknown |) }
200 assert [2 === _error.code]
201}
202
203proc test-print-spec {
204 parser (&spec) {
205 flag -v --verbose (Bool)
206 arg src
207 arg dst
208
209 rest more # allow more args
210 }
211
212 var expected = {
213 flags: [
214 {
215 short: "-v",
216 long: "--verbose",
217 name: "verbose",
218 type: Bool,
219 default: false,
220 help: null
221 }
222 ],
223 args: [
224 {
225 name: "src",
226 help: null
227 },
228 {
229 name: "dst",
230 help: null
231 }
232 ],
233 rest: "more"
234 }
235
236 # Type objects cannot be tested for equality, so check them for identity then
237 # erase the keys so the remainder of the Dict can be tested for equality.
238 for i, flag in (expected.flags) {
239 assert [flag.type is spec.flags[i].type]
240 call expected.flags[i]->erase('type')
241 call spec.flags[i]->erase('type')
242 }
243 assert [expected === spec]
244}
245
246proc test-vs-python3-argparse {
247 var spec = {
248 flags: [
249 {short: '-v', long: '--verbose', name: 'verbose', type: null, default: '', help: 'Enable verbose logging'},
250 {short: '-c', long: '--count', name: 'count', type: Int, default: 80, help: 'Maximum line length'},
251 ],
252 args: [
253 {name: 'file', type: 'str', help: 'File to check line lengths of'}
254 ],
255 rest: null,
256 }
257
258 var argsCases = [
259 :| -v --count 120 example.sh |,
260 :| -v --count 120 example.sh -v |, # duplicate flags are ignored
261 :| -v --count 120 example.sh -v --count 150 |, # the last duplicate has precedence
262 ]
263
264 var argparse_py = '''
265 import argparse
266 import sys
267
268 spec = argparse.ArgumentParser()
269 spec.add_argument("filename")
270 spec.add_argument("-c", "--count", type=int)
271 spec.add_argument("-v", "--verbose",
272 action="store_true")
273
274 result = spec.parse_args(sys.argv[1:])
275 print([result.filename, result.count, result.verbose])
276 '''
277
278 yb-capture (&r) {
279 for args in (argsCases) {
280 var args_str = args => join(" ")
281 echo "---------- $args_str ----------"
282 echo "\$ bin/ysh example.sh $args_str"
283 pp test_ (parseArgs(spec, args))
284
285 echo
286 echo "\$ python3 example.py $args_str"
287 python3 -c $argparse_py @args
288
289 echo
290 }
291 }
292
293 var expected = '''
294 ---------- -v --count 120 example.sh ----------
295 $ bin/ysh example.sh -v --count 120 example.sh
296 (Dict) {"verbose":true,"count":120,"file":"example.sh"}
297
298 $ python3 example.py -v --count 120 example.sh
299 ['example.sh', 120, True]
300
301 ---------- -v --count 120 example.sh -v ----------
302 $ bin/ysh example.sh -v --count 120 example.sh -v
303 (Dict) {"verbose":true,"count":120,"file":"example.sh"}
304
305 $ python3 example.py -v --count 120 example.sh -v
306 ['example.sh', 120, True]
307
308 ---------- -v --count 120 example.sh -v --count 150 ----------
309 $ bin/ysh example.sh -v --count 120 example.sh -v --count 150
310 (Dict) {"verbose":true,"count":150,"file":"example.sh"}
311
312 $ python3 example.py -v --count 120 example.sh -v --count 150
313 ['example.sh', 150, True]
314
315 '''
316
317 # This is acceptable, but the diff could look nicer and more precise
318 diff -u <(echo $expected) <(echo $[r.stdout])
319 #assert [expected === r.stdout]
320}
321
322proc test-multi-value-flags {
323 parser (&spec) {
324 flag -f --float (List[Float])
325 flag -i --int (List[Int])
326 flag -s --str (List[Str])
327 }
328
329 var args = parseArgs(spec, :| -f 1.0 -s one -i 0 --str two --int 1 -s three |)
330
331 assert [type(args.float) === 'List']
332 assert [len(args.float) === 1]
333 assert [floatsEqual(args.float[0], 1.0)]
334
335 call args->erase('float') # remove List[Float] value for subsequent equality check
336
337 var expected = {
338 "int": [0, 1],
339 "str": :| one two three |,
340 }
341
342 assert [expected === args]
343
344 try { call parseArgs(spec, :| -f not_a_float |) }
345 assert [2 === _error.code]
346 try { call parseArgs(spec, :| -i not_an_int |) }
347 assert [2 === _error.code]
348}
349
350if is-main {
351 byo-maybe-run
352}