benry-cmdapp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1038 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+
4
+ require 'oktest'
5
+
6
+ require 'benry/cmdapp'
7
+ require_relative './shared'
8
+
9
+
10
+ Oktest.scope do
11
+
12
+
13
+ topic Benry::CmdApp::ActionMetadata do
14
+ #include CommonTestingHelper # not work. why?
15
+ include ActionMetadataTestingHelper
16
+
17
+ class MetadataTestAction < Benry::CmdApp::ActionScope
18
+
19
+ @action.("print greeting message")
20
+ @option.(:lang, "-l, --lang=<en|fr|it>", "language")
21
+ def halo1(user="world", lang: "en")
22
+ case lang
23
+ when "en" ; puts "Hello, #{user}!"
24
+ when "fr" ; puts "Bonjour, #{user}!"
25
+ when "it" ; puts "Ciao, #{user}!"
26
+ else ; raise "#{lang}: unknown language."
27
+ end
28
+ end
29
+
30
+ @action.("print greeting message")
31
+ @option.(:lang, "-l, --lang=<en|fr|it>", "language")
32
+ def halo2(user="world", *users, lang: "en")
33
+ puts "user=#{user.inspect}, users=#{users.inspect}"
34
+ end
35
+
36
+ @action.("no arguments")
37
+ @option.(:lang, "-l, --lang=<en|fr|it>", "language")
38
+ def halo3(lang: "en")
39
+ puts "lang=#{lang}"
40
+ end
41
+
42
+ def args1(aa, bb, xx: nil); end # required arg
43
+ def args2(aa, bb=nil, cc=nil, xx: nil); end # optional arg
44
+ def args3(aa, bb=nil, cc=nil, *dd, xx: nil); end # variable arg
45
+ def args4(xx_or_yy_or_zz); end # xx|yy|zz
46
+ def args5(_xx_yy_zz); end # _xx-yy-zz
47
+
48
+ end
49
+
50
+
51
+ before do
52
+ schema = Benry::Cmdopt::Schema.new
53
+ schema.add(:lang, "-l, --lang=<en|fr|it>", "language")
54
+ @metadata = Benry::CmdApp::ActionMetadata.new("halo1", MetadataTestAction, :halo1, "greeting", schema)
55
+ end
56
+
57
+
58
+ topic '#hidden?()' do
59
+
60
+ class HiddenTestAction < Benry::CmdApp::ActionScope
61
+ @action.("public")
62
+ def pphidden1(); puts __method__(); end
63
+ #
64
+ @action.("private")
65
+ def pphidden2(); puts __method__(); end
66
+ private :pphidden2
67
+ #
68
+ private
69
+ @action.("private")
70
+ def pphidden3(); puts __method__(); end
71
+ end
72
+
73
+ spec "[!kp10p] returns true when action method is private." do
74
+ ameta = Benry::CmdApp::INDEX.get_action("pphidden3")
75
+ ok {ameta.hidden?} == true
76
+ ameta = Benry::CmdApp::INDEX.get_action("pphidden2")
77
+ ok {ameta.hidden?} == true
78
+ end
79
+
80
+ spec "[!nw322] returns false when action method is not private." do
81
+ ameta = Benry::CmdApp::INDEX.get_action("pphidden1")
82
+ ok {ameta.hidden?} == false
83
+ end
84
+
85
+ end
86
+
87
+
88
+ topic '#importance?()' do
89
+
90
+ spec "[!52znh] returns true if `@important == true`." do
91
+ ameta = @metadata
92
+ ameta.instance_variable_set('@important', true)
93
+ ok {ameta.important?} == true
94
+ end
95
+
96
+ spec "[!rlfac] returns false if `@important == false`." do
97
+ ameta = @metadata
98
+ ameta.instance_variable_set('@important', false)
99
+ ok {ameta.important?} == false
100
+ end
101
+
102
+ spec "[!j3trl] returns false if `@important == nil`. and action is hidden." do
103
+ ameta = @metadata
104
+ def ameta.hidden?; true; end
105
+ ok {ameta.important?} == false
106
+ end
107
+
108
+ spec "[!hhef8] returns nil if `@important == nil`." do
109
+ ameta = @metadata
110
+ ameta.instance_variable_set('@important', nil)
111
+ ok {ameta.important?} == nil
112
+ end
113
+
114
+ end
115
+
116
+
117
+ topic '#parse_options()' do
118
+
119
+ spec "[!ab3j8] parses argv and returns options." do
120
+ args = ["-l", "fr", "Alice"]
121
+ opts = @metadata.parse_options(args)
122
+ ok {opts} == {:lang => "fr"}
123
+ ok {args} == ["Alice"]
124
+ args = ["--lang=it", "Bob"]
125
+ opts = @metadata.parse_options(args)
126
+ ok {opts} == {:lang => "it"}
127
+ ok {args} == ["Bob"]
128
+ end
129
+
130
+ spec "[!56da8] raises InvalidOptionError if option value is invalid." do
131
+ args = ["-x", "fr", "Alice"]
132
+ pr = proc { @metadata.parse_options(args) }
133
+ ok {pr}.raise?(Benry::CmdApp::InvalidOptionError, "-x: Unknown option.")
134
+ end
135
+
136
+ end
137
+
138
+
139
+ topic '#run_action()' do
140
+
141
+ spec "[!veass] runs action with args and kwargs." do
142
+ args = ["Alice"]; kwargs = {lang: "fr"}
143
+ #
144
+ sout, serr = capture_sio { @metadata.run_action() }
145
+ ok {sout} == "Hello, world!\n"
146
+ ok {serr} == ""
147
+ #
148
+ sout, serr = capture_sio { @metadata.run_action(*args) }
149
+ ok {sout} == "Hello, Alice!\n"
150
+ ok {serr} == ""
151
+ #
152
+ sout, serr = capture_sio { @metadata.run_action(**kwargs) }
153
+ ok {sout} == "Bonjour, world!\n"
154
+ ok {serr} == ""
155
+ #
156
+ sout, serr = capture_sio { @metadata.run_action(*args, **kwargs) }
157
+ ok {sout} == "Bonjour, Alice!\n"
158
+ ok {serr} == ""
159
+ end
160
+
161
+ def _trace_mode(flag, &block)
162
+ bkup = $TRACE_MODE
163
+ $TRACE_MODE = true
164
+ begin
165
+ return yield
166
+ ensure
167
+ $TRACE_MODE = bkup
168
+ end
169
+ end
170
+
171
+ spec "[!tubhv] if $TRACE_MODE is on, prints tracing info." do
172
+ args = ["Alice"]; kwargs = {lang: "it"}
173
+ sout, serr = _trace_mode(true) do
174
+ capture_sio {
175
+ @metadata.run_action(*args, **kwargs)
176
+ }
177
+ end
178
+ ok {serr} == ""
179
+ ok {sout} == <<"END"
180
+ ## enter: halo1
181
+ Ciao, Alice!
182
+ ## exit: halo1
183
+ END
184
+ end
185
+
186
+ spec "[!zgp14] tracing info is colored when stdout is a tty." do
187
+ args = ["Alice"]; kwargs = {lang: "it"}
188
+ sout, serr = _trace_mode(true) do
189
+ capture_sio(tty: true) {
190
+ @metadata.run_action(*args, **kwargs)
191
+ }
192
+ end
193
+ ok {serr} == ""
194
+ ok {sout} == <<"END"
195
+ \e[33m## enter: halo1\e[0m
196
+ Ciao, Alice!
197
+ \e[33m## exit: halo1\e[0m
198
+ END
199
+ end
200
+
201
+ end
202
+
203
+
204
+ topic '#method_arity()' do
205
+
206
+ spec "[!7v4tp] returns min and max number of positional arguments." do
207
+ ok {@metadata.method_arity()} == [0, 1]
208
+ end
209
+
210
+ spec "[!w3rer] max is nil if variable argument exists." do
211
+ schema = Benry::Cmdopt::Schema.new
212
+ metadata = Benry::CmdApp::ActionMetadata.new("halo2", MetadataTestAction, :halo2, "greeting", schema)
213
+ ok {metadata.method_arity()} == [0, nil]
214
+ end
215
+
216
+ end
217
+
218
+
219
+ topic '#validate_method_params()' do
220
+
221
+ spec "[!plkhs] returns error message if keyword parameter for option not exist." do
222
+ schema = Benry::Cmdopt::Schema.new
223
+ schema.add(:foo, "--foo", "foo")
224
+ metadata = Benry::CmdApp::ActionMetadata.new("halo1", MetadataTestAction, :halo1, "greeting", schema)
225
+ msg = metadata.validate_method_params()
226
+ ok {msg} == "Should have keyword parameter 'foo' for '@option.(:foo)', but not."
227
+ end
228
+
229
+ spec "[!1koi8] returns nil if all keyword parameters for option exist." do
230
+ schema = Benry::Cmdopt::Schema.new
231
+ schema.add(:lang, "-l, --lang=<lang>", "lang")
232
+ metadata = Benry::CmdApp::ActionMetadata.new("halo1", MetadataTestAction, :halo1, "greeting", schema)
233
+ msg = metadata.validate_method_params()
234
+ ok {msg} == nil
235
+ end
236
+
237
+ end
238
+
239
+
240
+ topic '#help_message()' do
241
+
242
+ spec "[!i7siu] returns help message of action." do
243
+ schema = new_schema()
244
+ msg = without_tty { new_metadata(schema).help_message("testapp") }
245
+ ok {uncolorize(msg)} == <<END
246
+ testapp halo1 -- greeting
247
+
248
+ Usage:
249
+ $ testapp halo1 [<options>] [<user>]
250
+
251
+ Options:
252
+ -l, --lang=<en|fr|it> : language
253
+ END
254
+ end
255
+
256
+ end
257
+
258
+
259
+ end
260
+
261
+
262
+ topic Benry::CmdApp::ActionWithArgs do
263
+ include ActionMetadataTestingHelper
264
+
265
+
266
+ topic '#initialize()' do
267
+
268
+ spec "[!6jklb] keeps ActionMetadata, args, and kwargs." do
269
+ ameta = new_metadata(new_schema())
270
+ wrapper = Benry::CmdApp::ActionWithArgs.new(ameta, ["Alice"], {lang: "fr"})
271
+ ok {wrapper.action_metadata} == ameta
272
+ ok {wrapper.args} == ["Alice"]
273
+ ok {wrapper.kwargs} == {lang: "fr"}
274
+ end
275
+
276
+ end
277
+
278
+
279
+ topic '#method_missing()' do
280
+
281
+ spec "[!14li3] behaves as ActionMetadata." do
282
+ ameta = new_metadata(new_schema())
283
+ wrapper = Benry::CmdApp::ActionWithArgs.new(ameta, ["Alice"], {lang: "fr"})
284
+ ok {wrapper.name} == ameta.name
285
+ ok {wrapper.klass} == ameta.klass
286
+ ok {wrapper.schema} == ameta.schema
287
+ ok {wrapper.method_arity()} == ameta.method_arity()
288
+ end
289
+
290
+ end
291
+
292
+
293
+ topic '#run_action()' do
294
+
295
+ class ActionWithArgsTest < Benry::CmdApp::ActionScope
296
+ @action.("hello")
297
+ @option.(:lang, "-l <lang>", "language")
298
+ @option.(:repeat, "-r <N>", "repeat <N> times")
299
+ def hellowithargs(user1="alice", user2="bob", lang: nil, repeat: nil)
300
+ puts "user1=#{user1}, user2=#{user2}, lang=#{lang.inspect}, repeat=#{repeat.inspect}"
301
+ end
302
+ end
303
+
304
+ spec "[!fl26i] invokes action with args and kwargs." do
305
+ ameta = Benry::CmdApp::INDEX.get_action("hellowithargs")
306
+ #
307
+ wrapper = Benry::CmdApp::ActionWithArgs.new(ameta, ["bill"], {repeat: 3})
308
+ sout, serr = capture_sio() { wrapper.run_action() }
309
+ ok {serr} == ""
310
+ ok {sout} == "user1=bill, user2=bob, lang=nil, repeat=3\n"
311
+ #
312
+ wrapper = Benry::CmdApp::ActionWithArgs.new(ameta, ["bill"], nil)
313
+ sout, serr = capture_sio() { wrapper.run_action() }
314
+ ok {serr} == ""
315
+ ok {sout} == "user1=bill, user2=bob, lang=nil, repeat=nil\n"
316
+ #
317
+ wrapper = Benry::CmdApp::ActionWithArgs.new(ameta, nil, {repeat: 3})
318
+ sout, serr = capture_sio() { wrapper.run_action() }
319
+ ok {serr} == ""
320
+ ok {sout} == "user1=alice, user2=bob, lang=nil, repeat=3\n"
321
+ end
322
+
323
+ end
324
+
325
+
326
+ end
327
+
328
+
329
+ topic Benry::CmdApp::ActionScope do
330
+
331
+
332
+ class InvokeTestAction < Benry::CmdApp::ActionScope
333
+ prefix "test3:foo"
334
+ #
335
+ @action.("invoke once")
336
+ @option.(:lang, "-l, --lang=<en|fr>", "language")
337
+ def invoke1(user="world", lang: "en")
338
+ puts(lang == "fr" ? "Bonjour, #{user}!" : "Hello, #{user}!")
339
+ end
340
+ #
341
+ @action.("invoke twice")
342
+ @option.(:lang, "-l, --lang=<en|fr>", "language")
343
+ def invoke2(user="world", lang: "en")
344
+ puts(lang == "fr" ? "Bonjour, #{user}!" : "Hello, #{user}!")
345
+ end
346
+ #
347
+ @action.("invoke more")
348
+ @option.(:lang, "-l, --lang=<en|fr>", "language")
349
+ def invoke3(user="world", lang: "en")
350
+ puts(lang == "fr" ? "Bonjour, #{user}!" : "Hello, #{user}!")
351
+ end
352
+ end
353
+
354
+ class LoopedActionTest < Benry::CmdApp::ActionScope
355
+ @action.("test")
356
+ def loop1()
357
+ run_action_once("loop2")
358
+ end
359
+ @action.("test")
360
+ def loop2()
361
+ run_action_once("loop3")
362
+ end
363
+ @action.("test")
364
+ def loop3()
365
+ run_action_once("loop1")
366
+ end
367
+ end
368
+
369
+ before do
370
+ @action = InvokeTestAction.new()
371
+ Benry::CmdApp::INDEX.instance_variable_get('@done').clear()
372
+ end
373
+
374
+
375
+ topic '#run_action_once()' do
376
+
377
+ spec "[!oh8dc] don't invoke action if already invoked." do
378
+ sout, serr = capture_sio() do
379
+ @action.run_action_once("test3:foo:invoke2", "Alice", lang: "fr")
380
+ end
381
+ ok {sout} == "Bonjour, Alice!\n"
382
+ sout, serr = capture_sio() do
383
+ @action.run_action_once("test3:foo:invoke2", "Alice", lang: "fr")
384
+ end
385
+ ok {sout} == ""
386
+ ok {serr} == ""
387
+ end
388
+
389
+ end
390
+
391
+
392
+ topic '#run_action!()' do
393
+
394
+ spec "[!2yrc2] invokes action even if already invoked." do
395
+ sout, serr = capture_sio() do
396
+ @action.run_action!("test3:foo:invoke3", "Alice", lang: "fr")
397
+ end
398
+ ok {sout} == "Bonjour, Alice!\n"
399
+ sout, serr = capture_sio() do
400
+ @action.run_action!("test3:foo:invoke3", "Alice", lang: "fr")
401
+ end
402
+ ok {sout} == "Bonjour, Alice!\n"
403
+ sout, serr = capture_sio() do
404
+ @action.run_action!("test3:foo:invoke3", "Alice", lang: "en")
405
+ end
406
+ ok {sout} == "Hello, Alice!\n"
407
+ end
408
+
409
+ end
410
+
411
+
412
+ topic '#__run_action()' do
413
+
414
+ def __run_action(action_name, once, args, kwargs)
415
+ @action.__send__(:__run_action, action_name, once, args, kwargs)
416
+ end
417
+
418
+ def __run_loop(action_name, once, args, kwargs)
419
+ action = LoopedActionTest.new()
420
+ action.__send__(:__run_action, action_name, once, args, kwargs)
421
+ end
422
+
423
+ spec "[!lbp9r] invokes action name with prefix if prefix defined." do
424
+ sout, serr = capture_sio() do
425
+ @action.run_action_once("invoke2", "Alice", lang: "fr")
426
+ end
427
+ ok {sout} == "Bonjour, Alice!\n"
428
+ end
429
+
430
+ spec "[!7vszf] raises error if action specified not found." do
431
+ pr = proc { __run_action("loop9", nil, ["Alice"], {}) }
432
+ ok {pr}.raise?(Benry::CmdApp::ActionNotFoundError, "loop9: Action not found.")
433
+ end
434
+
435
+ spec "[!u8mit] raises error if action flow is looped." do
436
+ pr = proc { __run_loop("loop1", nil, [], {}) }
437
+ ok {pr}.raise?(Benry::CmdApp::LoopedActionError, "loop1: Action loop detected.")
438
+ end
439
+
440
+ spec "[!vhdo9] don't invoke action twice if 'once' arg is true." do
441
+ sout, serr = capture_sio() do
442
+ __run_action("test3:foo:invoke2", true, ["Alice"], {lang: "fr"})
443
+ end
444
+ ok {sout} == "Bonjour, Alice!\n"
445
+ sout, serr = capture_sio() do
446
+ __run_action("test3:foo:invoke2", true, ["Alice"], {lang: "fr"})
447
+ end
448
+ ok {sout} == ""
449
+ ok {serr} == ""
450
+ end
451
+
452
+ spec "[!r8fbn] invokes action." do
453
+ sout, serr = capture_sio() do
454
+ __run_action("test3:foo:invoke1", false, ["Alice"], {lang: "fr"})
455
+ end
456
+ ok {sout} == "Bonjour, Alice!\n"
457
+ end
458
+
459
+ end
460
+
461
+
462
+ topic '.prefix()' do
463
+
464
+ spec "[!1gwyv] converts symbol into string." do
465
+ class PrefixTest1 < Benry::CmdApp::ActionScope
466
+ prefix :foo
467
+ end
468
+ prefix = PrefixTest1.instance_variable_get('@__prefix__')
469
+ ok {prefix} == "foo:"
470
+ end
471
+
472
+ spec "[!pz46w] error if prefix contains extra '_'." do
473
+ pr = proc do
474
+ class PrefixTest2 < Benry::CmdApp::ActionScope
475
+ prefix "foo_bar"
476
+ end
477
+ end
478
+ ok {pr}.raise?(Benry::CmdApp::ActionDefError,
479
+ "foo_bar: Invalid prefix name (please use ':' or '-' instead of '_' as word separator).")
480
+ end
481
+
482
+ spec "[!9pu01] adds ':' at end of prefix name if prefix not end with ':'." do
483
+ class PrefixTest3 < Benry::CmdApp::ActionScope
484
+ prefix "foo:bar"
485
+ end
486
+ prefix = PrefixTest3.instance_variable_get('@__prefix__')
487
+ ok {prefix} == "foo:bar:"
488
+ end
489
+
490
+ end
491
+
492
+
493
+ topic '.inherited()' do
494
+
495
+ spec "[!f826w] registers all subclasses into 'ActionScope::SUBCLASSES'." do
496
+ class InheritedTest0a < Benry::CmdApp::ActionScope
497
+ end
498
+ class InheritedTest0b < Benry::CmdApp::ActionScope
499
+ end
500
+ ok {Benry::CmdApp::ActionScope::SUBCLASSES}.include?(InheritedTest0a)
501
+ ok {Benry::CmdApp::ActionScope::SUBCLASSES}.include?(InheritedTest0b)
502
+ end
503
+
504
+ spec "[!2imrb] sets class instance variables in subclass." do
505
+ class InheritedTest1 < Benry::CmdApp::ActionScope
506
+ end
507
+ ivars = InheritedTest1.instance_variables().sort()
508
+ ok {ivars} == [:@__action__, :@__aliasof__, :@__default__, :@__option__, :@__prefix__, :@action, :@copy_options, :@option]
509
+ end
510
+
511
+ spec "[!1qv12] @action is a Proc object and saves args." do
512
+ class InheritedTest2 < Benry::CmdApp::ActionScope
513
+ @action.("description", detail: "xxx", postamble: "yyy", important: true, tag: "zzz")
514
+ end
515
+ x = InheritedTest2.instance_variable_get('@__action__')
516
+ ok {x} == ["description", {detail: "xxx", postamble: "yyy", important: true, tag: "zzz"}]
517
+ end
518
+
519
+ spec "[!33ma7] @option is a Proc object and saves args." do
520
+ class InheritedTest3 < Benry::CmdApp::ActionScope
521
+ @action.("description", detail: "xxx", postamble: "yyy")
522
+ @option.(:xx, "-x, --xxx=<N>", "desc 1", type: Integer, rexp: /\A\d+\z/, enum: [2,4,8], range: (2..8))
523
+ @option.(:yy, "-y, --yyy[=<on|off>]", "desc 2", type: TrueClass, value: false)
524
+ end
525
+ x = InheritedTest3.instance_variable_get('@__option__')
526
+ ok {x}.is_a?(Benry::CmdOpt::Schema)
527
+ items = x.each.to_a()
528
+ ok {items.length} == 2
529
+ ok {items[0].key} == :xx
530
+ ok {items[0].optdef} == "-x, --xxx=<N>"
531
+ ok {items[0].desc} == "desc 1"
532
+ ok {items[0].type} == Integer
533
+ ok {items[0].rexp} == /\A\d+\z/
534
+ ok {items[0].enum} == [2, 4, 8]
535
+ ok {items[0].range} == (2..8)
536
+ ok {items[0].value} == nil
537
+ ok {items[1].key} == :yy
538
+ ok {items[1].optdef} == "-y, --yyy[=<on|off>]"
539
+ ok {items[1].desc} == "desc 2"
540
+ ok {items[1].type} == TrueClass
541
+ ok {items[1].rexp} == nil
542
+ ok {items[1].enum} == nil
543
+ ok {items[1].range} == nil
544
+ ok {items[1].value} == false
545
+ end
546
+
547
+ spec "[!gxybo] '@option.()' raises error when '@action.()' not called." do
548
+ pr = proc do
549
+ class InheritedTest4 < Benry::CmdApp::ActionScope
550
+ @option.(:xx, "-x, --xxx=<N>", "desc 1")
551
+ end
552
+ end
553
+ ok {pr}.raise?(Benry::CmdApp::OptionDefError,
554
+ "@option.(:xx): `@action.()` Required but not called.")
555
+ end
556
+
557
+ spec "[!ga6zh] '@option.()' raises error when invalid option info specified." do
558
+ pr = proc do
559
+ class InheritedTest20 < Benry::CmdApp::ActionScope
560
+ @action.("test")
561
+ @option.(:xx, "-x, --xxx=<N>", "desc 1", range: (2..8))
562
+ def hello(xx: nil)
563
+ end
564
+ end
565
+ end
566
+ ok {pr}.raise?(Benry::CmdApp::OptionDefError,
567
+ "2..8: Range value should be String, but not.")
568
+ end
569
+
570
+ spec "[!yrkxn] @copy_options is a Proc object and copies options from other action." do
571
+ class InheritedTest5 < Benry::CmdApp::ActionScope
572
+ @action.("copy src")
573
+ @option.(:xxx, "-x, --xxx=<arg>", "xxx #1")
574
+ def optcopy1(xxx: nil)
575
+ end
576
+ #
577
+ @action.("copy dst")
578
+ @copy_options.("optcopy1")
579
+ @option.(:yyy, "-y, --yyy=<on|off>", "yyy #2", type=TrueClass)
580
+ end
581
+ x = InheritedTest5.instance_variable_get('@__option__')
582
+ ok {x}.is_a?(Benry::CmdOpt::Schema)
583
+ items = x.each.to_a()
584
+ ok {items.length} == 2
585
+ ok {items[0].key} == :xxx
586
+ ok {items[0].short} == "x"
587
+ ok {items[0].long} == "xxx"
588
+ ok {items[1].key} == :yyy
589
+ ok {items[1].short} == "y"
590
+ ok {items[1].long} == "yyy"
591
+ end
592
+
593
+ spec "[!mhhn2] '@copy_options.()' raises error when action not found." do
594
+ pr = proc do
595
+ class InheritedTest6 < Benry::CmdApp::ActionScope
596
+ @action.("copy")
597
+ @copy_options.("optcopy99")
598
+ def copytest2(yyy: nil)
599
+ end
600
+ end
601
+ end
602
+ ok {pr}.raise?(Benry::CmdApp::OptionDefError,
603
+ "@copy_options.(\"optcopy99\"): Action not found.")
604
+ end
605
+
606
+ end
607
+
608
+
609
+ topic '.method_added()' do
610
+
611
+ def defined_actions()
612
+ actions = Benry::CmdApp::INDEX.instance_variable_get('@actions')
613
+ action_names = actions.keys()
614
+ yield
615
+ new_names = actions.keys() - action_names
616
+ metadata = new_names.length > 0 ? Benry::CmdApp::INDEX.get_action(new_names[0]) : nil
617
+ return new_names, metadata
618
+ end
619
+
620
+ spec "[!idh1j] do nothing if '@__action__' is nil." do
621
+ new_names, x = defined_actions() do
622
+ class Added1Test < Benry::CmdApp::ActionScope
623
+ prefix "added1"
624
+ def hello1(); end
625
+ end
626
+ end
627
+ ok {new_names} == []
628
+ ok {x} == nil
629
+ end
630
+
631
+ spec "[!ernnb] clears both '@__action__' and '@__option__'." do
632
+ new_names, x = defined_actions() do
633
+ class Added2Test < Benry::CmdApp::ActionScope
634
+ @action.("test", detail: "XXX", postamble: "YYY")
635
+ @option.(:foo, "--foo", "foo")
636
+ end
637
+ ok {Added2Test.instance_variable_get('@__action__')} != nil
638
+ ok {Added2Test.instance_variable_get('@__option__')} != nil
639
+ Added2Test.class_eval do
640
+ def hello2(foo: nil); end
641
+ end
642
+ ok {Added2Test.instance_variable_get('@__action__')} == nil
643
+ ok {Added2Test.instance_variable_get('@__option__')} == nil
644
+ end
645
+ end
646
+
647
+ spec "[!n8tem] creates ActionMetadata object if '@__action__' is not nil." do
648
+ new_names, x = defined_actions() do
649
+ class Added3Test < Benry::CmdApp::ActionScope
650
+ prefix "added3"
651
+ @action.("test", detail: "XXX", postamble: "YYY")
652
+ def hello3(); end
653
+ end
654
+ end
655
+ ok {new_names} == ["added3:hello3"]
656
+ ok {x}.is_a?(Benry::CmdApp::ActionMetadata)
657
+ ok {x.name} == "added3:hello3"
658
+ ok {x.klass} == Added3Test
659
+ ok {x.method} == :hello3
660
+ ok {x.hidden?} == false
661
+ ok {x.detail} == "XXX"
662
+ ok {x.postamble} == "YYY"
663
+ end
664
+
665
+ spec "[!4pbsc] raises error if keyword param for option not exist in method." do
666
+ pr = proc do
667
+ class Added4Test < Benry::CmdApp::ActionScope
668
+ prefix "added4"
669
+ @action.("test")
670
+ @option.(:flag, "--flag=<on|off>", nil, type: TrueClass)
671
+ def hello4(xx: nil); end
672
+ end
673
+ end
674
+ ok {pr}.raise?(Benry::CmdApp::ActionDefError,
675
+ "def hello4(): Should have keyword parameter 'flag' for '@option.(:flag)', but not.")
676
+ end
677
+
678
+ spec "[!t8vbf] raises error if action name duplicated." do
679
+ pr = proc do
680
+ class Added5Test < Benry::CmdApp::ActionScope
681
+ prefix "added5"
682
+ @action.("test")
683
+ def hello5(xx: nil); end
684
+ @action.("test")
685
+ def hello5(xx: nil); end
686
+ end
687
+ end
688
+ ok {pr}.raise?(Benry::CmdApp::ActionDefError,
689
+ "def hello5(): Action 'added5:hello5' already exist.")
690
+ end
691
+
692
+ case_when '[!5e5o0] when method name is same as default action name...' do
693
+
694
+ spec "[!myj3p] uses prefix name (expect last char ':') as action name." do
695
+ new_names, x = defined_actions() do
696
+ class Added6Test < Benry::CmdApp::ActionScope
697
+ prefix "added6", action: :hello6
698
+ @action.("test")
699
+ def hello6(); end
700
+ end
701
+ end
702
+ ok {new_names} == ["added6"]
703
+ ok {x.klass} == Added6Test
704
+ ok {x.method} == :hello6
705
+ end
706
+
707
+ spec "[!j5oto] clears '@__default__'." do
708
+ class ClearDefaultTest1 < Benry::CmdApp::ActionScope
709
+ prefix "cleardefault1", action: :test2_ # symbol
710
+ @__default__ != nil or
711
+ raise MiniTest::Assertion, "@__default__ should NOT be nil"
712
+ #
713
+ @action.("test")
714
+ def test1_(); end
715
+ @__default__ != nil or
716
+ raise MiniTest::Assertion, "@__default__ should NOT be nil"
717
+ #
718
+ @action.("test")
719
+ def test2_(); end
720
+ @__default__ == nil or
721
+ raise MiniTest::Assertion, "@__default__ should be nil"
722
+ end
723
+ end
724
+
725
+ end
726
+
727
+ case_else '[!agpwh] else...' do
728
+
729
+ spec "[!3icc4] uses method name as action name." do
730
+ new_names, x = defined_actions() do
731
+ class Added7Test < Benry::CmdApp::ActionScope
732
+ @action.("test")
733
+ def hello7xx(); end
734
+ end
735
+ end
736
+ ok {new_names} == ["hello7xx"]
737
+ ok {x.klass} == Added7Test
738
+ ok {x.method} == :hello7xx
739
+ end
740
+
741
+ spec "[!c643b] converts action name 'aa_bb_cc_' into 'aa_bb_cc'." do
742
+ new_names, x = defined_actions() do
743
+ class Added8Test < Benry::CmdApp::ActionScope
744
+ @action.("test")
745
+ def hello8xx_(); end
746
+ end
747
+ end
748
+ ok {new_names} == ["hello8xx"]
749
+ ok {x.klass} == Added8Test
750
+ ok {x.method} == :hello8xx_
751
+ end
752
+
753
+ spec "[!3fkb3] converts action name 'aa__bb__cc' into 'aa:bb:cc'." do
754
+ new_names, x = defined_actions() do
755
+ class Added9Test < Benry::CmdApp::ActionScope
756
+ @action.("test")
757
+ def hello9xx__yy__zz(); end
758
+ end
759
+ end
760
+ ok {new_names} == ["hello9xx:yy:zz"]
761
+ ok {x.klass} == Added9Test
762
+ ok {x.method} == :hello9xx__yy__zz
763
+ end
764
+
765
+ spec "[!o9s9h] converts action name 'aa_bb:_cc_dd' into 'aa-bb:_cc-dd'." do
766
+ new_names, x = defined_actions() do
767
+ class Added10Test < Benry::CmdApp::ActionScope
768
+ @action.("test")
769
+ def _hello10xx_yy_zz(); end
770
+ end
771
+ end
772
+ ok {new_names} == ["_hello10xx-yy-zz"]
773
+ ok {x.klass} == Added10Test
774
+ ok {x.method} == :_hello10xx_yy_zz
775
+ end
776
+
777
+ spec "[!8hlni] when action name is same as default name, uses prefix as action name." do
778
+ new_names, x = defined_actions() do
779
+ class Added11Test < Benry::CmdApp::ActionScope
780
+ prefix "added11", action: "hello11"
781
+ @action.("test")
782
+ def hello11(); end
783
+ end
784
+ end
785
+ ok {new_names} == ["added11"]
786
+ ok {x.klass} == Added11Test
787
+ ok {x.method} == :hello11
788
+ end
789
+
790
+ spec "[!q8oxi] clears '@__default__' when default name matched to action name." do
791
+ class ClearDefaultTest2 < Benry::CmdApp::ActionScope
792
+ prefix "cleardefault2", action: "test2" # string
793
+ @__default__ != nil or
794
+ raise MiniTest::Assertion, "@__default__ should NOT be nil"
795
+ #
796
+ @action.("test")
797
+ def test1_(); end
798
+ @__default__ != nil or
799
+ raise MiniTest::Assertion, "@__default__ should NOT be nil"
800
+ #
801
+ @action.("test")
802
+ def test2_(); end
803
+ @__default__ == nil or
804
+ raise MiniTest::Assertion, "@__default__ should be nil"
805
+ end
806
+ end
807
+
808
+ spec "[!xfent] when prefix is provided, adds it to action name." do
809
+ new_names, x = defined_actions() do
810
+ class Added12Test < Benry::CmdApp::ActionScope
811
+ prefix "added12"
812
+ @action.("test")
813
+ def hello12(); end
814
+ end
815
+ end
816
+ ok {new_names} == ["added12:hello12"]
817
+ ok {x.klass} == Added12Test
818
+ ok {x.method} == :hello12
819
+ end
820
+
821
+ end
822
+
823
+ spec "[!jpzbi] defines same name alias of action as prefix." do
824
+ ## when symbol
825
+ class AliasOfTest1 < Benry::CmdApp::ActionScope
826
+ prefix "blabla1", alias_of: :bla1 # symbol
827
+ @action.("test")
828
+ def bla1(); end
829
+ end
830
+ ok {Benry::CmdApp::INDEX.get_alias("blabla1")} != nil
831
+ ok {Benry::CmdApp::INDEX.get_alias("blabla1").action_name} == "blabla1:bla1"
832
+ ## when string
833
+ class AliasOfTest2 < Benry::CmdApp::ActionScope
834
+ prefix "bla:bla2", alias_of: "blala" # string
835
+ @action.("test")
836
+ def blala(); end
837
+ end
838
+ ok {Benry::CmdApp::INDEX.get_alias("bla:bla2")} != nil
839
+ ok {Benry::CmdApp::INDEX.get_alias("bla:bla2").action_name} == "bla:bla2:blala"
840
+ end
841
+
842
+ spec "[!tvjb0] clears '@__aliasof__' only when alias created." do
843
+ class AliasOfTest3 < Benry::CmdApp::ActionScope
844
+ prefix "bla:bla3", alias_of: "bla3b" # string
845
+ @__aliasof__ != nil or
846
+ raise MiniTest::Assertion, "@__aliasof__ should NOT be nil"
847
+ #
848
+ @action.("test")
849
+ def bla3a(); end
850
+ @__aliasof__ != nil or
851
+ raise MiniTest::Assertion, "@__aliasof__ should NOT be nil"
852
+ #
853
+ @action.("test")
854
+ def bla3b(); end
855
+ @__aliasof__ == nil or
856
+ raise MiniTest::Assertion, "@__aliasof__ should be nil"
857
+ end
858
+ begin
859
+ ok {AliasOfTest3.instance_variable_get('@__aliasof__')} == nil
860
+ ensure
861
+ AliasOfTest3.class_eval do
862
+ @__aliasof__ = nil
863
+ end
864
+ end
865
+ end
866
+
867
+ spec "[!997gs] not raise error when action not found." do
868
+ pr = proc do
869
+ class AliasOfTest4 < Benry::CmdApp::ActionScope
870
+ prefix "blabla4", alias_of: :bla99 # action not exist
871
+ @action.("test")
872
+ def bla3(); end
873
+ end
874
+ end
875
+ begin
876
+ ok {pr}.NOT.raise?(Exception)
877
+ ensure
878
+ AliasOfTest4.class_eval { @__aliasof__ = nil }
879
+ end
880
+ end
881
+
882
+ spec "[!349nr] raises error when same name action or alias with prefix already exists." do
883
+ pr = proc do
884
+ class AliasOfTest5a < Benry::CmdApp::ActionScope
885
+ @action.("test")
886
+ def bla5(); end # define 'bla5' action
887
+ end
888
+ class AliasOfTest5b < Benry::CmdApp::ActionScope
889
+ prefix "bla5", alias_of: :blala # define 'bla5' action, too
890
+ @action.("test")
891
+ def blala(); end
892
+ end
893
+ end
894
+ begin
895
+ ok {pr}.raise?(Benry::CmdApp::AliasDefError,
896
+ 'action_alias("bla5", "bla5:blala"): Not allowed to define same name alias as existing action.')
897
+ ensure
898
+ AliasOfTest5b.class_eval { @__aliasof__ = nil }
899
+ end
900
+ end
901
+
902
+ end
903
+
904
+
905
+ end
906
+
907
+
908
+ topic Benry::CmdApp::BuiltInAction do
909
+ include CommonTestingHelper
910
+
911
+ before do
912
+ @config = Benry::CmdApp::Config.new("test app", "1.0.0")
913
+ @config.app_name = "TestApp"
914
+ @config.app_command = "testapp"
915
+ @config.default_action = nil
916
+ @app = Benry::CmdApp::Application.new(@config)
917
+ end
918
+
919
+
920
+ topic '#help()' do
921
+
922
+ spec "[!jfgsy] prints help message of action if action name specified." do
923
+ sout, serr = capture_sio { @app.run("help", "help") }
924
+ ok {serr} == ""
925
+ ok {uncolorize(sout)} == <<"END"
926
+ testapp help -- print help message (of action)
927
+
928
+ Usage:
929
+ $ testapp help [<options>] [<action>]
930
+
931
+ Options:
932
+ -a, --all : show private (hidden) options, too
933
+ END
934
+ end
935
+
936
+ spec "[!fhpjg] prints help message of command if action name not specified." do
937
+ sout, serr = capture_sio { @app.run("help") }
938
+ ok {serr} == ""
939
+ ok {sout}.start_with?(<<"END")
940
+ TestApp (1.0.0) -- test app
941
+
942
+ Usage:
943
+ $ testapp [<options>] [<action> [<arguments>...]]
944
+
945
+ Options:
946
+ -h, --help : print help message
947
+ -V, --version : print version
948
+
949
+ Actions:
950
+ END
951
+ end
952
+
953
+ spec "[!6g7jh] prints colorized help message when color mode is on." do
954
+ sout, serr = capture_sio(tty: true) { @app.run("help") }
955
+ ok {serr} == ""
956
+ ok {sout}.start_with?(<<"END")
957
+ \e[1mTestApp\e[0m (1.0.0) -- test app
958
+
959
+ \e[34mUsage:\e[0m
960
+ $ \e[1mtestapp\e[0m [<options>] [<action> [<arguments>...]]
961
+
962
+ \e[34mOptions:\e[0m
963
+ \e[1m-h, --help \e[0m : print help message
964
+ \e[1m-V, --version \e[0m : print version
965
+
966
+ \e[34mActions:\e[0m
967
+ END
968
+ end
969
+
970
+ spec "[!ihr5u] prints non-colorized help message when color mode is off." do
971
+ sout, serr = capture_sio(tty: false) { @app.run("help") }
972
+ ok {serr} == ""
973
+ ok {sout}.start_with?(<<"END")
974
+ TestApp (1.0.0) -- test app
975
+
976
+ Usage:
977
+ $ testapp [<options>] [<action> [<arguments>...]]
978
+
979
+ Options:
980
+ -h, --help : print help message
981
+ -V, --version : print version
982
+
983
+ Actions:
984
+ END
985
+ end
986
+
987
+ end
988
+
989
+
990
+ end
991
+
992
+
993
+ topic Benry::CmdApp::Alias do
994
+
995
+ class Alias2Test < Benry::CmdApp::ActionScope
996
+ prefix "alias2"
997
+ #
998
+ @action.("alias test")
999
+ def a1(); end
1000
+ #
1001
+ @action.("alias test", important: true)
1002
+ def a2(); end
1003
+ #
1004
+ @action.("alias test", important: false)
1005
+ def a3(); end
1006
+ #
1007
+ private
1008
+ @action.("alias test")
1009
+ def a4(); end
1010
+ end
1011
+
1012
+
1013
+ topic '#important?()' do
1014
+
1015
+ spec "[!5juwq] returns true if `@important == true`." do
1016
+ ali = Benry::CmdApp::Alias.new("a2-1", "alias2:a1", important: true)
1017
+ ok {ali.important?} == true
1018
+ end
1019
+
1020
+ spec "[!1gnbc] returns false if `@important == false`." do
1021
+ ali = Benry::CmdApp::Alias.new("a2-2", "alias2:a1", important: false)
1022
+ ok {ali.important?} == false
1023
+ end
1024
+
1025
+ spec "[!h3nm3] returns true or false according to action object if `@important == nil`." do
1026
+ ok {Benry::CmdApp::Alias.new("ali1", "alias2:a1").important?} == nil
1027
+ ok {Benry::CmdApp::Alias.new("ali1", "alias2:a2").important?} == true
1028
+ ok {Benry::CmdApp::Alias.new("ali1", "alias2:a3").important?} == false
1029
+ ok {Benry::CmdApp::Alias.new("ali1", "alias2:a4").important?} == false
1030
+ end
1031
+
1032
+ end
1033
+
1034
+
1035
+ end
1036
+
1037
+
1038
+ end