benry-cmdopt 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES.md +9 -0
- data/README.md +163 -0
- data/Rakefile.rb +92 -0
- data/benry-cmdopt.gemspec +30 -0
- data/lib/benry/cmdopt.rb +504 -0
- data/test/cmdopt_test.rb +922 -0
- metadata +79 -0
data/test/cmdopt_test.rb
ADDED
@@ -0,0 +1,922 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'minitest/spec'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'minitest/ok'
|
6
|
+
|
7
|
+
require 'benry/cmdopt'
|
8
|
+
|
9
|
+
|
10
|
+
def new_sample_schema()
|
11
|
+
sc = Benry::Cmdopt::Schema.new
|
12
|
+
sc.add(:help , "-h, --help" , "show help message.")
|
13
|
+
sc.add(:version, "--version" , "print version")
|
14
|
+
sc.add(:file , "-f, --file=<FILE>" , "filename")
|
15
|
+
sc.add(:indent , "-i, --indent[=<WIDTH>]", "enable indent", type: Integer)
|
16
|
+
sc.add(:mode , "-m, --mode=<MODE>" , "mode", enum: ['verbose', 'quiet'])
|
17
|
+
sc.add(:libpath, "-I, --path=<PATH>" , "library path (multiple ok)") do |optdef, key, val|
|
18
|
+
File.directory?(val) or raise "directory not exist."
|
19
|
+
arr = optdef[key] || []
|
20
|
+
arr << val
|
21
|
+
arr
|
22
|
+
end
|
23
|
+
sc
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
class Benry::Cmdopt::Schema::Test < MiniTest::Test
|
29
|
+
|
30
|
+
|
31
|
+
describe "#parse()" do
|
32
|
+
|
33
|
+
before do
|
34
|
+
@schema = Benry::Cmdopt::Schema.new
|
35
|
+
end
|
36
|
+
|
37
|
+
it "[!qw0ac] parses command option definition string." do
|
38
|
+
sc = @schema
|
39
|
+
tuple = sc.__send__(:parse_optdef, "-h, --help")
|
40
|
+
ok {tuple} == ['h', 'help', nil, false]
|
41
|
+
tuple = sc.__send__(:parse_optdef, "-h")
|
42
|
+
ok {tuple} == ['h', nil, nil, false]
|
43
|
+
tuple = sc.__send__(:parse_optdef, "--help")
|
44
|
+
ok {tuple} == [nil, 'help', nil, false]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "[!ae733] parses command option definition which has a required param." do
|
48
|
+
sc = @schema
|
49
|
+
tuple = sc.__send__(:parse_optdef, "-f, --file=<FILE>")
|
50
|
+
ok {tuple} == ['f', 'file', '<FILE>', false]
|
51
|
+
tuple = sc.__send__(:parse_optdef, "-f <FILE>")
|
52
|
+
ok {tuple} == ['f', nil, '<FILE>', false]
|
53
|
+
tuple = sc.__send__(:parse_optdef, "--file=<FILE>")
|
54
|
+
ok {tuple} == [nil, 'file', '<FILE>', false]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "[!4h05c] parses command option definition which has an optional param." do
|
58
|
+
sc = @schema
|
59
|
+
tuple = sc.__send__(:parse_optdef, "-i, --indent[=<WIDTH>]")
|
60
|
+
ok {tuple} == ['i', 'indent', '<WIDTH>', true]
|
61
|
+
tuple = sc.__send__(:parse_optdef, "-i[<WIDTH>]")
|
62
|
+
ok {tuple} == ['i', nil, '<WIDTH>', true]
|
63
|
+
tuple = sc.__send__(:parse_optdef, "--indent[=<WIDTH>]")
|
64
|
+
ok {tuple} == [nil, 'indent', '<WIDTH>', true]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "[!b7jo3] raises SchemaError when command option definition is invalid." do
|
68
|
+
sc = @schema
|
69
|
+
pr = proc {
|
70
|
+
tuple = sc.__send__(:parse_optdef, "-i, --indent <WIDTH>")
|
71
|
+
}
|
72
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
73
|
+
"-i, --indent <WIDTH>: invalid option definition (use '=--indent' instead of ' --indent').")
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
describe "#add()" do
|
80
|
+
|
81
|
+
before do
|
82
|
+
@schema = Benry::Cmdopt::Schema.new
|
83
|
+
end
|
84
|
+
|
85
|
+
it "[!7hi2d] takes command option definition string." do
|
86
|
+
sc = @schema
|
87
|
+
sc.add(:indent, "-i, --indent=<WIDTH>", "print help")
|
88
|
+
items = sc.instance_eval { @items }
|
89
|
+
ok {items.length} == 1
|
90
|
+
ok {items[0]}.is_a?(Benry::Cmdopt::SchemaItem)
|
91
|
+
ok {items[0].key} == :indent
|
92
|
+
ok {items[0].short} == 'i'
|
93
|
+
ok {items[0].long} == 'indent'
|
94
|
+
ok {items[0].param} == '<WIDTH>'
|
95
|
+
ok {items[0].optional} == false
|
96
|
+
ok {items[0].type} == nil
|
97
|
+
ok {items[0].pattern} == nil
|
98
|
+
ok {items[0].enum} == nil
|
99
|
+
ok {items[0].callback} == nil
|
100
|
+
end
|
101
|
+
|
102
|
+
it "[!p9924] option key is omittable only when long option specified." do
|
103
|
+
sc = @schema
|
104
|
+
sc.add(nil, "-m, --max-num=<N>", nil)
|
105
|
+
items = sc.instance_eval { @items }
|
106
|
+
ok {items[0].key} == :max_num
|
107
|
+
end
|
108
|
+
|
109
|
+
it "[!jtp7z] raises SchemaError when key is nil and no long option." do
|
110
|
+
sc = @schema
|
111
|
+
pr = proc { sc.add(nil, "-i <N>", nil) }
|
112
|
+
msg = "add(nil, \"-i <N>\"): long option required when option key (1st arg) not specified."
|
113
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError, msg)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "[!yht0v] keeps command option definitions." do
|
117
|
+
sc = @schema
|
118
|
+
sc.add(:indent, "-i, --indent[=<WIDTH>]", "indent width",
|
119
|
+
type: Integer, pattern: /\A\d+\z/, enum: ['2', '4', '8']) {|v| v.to_i }
|
120
|
+
items = sc.instance_eval { @items }
|
121
|
+
ok {items.length} == 1
|
122
|
+
ok {items[0]}.is_a?(Benry::Cmdopt::SchemaItem)
|
123
|
+
ok {items[0].key} == :indent
|
124
|
+
ok {items[0].short} == 'i'
|
125
|
+
ok {items[0].long} == 'indent'
|
126
|
+
ok {items[0].param} == '<WIDTH>'
|
127
|
+
ok {items[0].optional} == true
|
128
|
+
ok {items[0].type} == Integer
|
129
|
+
ok {items[0].pattern} == /\A\d+\z/
|
130
|
+
ok {items[0].enum} == ['2', '4', '8']
|
131
|
+
ok {items[0].callback}.is_a?(Proc)
|
132
|
+
ok {items[0].callback.arity} == 1
|
133
|
+
end
|
134
|
+
|
135
|
+
it "[!rhhji] raises SchemaError when key is not a Symbol." do
|
136
|
+
sc = @schema
|
137
|
+
pr = proc {
|
138
|
+
sc.add("-i, --indent[=<WIDTH>]", "indent width", nil)
|
139
|
+
}
|
140
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
141
|
+
'add("-i, --indent[=<WIDTH>]"): 1st arg should be a Symbol as an option key.')
|
142
|
+
end
|
143
|
+
|
144
|
+
it "[!vq6eq] raises SchemaError when help message is missing." do
|
145
|
+
sc = @schema
|
146
|
+
pr = proc {
|
147
|
+
begin
|
148
|
+
sc.add(:indent, "-i, --indent[=<WIDTH>]", type: Array) # Ruby 2
|
149
|
+
rescue ArgumentError
|
150
|
+
sc.add(:indent, "-i, --indent[=<WIDTH>]", {type: Array}) # Ruby 3
|
151
|
+
end
|
152
|
+
}
|
153
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
154
|
+
'add(:indent, "-i, --indent[=<WIDTH>]"): help message required as 3rd argument.')
|
155
|
+
end
|
156
|
+
|
157
|
+
it "[!7xmr5] raises SchemaError when type is not registered." do
|
158
|
+
sc = @schema
|
159
|
+
pr = proc {
|
160
|
+
sc.add(:indent, "-i, --indent[=<WIDTH>]", "indent width", type: Array)
|
161
|
+
}
|
162
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
163
|
+
"Array: unregistered type.")
|
164
|
+
end
|
165
|
+
|
166
|
+
it "[!s2aaj] raises SchemaError when option has no params but type specified." do
|
167
|
+
sc = @schema
|
168
|
+
pr = proc {
|
169
|
+
sc.add(:indent, "-i, --indent", "indent width", type: Integer)
|
170
|
+
}
|
171
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
172
|
+
"Integer: type specified in spite of option has no params.")
|
173
|
+
end
|
174
|
+
|
175
|
+
it "[!bi2fh] raises SchemaError when pattern is not a regexp." do
|
176
|
+
sc = @schema
|
177
|
+
pr = proc {
|
178
|
+
sc.add(:indent, "-x, --indent[=<WIDTH>]", "indent width", pattern: '\A\d+\z')
|
179
|
+
}
|
180
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
181
|
+
'"\\\\A\\\\d+\\\\z": regexp expected.')
|
182
|
+
end
|
183
|
+
|
184
|
+
it "[!01fmt] raises SchmeaError when option has no params but pattern specified." do
|
185
|
+
sc = @schema
|
186
|
+
pr = proc {
|
187
|
+
sc.add(:indent, "-i, --indent", "indent width", pattern: /\A\d+\z/)
|
188
|
+
}
|
189
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
190
|
+
'/\A\d+\z/: pattern specified in spite of option has no params.')
|
191
|
+
end
|
192
|
+
|
193
|
+
it "[!melyd] raises SchmeaError when enum is not a Array nor Set." do
|
194
|
+
sc = @schema
|
195
|
+
sc.add(:indent, "-i <N>", "indent width", enum: ["2", "4", "8"])
|
196
|
+
sc.add(:indent, "-i <N>", "indent width", enum: Set.new(["2", "4", "8"]))
|
197
|
+
pr = proc {
|
198
|
+
sc.add(:indent, "-i <N>", "indent width", enum: "2,4,8")
|
199
|
+
}
|
200
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
201
|
+
'"2,4,8": array or set expected.')
|
202
|
+
end
|
203
|
+
|
204
|
+
it "[!xqed8] raises SchemaError when enum specified for no param option." do
|
205
|
+
sc = @schema
|
206
|
+
pr = proc {
|
207
|
+
sc.add(:indent, "-i", "enable indent", enum: [2, 4, 8])
|
208
|
+
}
|
209
|
+
ok {pr}.raise?(Benry::Cmdopt::SchemaError,
|
210
|
+
"[2, 4, 8]: enum specified in spite of option has no params.")
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
describe '#build_option_help()' do
|
217
|
+
|
218
|
+
before do
|
219
|
+
sc = Benry::Cmdopt::Schema.new
|
220
|
+
sc.add(:help , "-h, --help" , "show help message.")
|
221
|
+
sc.add(:version, " --version" , "print version")
|
222
|
+
sc.add(:file , "-f, --file=<FILE>" , "filename")
|
223
|
+
sc.add(:indent , "-i, --indent[=<WIDTH>]", "enable indent", type: Integer)
|
224
|
+
sc.add(:debug , "-d, --debug" , nil)
|
225
|
+
@schema = sc
|
226
|
+
end
|
227
|
+
|
228
|
+
it "[!0aq0i] can take integer as width." do
|
229
|
+
help = @schema.build_option_help(41)
|
230
|
+
ok {help} == <<END
|
231
|
+
-h, --help : show help message.
|
232
|
+
--version : print version
|
233
|
+
-f, --file=<FILE> : filename
|
234
|
+
-i, --indent[=<WIDTH>] : enable indent
|
235
|
+
END
|
236
|
+
s = help.each_line.first.split(':')[0]
|
237
|
+
ok {s.length} == 41+2
|
238
|
+
end
|
239
|
+
|
240
|
+
it "[!pcsah] can take format string." do
|
241
|
+
help = @schema.build_option_help("%-42s: %s")
|
242
|
+
ok {help} == <<END
|
243
|
+
-h, --help : show help message.
|
244
|
+
--version : print version
|
245
|
+
-f, --file=<FILE> : filename
|
246
|
+
-i, --indent[=<WIDTH>] : enable indent
|
247
|
+
END
|
248
|
+
s = help.each_line.first.split(':')[0]
|
249
|
+
ok {s.length} == 42+0
|
250
|
+
end
|
251
|
+
|
252
|
+
it "[!dndpd] detects option width automatically when nothing specified." do
|
253
|
+
help = @schema.build_option_help()
|
254
|
+
ok {help} == <<END
|
255
|
+
-h, --help : show help message.
|
256
|
+
--version : print version
|
257
|
+
-f, --file=<FILE> : filename
|
258
|
+
-i, --indent[=<WIDTH>] : enable indent
|
259
|
+
END
|
260
|
+
s = help.each_line.to_a.last.split(':')[0]
|
261
|
+
ok {s.length} == 25
|
262
|
+
end
|
263
|
+
|
264
|
+
it "[!v7z4x] skips option help if help message is not specified." do
|
265
|
+
help = @schema.build_option_help()
|
266
|
+
ok {help} !~ /debug/
|
267
|
+
end
|
268
|
+
|
269
|
+
it "[!to1th] includes all option help when `all` is true." do
|
270
|
+
help = @schema.build_option_help(nil, all: true)
|
271
|
+
ok {help} =~ /debug/
|
272
|
+
ok {help} == <<END
|
273
|
+
-h, --help : show help message.
|
274
|
+
--version : print version
|
275
|
+
-f, --file=<FILE> : filename
|
276
|
+
-i, --indent[=<WIDTH>] : enable indent
|
277
|
+
-d, --debug :
|
278
|
+
END
|
279
|
+
end
|
280
|
+
|
281
|
+
it "[!848rm] supports multi-lines help message." do
|
282
|
+
sc = Benry::Cmdopt::Schema.new
|
283
|
+
sc.add(:mode, "-m, --mode=<MODE>", <<END)
|
284
|
+
output mode
|
285
|
+
v, verbose: print many output
|
286
|
+
q, quiet: print litte output
|
287
|
+
c, compact: print summary output
|
288
|
+
END
|
289
|
+
actual = sc.build_option_help()
|
290
|
+
expected = <<END
|
291
|
+
-m, --mode=<MODE> : output mode
|
292
|
+
v, verbose: print many output
|
293
|
+
q, quiet: print litte output
|
294
|
+
c, compact: print summary output
|
295
|
+
END
|
296
|
+
ok {actual} == expected
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
describe '#_default_format()' do
|
303
|
+
|
304
|
+
before do
|
305
|
+
sc = Benry::Cmdopt::Schema.new
|
306
|
+
sc.add(:help , "-h, --help" , "show help message.")
|
307
|
+
sc.add(:version, " --version" , "print version")
|
308
|
+
sc.add(:file , "-f, --file=<FILE>" , "filename")
|
309
|
+
sc.add(:indent , "-i, --indent[=<WIDTH>]", "enable indent", type: Integer)
|
310
|
+
sc.add(:debug , "-d, --debug" , nil)
|
311
|
+
@schema = sc
|
312
|
+
end
|
313
|
+
|
314
|
+
it "[!kkh9t] returns format string." do
|
315
|
+
ret = @schema.__send__(:_default_format)
|
316
|
+
ok {ret} == " %-22s : %s"
|
317
|
+
end
|
318
|
+
|
319
|
+
it "[!hr45y] detects preffered option width." do
|
320
|
+
ret = @schema.__send__(:_default_format, 10, 20)
|
321
|
+
ok {ret} == " %-20s : %s"
|
322
|
+
ret = @schema.__send__(:_default_format, 30, 40)
|
323
|
+
ok {ret} == " %-30s : %s"
|
324
|
+
ret = @schema.__send__(:_default_format, 10, 40)
|
325
|
+
ok {ret} == " %-22s : %s"
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
describe '#each_option_help()' do
|
332
|
+
|
333
|
+
before do
|
334
|
+
sc = Benry::Cmdopt::Schema.new
|
335
|
+
sc.add(:help, "-h, --help", "show help message")
|
336
|
+
sc.add(:version, " --version", "print version")
|
337
|
+
@schema = sc
|
338
|
+
end
|
339
|
+
|
340
|
+
it "[!4b911] yields each optin definition str and help message." do
|
341
|
+
sc = @schema
|
342
|
+
arr = []
|
343
|
+
sc.each_option_help do |opt, help|
|
344
|
+
arr << [opt, help]
|
345
|
+
end
|
346
|
+
ok {arr} == [
|
347
|
+
["-h, --help", "show help message"],
|
348
|
+
[" --version", "print version"],
|
349
|
+
]
|
350
|
+
end
|
351
|
+
|
352
|
+
it "[!zbxyv] returns self." do
|
353
|
+
sc = @schema
|
354
|
+
ret = sc.each_option_help { nil }
|
355
|
+
ok {ret}.same? sc
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
describe '#find_short_option()' do
|
362
|
+
|
363
|
+
before do
|
364
|
+
@schema = new_sample_schema()
|
365
|
+
end
|
366
|
+
|
367
|
+
it "[!b4js1] returns option definition matched to short name." do
|
368
|
+
x = @schema.find_short_option('h')
|
369
|
+
ok {x.key} == :help
|
370
|
+
x = @schema.find_short_option('f')
|
371
|
+
ok {x.key} == :file
|
372
|
+
x = @schema.find_short_option('i')
|
373
|
+
ok {x.key} == :indent
|
374
|
+
end
|
375
|
+
|
376
|
+
it "[!s4d1y] returns nil when nothing found." do
|
377
|
+
ok {@schema.find_short_option('v')} == nil
|
378
|
+
end
|
379
|
+
|
380
|
+
end
|
381
|
+
|
382
|
+
|
383
|
+
describe '#find_long_option()' do
|
384
|
+
|
385
|
+
before do
|
386
|
+
@schema = new_sample_schema()
|
387
|
+
end
|
388
|
+
|
389
|
+
it "[!atmf9] returns option definition matched to long name." do
|
390
|
+
x = @schema.find_long_option('help')
|
391
|
+
ok {x.key} == :help
|
392
|
+
x = @schema.find_long_option('file')
|
393
|
+
ok {x.key} == :file
|
394
|
+
x = @schema.find_long_option('indent')
|
395
|
+
ok {x.key} == :indent
|
396
|
+
end
|
397
|
+
|
398
|
+
it "[!6haoo] returns nil when nothing found." do
|
399
|
+
ok {@schema.find_long_option('lib')} == nil
|
400
|
+
end
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
|
405
|
+
end
|
406
|
+
|
407
|
+
|
408
|
+
|
409
|
+
class Benry::Cmdopt::SchemaItem::Test < MiniTest::Test
|
410
|
+
|
411
|
+
|
412
|
+
describe '#validate_and_convert()' do
|
413
|
+
|
414
|
+
def new_item(key, optstr, short, long, param, help,
|
415
|
+
optional: nil, type: nil, pattern: nil, enum: nil, &callback)
|
416
|
+
return Benry::Cmdopt::SchemaItem.new(key, optstr, short, long, param, help,
|
417
|
+
optional: optional, type: type, pattern: pattern, enum: enum, &callback)
|
418
|
+
end
|
419
|
+
|
420
|
+
it "[!h0s0o] raises RuntimeError when value not matched to pattern." do
|
421
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", "indent width", pattern: /\A\d+\z/)
|
422
|
+
optdict = {}
|
423
|
+
pr = proc { x.validate_and_convert("abc", optdict) }
|
424
|
+
ok {pr}.raise?(RuntimeError, "pattern unmatched.")
|
425
|
+
end
|
426
|
+
|
427
|
+
it "[!5jrdf] raises RuntimeError when value not in enum." do
|
428
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", "indent width", enum: ['2', '4', '8'])
|
429
|
+
optdict = {}
|
430
|
+
pr = proc { x.validate_and_convert("10", optdict) }
|
431
|
+
ok {pr}.raise?(RuntimeError, "expected one of 2/4/8.")
|
432
|
+
end
|
433
|
+
|
434
|
+
it "[!j4fuz] calls type-specific callback when type specified." do
|
435
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", "indent width", type: Integer)
|
436
|
+
optdict = {}
|
437
|
+
pr = proc { x.validate_and_convert("abc", optdict) }
|
438
|
+
ok {pr}.raise?(RuntimeError, "integer expected.")
|
439
|
+
end
|
440
|
+
|
441
|
+
it "[!jn9z3] calls callback when callback specified." do
|
442
|
+
called = false
|
443
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", "indent width") {|va|
|
444
|
+
called = true
|
445
|
+
}
|
446
|
+
optdict = {}
|
447
|
+
x.validate_and_convert("abc", optdict)
|
448
|
+
ok {called} == true
|
449
|
+
end
|
450
|
+
|
451
|
+
it "[!iqalh] calls callback with different number of args according to arity." do
|
452
|
+
args1 = nil
|
453
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", nil) {|val|
|
454
|
+
args1 = val
|
455
|
+
}
|
456
|
+
optdict = {}
|
457
|
+
x.validate_and_convert("123", optdict)
|
458
|
+
ok {args1} == "123"
|
459
|
+
#
|
460
|
+
args2 = nil
|
461
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", nil) {|optdict, key, val|
|
462
|
+
args2 = [optdict, key, val]
|
463
|
+
}
|
464
|
+
optdict = {}
|
465
|
+
x.validate_and_convert("123", optdict)
|
466
|
+
ok {args2} == [optdict, :indent, "123"]
|
467
|
+
end
|
468
|
+
|
469
|
+
it "[!x066l] returns new value." do
|
470
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", nil, type: Integer)
|
471
|
+
ok {x.validate_and_convert("123", {})} == 123
|
472
|
+
#
|
473
|
+
x = new_item(:indent, "", "i", "indent", "<WIDTH>", nil, type: Integer) {|val|
|
474
|
+
val * 2
|
475
|
+
}
|
476
|
+
ok {x.validate_and_convert("123", {})} == 246
|
477
|
+
end
|
478
|
+
|
479
|
+
end
|
480
|
+
|
481
|
+
end
|
482
|
+
|
483
|
+
|
484
|
+
|
485
|
+
class Benry::Cmdopt::Parser::Test < MiniTest::Test
|
486
|
+
|
487
|
+
|
488
|
+
describe '#parse_options()' do
|
489
|
+
|
490
|
+
before do
|
491
|
+
@parser = Benry::Cmdopt::Parser.new(new_sample_schema())
|
492
|
+
end
|
493
|
+
|
494
|
+
it "[!3wmsy] returns command option values as a dict." do
|
495
|
+
argv = ["-h", "--version"]
|
496
|
+
d = @parser.parse(argv)
|
497
|
+
ok {d} == {help: true, version: true}
|
498
|
+
end
|
499
|
+
|
500
|
+
it "[!uh7j8] parses long options." do
|
501
|
+
argv = ["--help", "--file=foo.png", "--indent=10"]
|
502
|
+
d = @parser.parse(argv)
|
503
|
+
ok {d} == {help: true, file: "foo.png", indent: 10}
|
504
|
+
end
|
505
|
+
|
506
|
+
it "[!nwnjc] parses short options." do
|
507
|
+
argv = ["-h", "-f", "foo.png", "-i10"]
|
508
|
+
d = @parser.parse(argv)
|
509
|
+
ok {d} == {help: true, file: "foo.png", indent: 10}
|
510
|
+
end
|
511
|
+
|
512
|
+
it "[!y04um] skips rest options when '--' found in argv." do
|
513
|
+
argv = ["-h", "--", "-f", "foo.png", "-i10"]
|
514
|
+
d = @parser.parse(argv)
|
515
|
+
ok {d} == {help: true}
|
516
|
+
ok {argv} == ["-f", "foo.png", "-i10"]
|
517
|
+
end
|
518
|
+
|
519
|
+
it "[!qpuxh] handles only OptionError when block given." do
|
520
|
+
errmsg = nil
|
521
|
+
errcls = nil
|
522
|
+
@parser.parse(["-ix"]) {|err|
|
523
|
+
errmsg = err.message
|
524
|
+
errcls = err.class
|
525
|
+
}
|
526
|
+
ok {errmsg} == "-ix: integer expected."
|
527
|
+
ok {errcls} == Benry::Cmdopt::OptionError
|
528
|
+
#
|
529
|
+
sc = Benry::Cmdopt::Schema.new
|
530
|
+
sc.add(:file, "--file=<FILE>", "file") do |val|
|
531
|
+
File.open(val) {|f| f.read }
|
532
|
+
end
|
533
|
+
parser = Benry::Cmdopt::Parser.new(sc)
|
534
|
+
pr = proc { parser.parse(["--file=/foo/bar/baz.png"]) }
|
535
|
+
ok {pr}.raise?(Errno::ENOENT, /No such file or directory/)
|
536
|
+
end
|
537
|
+
|
538
|
+
it "[!dhpw1] returns nil when OptionError handled." do
|
539
|
+
ret = @parser.parse(["-dx"]) {|err| 1 }
|
540
|
+
ok {ret} == nil
|
541
|
+
end
|
542
|
+
|
543
|
+
end
|
544
|
+
|
545
|
+
|
546
|
+
describe '#parse_long_option()' do
|
547
|
+
|
548
|
+
before do
|
549
|
+
@parser = Benry::Cmdopt::Parser.new(new_sample_schema())
|
550
|
+
end
|
551
|
+
|
552
|
+
it "[!3i994] raises OptionError when invalid long option format." do
|
553
|
+
argv = ["--f/o/o"]
|
554
|
+
pr = proc { @parser.parse(argv) }
|
555
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--f/o/o: invalid long option.")
|
556
|
+
end
|
557
|
+
|
558
|
+
it "[!er7h4] raises OptionError when unknown long option." do
|
559
|
+
argv = ["--foo"]
|
560
|
+
pr = proc { @parser.parse(argv) }
|
561
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--foo: unknown long option.")
|
562
|
+
end
|
563
|
+
|
564
|
+
it "[!2jd9w] raises OptionError when no arguments specified for arg required long option." do
|
565
|
+
argv = ["--file"]
|
566
|
+
pr = proc { @parser.parse(argv) }
|
567
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--file: argument required.")
|
568
|
+
end
|
569
|
+
|
570
|
+
it "[!qyq8n] raises optionError when an argument specified for no arg long option." do
|
571
|
+
argv = ["--version=1.0.0"]
|
572
|
+
pr = proc { @parser.parse(argv) }
|
573
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--version=1.0.0: unexpected argument.")
|
574
|
+
end
|
575
|
+
|
576
|
+
it "[!o596x] validates argument value." do
|
577
|
+
argv = ["--indent=abc"]
|
578
|
+
pr = proc { @parser.parse(argv) }
|
579
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--indent=abc: integer expected.")
|
580
|
+
#
|
581
|
+
argv = ["--path=/foo/bar"]
|
582
|
+
pr = proc { @parser.parse(argv) }
|
583
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--path=/foo/bar: directory not exist.")
|
584
|
+
|
585
|
+
end
|
586
|
+
|
587
|
+
end
|
588
|
+
|
589
|
+
|
590
|
+
describe '#parse_short_option()' do
|
591
|
+
|
592
|
+
before do
|
593
|
+
@parser = Benry::Cmdopt::Parser.new(new_sample_schema())
|
594
|
+
end
|
595
|
+
|
596
|
+
it "[!4eh49] raises OptionError when unknown short option specified." do
|
597
|
+
argv = ["-hxf", "foo.png"]
|
598
|
+
pr = proc { @parser.parse(argv) }
|
599
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "-x: unknown option.")
|
600
|
+
end
|
601
|
+
|
602
|
+
it "[!utdbf] raises OptionError when argument required but not specified." do
|
603
|
+
argv = ["-hf"]
|
604
|
+
pr = proc { @parser.parse(argv) }
|
605
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "-f: argument required.")
|
606
|
+
end
|
607
|
+
|
608
|
+
it "[!f63hf] short option arg can be specified without space separator." do
|
609
|
+
argv = ["-hfabc.png", "xx"]
|
610
|
+
d = @parser.parse(argv)
|
611
|
+
ok {d} == {help: true, file: "abc.png"}
|
612
|
+
ok {argv} == ["xx"]
|
613
|
+
end
|
614
|
+
|
615
|
+
it "[!yjq6b] optional arg should be specified without space separator." do
|
616
|
+
argv = ["-hi123", "xx"]
|
617
|
+
d = @parser.parse(argv)
|
618
|
+
ok {d} == {help: true, indent: 123}
|
619
|
+
ok {argv} == ['xx']
|
620
|
+
end
|
621
|
+
|
622
|
+
it "[!wape4] otpional arg can be omit." do
|
623
|
+
argv = ["-hi", "xx"]
|
624
|
+
d = @parser.parse(argv)
|
625
|
+
ok {d} == {help: true, indent: true}
|
626
|
+
ok {argv} == ['xx']
|
627
|
+
end
|
628
|
+
|
629
|
+
it "[!yu0kc] validates short option argument." do
|
630
|
+
argv = ["-iaaa"]
|
631
|
+
pr = proc { @parser.parse(argv) }
|
632
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "-iaaa: integer expected.")
|
633
|
+
#
|
634
|
+
argv = ["-I", "/foo/bar"]
|
635
|
+
pr = proc { @parser.parse(argv) }
|
636
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "-I /foo/bar: directory not exist.")
|
637
|
+
end
|
638
|
+
|
639
|
+
end
|
640
|
+
|
641
|
+
|
642
|
+
describe '#new_options_dict()' do
|
643
|
+
|
644
|
+
it "[!vm6h0] returns new hash object." do
|
645
|
+
parser = Benry::Cmdopt::Parser.new(new_sample_schema())
|
646
|
+
ret = parser.__send__(:new_options_dict)
|
647
|
+
ok {ret}.is_a?(Hash)
|
648
|
+
ok {ret} == {}
|
649
|
+
end
|
650
|
+
|
651
|
+
end
|
652
|
+
|
653
|
+
|
654
|
+
end
|
655
|
+
|
656
|
+
|
657
|
+
|
658
|
+
class Benry::Cmdopt::Test < MiniTest::Test
|
659
|
+
|
660
|
+
|
661
|
+
describe 'PARAM_TYPES[Integer]' do
|
662
|
+
|
663
|
+
before do
|
664
|
+
sc = Benry::Cmdopt::Schema.new
|
665
|
+
sc.add(:indent, "-i, --indent[=<N>]", "indent width", type: Integer)
|
666
|
+
@parser = Benry::Cmdopt::Parser.new(sc)
|
667
|
+
end
|
668
|
+
|
669
|
+
it "[!6t8cs] converts value into integer." do
|
670
|
+
d = @parser.parse(['-i20'])
|
671
|
+
ok {d[:indent]} == 20
|
672
|
+
#
|
673
|
+
d = @parser.parse(['--indent=12'])
|
674
|
+
ok {d[:indent]} == 12
|
675
|
+
end
|
676
|
+
|
677
|
+
it "[!nzwc9] raises error when failed to convert value into integer." do
|
678
|
+
pr = proc { @parser.parse(['-i2.1']) }
|
679
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "-i2.1: integer expected.")
|
680
|
+
#
|
681
|
+
pr = proc { @parser.parse(['--indent=2.2']) }
|
682
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--indent=2.2: integer expected.")
|
683
|
+
end
|
684
|
+
|
685
|
+
end
|
686
|
+
|
687
|
+
|
688
|
+
describe 'PARAM_TYPES[Float]' do
|
689
|
+
|
690
|
+
before do
|
691
|
+
sc = Benry::Cmdopt::Schema.new
|
692
|
+
sc.add(:ratio, "-r, --ratio=<RATIO>", "ratio", type: Float)
|
693
|
+
@parser = Benry::Cmdopt::Parser.new(sc)
|
694
|
+
end
|
695
|
+
|
696
|
+
it "[!gggy6] converts value into float." do
|
697
|
+
d = @parser.parse(['-r', '1.25'])
|
698
|
+
ok {d[:ratio]} == 1.25
|
699
|
+
#
|
700
|
+
d = @parser.parse(['--ratio=1.25'])
|
701
|
+
ok {d[:ratio]} == 1.25
|
702
|
+
end
|
703
|
+
|
704
|
+
it "[!t4elj] raises error when faield to convert value into float." do
|
705
|
+
pr = proc { @parser.parse(['-r', 'abc']) }
|
706
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "-r abc: float expected.")
|
707
|
+
#
|
708
|
+
pr = proc { @parser.parse(['--ratio=abc']) }
|
709
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError, "--ratio=abc: float expected.")
|
710
|
+
end
|
711
|
+
|
712
|
+
end
|
713
|
+
|
714
|
+
|
715
|
+
describe 'PARAM_TYPES[TrueClass]' do
|
716
|
+
|
717
|
+
before do
|
718
|
+
sc = Benry::Cmdopt::Schema.new
|
719
|
+
sc.add(:border , "-b, --border[=<on|off>]", "enable border", type: TrueClass)
|
720
|
+
@parser = Benry::Cmdopt::Parser.new(sc)
|
721
|
+
end
|
722
|
+
|
723
|
+
it "[!47kx4] converts 'true'/'on'/'yes' into true." do
|
724
|
+
d = @parser.parse(["-btrue"])
|
725
|
+
ok {d} == {border: true}
|
726
|
+
d = @parser.parse(["-bon"])
|
727
|
+
ok {d} == {border: true}
|
728
|
+
d = @parser.parse(["-byes"])
|
729
|
+
ok {d} == {border: true}
|
730
|
+
#
|
731
|
+
d = @parser.parse(["--border=true"])
|
732
|
+
ok {d} == {border: true}
|
733
|
+
d = @parser.parse(["--border=on"])
|
734
|
+
ok {d} == {border: true}
|
735
|
+
d = @parser.parse(["--border=yes"])
|
736
|
+
ok {d} == {border: true}
|
737
|
+
end
|
738
|
+
|
739
|
+
it "[!3n810] converts 'false'/'off'/'no' into false." do
|
740
|
+
d = @parser.parse(["-bfalse"])
|
741
|
+
ok {d} == {border: false}
|
742
|
+
d = @parser.parse(["-boff"])
|
743
|
+
ok {d} == {border: false}
|
744
|
+
d = @parser.parse(["-bno"])
|
745
|
+
ok {d} == {border: false}
|
746
|
+
#
|
747
|
+
d = @parser.parse(["--border=false"])
|
748
|
+
ok {d} == {border: false}
|
749
|
+
d = @parser.parse(["--border=off"])
|
750
|
+
ok {d} == {border: false}
|
751
|
+
d = @parser.parse(["--border=no"])
|
752
|
+
ok {d} == {border: false}
|
753
|
+
end
|
754
|
+
|
755
|
+
it "[!h8ayh] raises error when failed to convert value into true nor false." do
|
756
|
+
pr = proc { @parser.parse(["-bt"]) }
|
757
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError,
|
758
|
+
"-bt: boolean expected.")
|
759
|
+
#
|
760
|
+
pr = proc { @parser.parse(["--border=t"]) }
|
761
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError,
|
762
|
+
"--border=t: boolean expected.")
|
763
|
+
end
|
764
|
+
|
765
|
+
end
|
766
|
+
|
767
|
+
|
768
|
+
describe 'PARAM_TYPES[Date]' do
|
769
|
+
|
770
|
+
before do
|
771
|
+
require 'date'
|
772
|
+
sc = Benry::Cmdopt::Schema.new
|
773
|
+
sc.add(:date, "-d, --date=<YYYY-MM-DD>]", "date", type: Date)
|
774
|
+
@parser = Benry::Cmdopt::Parser.new(sc)
|
775
|
+
end
|
776
|
+
|
777
|
+
it "[!sru5j] converts 'YYYY-MM-DD' into date object." do
|
778
|
+
d = @parser.parse(['-d', '2000-01-01'])
|
779
|
+
ok {d[:date]} == Date.new(2000, 1, 1)
|
780
|
+
#
|
781
|
+
d = @parser.parse(['--date=2000-1-2'])
|
782
|
+
ok {d[:date]} == Date.new(2000, 1, 2)
|
783
|
+
end
|
784
|
+
|
785
|
+
it "[!h9q9y] raises error when failed to convert into date object." do
|
786
|
+
pr = proc { @parser.parse(['-d', '2000/01/01']) }
|
787
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError,
|
788
|
+
"-d 2000/01/01: invalid date format (ex: '2000-01-01')")
|
789
|
+
#
|
790
|
+
pr = proc { @parser.parse(['--date=01-01-2000']) }
|
791
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError,
|
792
|
+
"--date=01-01-2000: invalid date format (ex: '2000-01-01')")
|
793
|
+
end
|
794
|
+
|
795
|
+
it "[!i4ui8] raises error when specified date not exist." do
|
796
|
+
pr = proc { @parser.parse(['-d', '2001-02-29']) }
|
797
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError,
|
798
|
+
"-d 2001-02-29: date not exist.")
|
799
|
+
#
|
800
|
+
pr = proc { @parser.parse(['--date=2001-02-29']) }
|
801
|
+
ok {pr}.raise?(Benry::Cmdopt::OptionError,
|
802
|
+
"--date=2001-02-29: date not exist.")
|
803
|
+
end
|
804
|
+
|
805
|
+
end
|
806
|
+
|
807
|
+
|
808
|
+
describe '.new()' do
|
809
|
+
|
810
|
+
it "[!7kkqv] creates Facade object." do
|
811
|
+
obj = Benry::Cmdopt.new
|
812
|
+
ok {obj}.is_a?(Benry::Cmdopt::Facade)
|
813
|
+
end
|
814
|
+
|
815
|
+
end
|
816
|
+
|
817
|
+
|
818
|
+
end
|
819
|
+
|
820
|
+
|
821
|
+
|
822
|
+
class Benry::Cmdopt::Facade::Test < MiniTest::Test
|
823
|
+
|
824
|
+
|
825
|
+
describe '#add()' do
|
826
|
+
|
827
|
+
it "[!vmb3r] defines command option." do
|
828
|
+
cmdopt = Benry::Cmdopt.new()
|
829
|
+
cmdopt.add(:help, "-h, --help", "show help message")
|
830
|
+
items = cmdopt.instance_eval { @schema.instance_variable_get('@items') }
|
831
|
+
ok {items}.is_a?(Array)
|
832
|
+
ok {items.length} == 1
|
833
|
+
ok {items[0].key} == :help
|
834
|
+
ok {items[0].short} == 'h'
|
835
|
+
ok {items[0].long} == 'help'
|
836
|
+
ok {items[0].help} == 'show help message'
|
837
|
+
end
|
838
|
+
|
839
|
+
end
|
840
|
+
|
841
|
+
|
842
|
+
describe '#build_option_help()' do
|
843
|
+
|
844
|
+
before do
|
845
|
+
@cmdopt = Benry::Cmdopt.new
|
846
|
+
@cmdopt.add(:help , "-h, --help" , "show help message")
|
847
|
+
@cmdopt.add(:version, " --version" , "print version")
|
848
|
+
@cmdopt.add(:file , "-f, --file=<FILE>" , "filename")
|
849
|
+
end
|
850
|
+
|
851
|
+
it "[!dm4p8] returns option help message." do
|
852
|
+
help = @cmdopt.build_option_help()
|
853
|
+
ok {help} == <<END
|
854
|
+
-h, --help : show help message
|
855
|
+
--version : print version
|
856
|
+
-f, --file=<FILE> : filename
|
857
|
+
END
|
858
|
+
end
|
859
|
+
|
860
|
+
end
|
861
|
+
|
862
|
+
|
863
|
+
describe '#each_option_help()' do
|
864
|
+
|
865
|
+
before do
|
866
|
+
@cmdopt = Benry::Cmdopt.new
|
867
|
+
@cmdopt.add(:help , "-h, --help" , "show help message")
|
868
|
+
@cmdopt.add(:version, " --version" , "print version")
|
869
|
+
@cmdopt.add(:file , "-f, --file=<FILE>" , "filename")
|
870
|
+
end
|
871
|
+
|
872
|
+
it "[!bw9qx] yields each option definition string and help message." do
|
873
|
+
cmdopt = @cmdopt
|
874
|
+
pairs = []
|
875
|
+
cmdopt.each_option_help do |opt, help|
|
876
|
+
pairs << [opt, help]
|
877
|
+
end
|
878
|
+
ok {pairs} == [
|
879
|
+
["-h, --help", "show help message"],
|
880
|
+
[" --version", "print version"],
|
881
|
+
["-f, --file=<FILE>", "filename"],
|
882
|
+
]
|
883
|
+
end
|
884
|
+
|
885
|
+
end
|
886
|
+
|
887
|
+
|
888
|
+
describe '#parse_options()' do
|
889
|
+
|
890
|
+
before do
|
891
|
+
@cmdopt = Benry::Cmdopt.new()
|
892
|
+
@cmdopt.add(:file, "-f, --file=<FILE>", "file") do |val|
|
893
|
+
File.open(val) {|f| f.read }
|
894
|
+
end
|
895
|
+
@cmdopt.add(:file, "-d, --debug[=<LEVEL>]", "debug", type: Integer)
|
896
|
+
end
|
897
|
+
|
898
|
+
it "[!areof] handles only OptionError when block given." do
|
899
|
+
errmsg = nil
|
900
|
+
errcls = nil
|
901
|
+
@cmdopt.parse(["-dx"]) {|err|
|
902
|
+
errmsg = err.message
|
903
|
+
errcls = err.class
|
904
|
+
}
|
905
|
+
ok {errmsg} == "-dx: integer expected."
|
906
|
+
ok {errcls} == Benry::Cmdopt::OptionError
|
907
|
+
#
|
908
|
+
pr = proc do
|
909
|
+
@cmdopt.parse(["-f", "/foo/bar/baz.png"])
|
910
|
+
end
|
911
|
+
ok {pr}.raise?(Errno::ENOENT, /No such file or directory/)
|
912
|
+
end
|
913
|
+
|
914
|
+
it "[!peuva] returns nil when OptionError handled." do
|
915
|
+
ret = @cmdopt.parse(["-dx"]) {|err| 1 }
|
916
|
+
ok {ret} == nil
|
917
|
+
end
|
918
|
+
|
919
|
+
end
|
920
|
+
|
921
|
+
|
922
|
+
end
|