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.
@@ -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