benry-cmdapp 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+
5
+ require 'oktest'
6
+
7
+ require 'benry/cmdapp'
8
+
9
+
10
+ Oktest.scope do
11
+
12
+
13
+ topic Benry::CmdApp::Config do
14
+
15
+ before do
16
+ @config = Benry::CmdApp::Config.new("testapp", "1.2.3")
17
+ end
18
+
19
+
20
+ topic '#initialize()' do
21
+
22
+ spec "[!pzp34] if `option_version` is not specified, then set true if `app_version` is provided." do
23
+ c = Benry::CmdApp::Config.new("x", "1.0.0", option_version: nil)
24
+ ok {c.option_version} == true
25
+ c = Benry::CmdApp::Config.new("x", option_version: nil)
26
+ ok {c.option_version} == false
27
+ #
28
+ c = Benry::CmdApp::Config.new("x", "1.0.0", option_version: true)
29
+ ok {c.option_version} == true
30
+ c = Benry::CmdApp::Config.new("x", option_version: true)
31
+ ok {c.option_version} == true
32
+ #
33
+ c = Benry::CmdApp::Config.new("x", "1.0.0", option_version: false)
34
+ ok {c.option_version} == false
35
+ c = Benry::CmdApp::Config.new("x", option_version: false)
36
+ ok {c.option_version} == false
37
+ end
38
+
39
+ end
40
+
41
+
42
+ topic '#each()' do
43
+
44
+ spec "[!yxi7r] returns Enumerator object if block not given." do
45
+ ok {@config.each()}.is_a?(Enumerator)
46
+ end
47
+
48
+ spec "[!64zkf] yields each config name and value." do
49
+ d = {}
50
+ @config.each do |k, v|
51
+ ok {k}.is_a?(Symbol)
52
+ d[k] = v
53
+ end
54
+ ok {d}.NOT.empty?
55
+ end
56
+
57
+ spec "[!0zatj] sorts key names if `sort: true` passed." do
58
+ keys1 = []; keys2 = []
59
+ @config.each {|k, v| keys1 << k }
60
+ @config.each(sort: true) {|k, v| keys2 << k }
61
+ ok {keys1} != keys2
62
+ ok {keys1.sort} == keys2
63
+ end
64
+
65
+ end
66
+
67
+
68
+ end
69
+
70
+
71
+ end
@@ -0,0 +1,382 @@
1
+ # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+
4
+
5
+ require_relative "shared"
6
+
7
+
8
+
9
+ class FooBarAction < Benry::CmdApp::Action
10
+ category "foo:"
11
+
12
+ @action.("prep for foo")
13
+ def prep; puts "foo:prep"; end
14
+
15
+ category "bar:" do
16
+
17
+ @action.("prep for foo:bar")
18
+ def prep; puts "foo:bar:prep"; end
19
+
20
+ category "baz:" do
21
+
22
+ @action.("prep for foo:bar:baz")
23
+ def prep; puts "foo:bar:baz:prep"; end
24
+
25
+ @action.("aaa")
26
+ def aaa(); run_action("prep"); puts "foo:bar:baz:aaa"; end
27
+
28
+ end
29
+
30
+ @action.("bbb")
31
+ def bbb(); run_action("prep"); puts "foo:bar:bbb"; end
32
+
33
+ end
34
+
35
+ @action.("ccc")
36
+ def ccc(); run_action("prep"); puts "foo:ccc"; end
37
+
38
+ @action.("ddd")
39
+ def ddd(); run_action("prep"); puts "foo:ddd"; end
40
+
41
+ ### looped action
42
+
43
+ @action.("looped")
44
+ def loop1()
45
+ run_once("loop2")
46
+ end
47
+
48
+ @action.("looped")
49
+ def loop2()
50
+ run_once("loop3")
51
+ end
52
+
53
+ @action.("looped")
54
+ def loop3()
55
+ run_once("loop1")
56
+ end
57
+
58
+ ##
59
+
60
+ @action.("err1")
61
+ @option.(:z, "-z", "option z")
62
+ def err1(x, y=0, z: nil)
63
+ puts "x=#{x.inspect}, y=#{y.inspect}, z=#{z.inspect}"
64
+ end
65
+
66
+ ##
67
+
68
+ @action.("nest1")
69
+ def nest1(); run_action("nest2"); end
70
+
71
+ @action.("nest2")
72
+ def nest2(); run_action("nest3"); end
73
+
74
+ @action.("nest3")
75
+ def nest3(); puts "nest3"; end
76
+
77
+ ##
78
+
79
+ @action.("takes any arguments")
80
+ def anyargs(*args, **kwargs)
81
+ puts "args=#{args.inspect}, kwargs=#{kwargs.inspect}"
82
+ end
83
+
84
+ end
85
+
86
+
87
+ Oktest.scope do
88
+
89
+
90
+ topic Benry::CmdApp::ApplicationContext do
91
+
92
+
93
+ before do
94
+ @config = Benry::CmdApp::Config.new("test app", "1.2.3", app_command: "testapp")
95
+ @context = Benry::CmdApp::ApplicationContext.new(@config)
96
+ end
97
+
98
+
99
+ topic '#teardown()' do
100
+
101
+ before do
102
+ @scope = MyAction.new(@config, @context)
103
+ @result = []
104
+ @scope.at_end { @result << "A" }
105
+ @scope.at_end { @result << "B" }
106
+ @scope.at_end { @result << "C" }
107
+ end
108
+
109
+ spec "[!4df2f] invokes end blocks in reverse order of registration." do
110
+ ok {@result} == []
111
+ @context.__send__(:teardown)
112
+ ok {@result} == ["C", "B", "A"]
113
+ end
114
+
115
+ spec "[!vskre] end block list should be cleared." do
116
+ ok {@context.instance_variable_get(:@end_blocks)}.length(3)
117
+ @context.__send__(:teardown)
118
+ ok {@context.instance_variable_get(:@end_blocks)}.length(0)
119
+ end
120
+
121
+ end
122
+
123
+
124
+ topic '#start_action()' do
125
+
126
+ spec "[!2mnh7] looks up action metadata with action or alias name." do
127
+ metadata = Benry::CmdApp::REGISTRY.metadata_get("foo:anyargs")
128
+ ok {metadata} != nil
129
+ Benry::CmdApp.define_alias("ali21", "foo:anyargs")
130
+ Benry::CmdApp.define_alias!("ali22", "ali21")
131
+ Benry::CmdApp.define_alias!("ali23", "ali22")
132
+ #
133
+ sout, serr = capture_sio() { @context.start_action("ali23", []) }
134
+ ok {sout} == "args=[], kwargs={}\n"
135
+ #
136
+ r = recorder()
137
+ r.fake_method(@context, :_invoke_action => nil)
138
+ @context.start_action("ali23", [])
139
+ ok {r[0].args} == [metadata, [], {}, {:once=>false}]
140
+ end
141
+
142
+ spec "[!0ukvb] raises CommandError if action nor alias not found." do
143
+ pr = proc { @context.start_action("hello99", []) }
144
+ ok {pr}.raise?(Benry::CmdApp::CommandError,
145
+ "hello99: Action nor alias not found.")
146
+ end
147
+
148
+ spec "[!9n46s] if alias has its own args, combines them with command-line args." do
149
+ metadata = Benry::CmdApp::REGISTRY.metadata_get("foo:anyargs")
150
+ ok {metadata} != nil
151
+ Benry::CmdApp.define_alias("ali31", ["foo:anyargs", "aa"])
152
+ Benry::CmdApp.define_alias!("ali32", "ali31")
153
+ Benry::CmdApp.define_alias!("ali33", ["ali32" , "bb", "cc"])
154
+ #
155
+ sout, serr = capture_sio() { @context.start_action("ali33", ["xx", "yy"]) }
156
+ ok {sout} == "args=[\"aa\", \"bb\", \"cc\", \"xx\", \"yy\"], kwargs={}\n"
157
+ #
158
+ r = recorder()
159
+ r.fake_method(@context, :_invoke_action => nil)
160
+ @context.start_action("ali33", [])
161
+ ok {r[0].args} == [metadata, ["aa", "bb", "cc"], {}, {:once=>false}]
162
+ end
163
+
164
+ spec "[!5ru31] options in alias args are also parsed as well as command-line options." do
165
+ metadata = Benry::CmdApp::REGISTRY.metadata_get("hello")
166
+ ok {metadata} != nil
167
+ Benry::CmdApp.define_alias("ali41", ["hello", "-l", "it"])
168
+ Benry::CmdApp.define_alias!("ali42", ["ali41"])
169
+ #
170
+ sout, serr = capture_sio() { @context.start_action("ali42", ["-l", "fr", "Alice"]) }
171
+ ok {sout} == "Bonjour, Alice!\n"
172
+ #
173
+ r = recorder()
174
+ r.fake_method(@context, :_invoke_action => nil)
175
+ @context.start_action("ali42", ["Alice"])
176
+ ok {r[0].args} == [metadata, ["Alice"], {:lang=>"it"}, {:once=>false}]
177
+ end
178
+
179
+ spec "[!r3gfv] raises OptionError if invalid action options specified." do
180
+ pr = proc { @context.start_action("hello", ["-x"]) }
181
+ ok {pr}.raise?(Benry::CmdApp::OptionError,
182
+ "-x: Unknown option.")
183
+ end
184
+
185
+ spec "[!lg6br] runs action with command-line arguments." do
186
+ sout, serr = capture_sio do
187
+ @context.start_action("hello", ["-lit", "Alice"])
188
+ end
189
+ ok {sout} == "Chao, Alice!\n"
190
+ ok {serr} == ""
191
+ end
192
+
193
+ spec "[!jcguj] clears instance variables." do
194
+ @context.instance_eval { @status_dict[:x] = :done }
195
+ ok {@context.instance_eval { @status_dict } }.NOT.empty?
196
+ r = recorder()
197
+ r.record_method(@context, :teardown)
198
+ sout, serr = capture_sio do
199
+ @context.start_action("hello", ["-lit", "Alice"])
200
+ end
201
+ ok {r[0].name} == :teardown
202
+ ok {r[0].args} == []
203
+ ok {@context.instance_eval { @status_dict } }.empty?
204
+ end
205
+
206
+ end
207
+
208
+
209
+ topic '#invoke_action()' do
210
+
211
+ spec "[!uw6rq] raises ActionError if action name is not a string." do
212
+ pr = proc { @context.invoke_action(:hello, [], {}) }
213
+ ok {pr}.raise?(Benry::CmdApp::ActionError,
214
+ "`:hello`: Action name should be a string, but got Symbol object.")
215
+ end
216
+
217
+ spec "[!dri6e] if called from other action containing prefix, looks up action with the prefix firstly." do
218
+ sout, serr = capture_sio { @context.invoke_action("foo:ccc", [], {}) }
219
+ ok {sout} == "foo:prep\nfoo:ccc\n"
220
+ #
221
+ sout, serr = capture_sio { @context.invoke_action("foo:bar:bbb", [], {}) }
222
+ ok {sout} == "foo:bar:prep\nfoo:bar:bbb\n"
223
+ #
224
+ sout, serr = capture_sio { @context.invoke_action("foo:bar:baz:aaa", [], {}) }
225
+ ok {sout} == "foo:bar:baz:prep\nfoo:bar:baz:aaa\n"
226
+ end
227
+
228
+ spec "[!ygpsw] raises ActionError if action not found." do
229
+ pr = proc { @context.invoke_action("foo:xxx", [], {}) }
230
+ ok {pr}.raise?(Benry::CmdApp::ActionError,
231
+ "foo:xxx: Action not found.")
232
+ end
233
+
234
+ spec "[!de6a9] raises ActionError if alias name specified." do
235
+ Benry::CmdApp.define_alias("a0469", "hello")
236
+ pr = proc { @context.invoke_action("a0469", [], {}) }
237
+ ok {pr}.raise?(Benry::CmdApp::ActionError,
238
+ "a0469: Action expected, but it is an alias.")
239
+ end
240
+
241
+ spec "[!ev3qh] handles help option firstly if specified." do
242
+ config = Benry::CmdApp::Config.new("x", app_command: "testapp")
243
+ app = Benry::CmdApp::Application.new(config)
244
+ Benry::CmdApp._set_current_app(app)
245
+ at_end { Benry::CmdApp._set_current_app(nil) }
246
+ #
247
+ alias_names = []
248
+ Benry::CmdApp::REGISTRY.metadata_each(all: true) do |md|
249
+ alias_names << md.name if md.alias? && md.action == "hello"
250
+ end
251
+ alias_names.each {|alias_name| Benry::CmdApp.undef_alias(alias_name) }
252
+ #
253
+ expected = <<"END"
254
+ \e[1mtestapp hello\e[0m --- greeting message
255
+
256
+ \e[1;34mUsage:\e[0m
257
+ $ \e[1mtestapp hello\e[0m [<options>] [<name>]
258
+
259
+ \e[1;34mOptions:\e[0m
260
+ -l, --lang=<lang> : language name (en/fr/it)
261
+ END
262
+ #
263
+ sout, serr = capture_sio(tty: true) do
264
+ @context.invoke_action("hello", [], {:help=>true})
265
+ end
266
+ ok {sout} == expected
267
+ #
268
+ sout, serr = capture_sio(tty: true) { app.run("hello", "--help", "--lang=en") }
269
+ ok {sout} == expected
270
+ #
271
+ sout, serr = capture_sio(tty: true) { app.run("hello", "-h", "-lfr") }
272
+ ok {sout} == expected
273
+ end
274
+
275
+ spec "[!6hoir] don't run action and returns false if `once: true` specified and the action already done." do
276
+ ret1 = ret2 = ret3 = nil
277
+ sout, serr = capture_sio do
278
+ ret1 = @context.invoke_action("foo:prep", [], {}, once: true)
279
+ ret2 = @context.invoke_action("foo:prep", [], {}, once: true)
280
+ ret3 = @context.invoke_action("foo:prep", [], {}, once: true)
281
+ end
282
+ ok {sout} == "foo:prep\n"
283
+ ok {ret1} == true
284
+ ok {ret2} == false
285
+ ok {ret3} == false
286
+ #
287
+ sout, serr = capture_sio do
288
+ ret1 = @context.invoke_action("foo:prep", [], {}, once: false)
289
+ ret2 = @context.invoke_action("foo:prep", [], {}, once: false)
290
+ ret3 = @context.invoke_action("foo:prep", [], {}, once: false)
291
+ end
292
+ ok {sout} == "foo:prep\n" * 3
293
+ ok {ret1} == true
294
+ ok {ret2} == true
295
+ ok {ret3} == true
296
+ end
297
+
298
+ spec "[!xwlou] raises ActionError if looped aciton detected." do
299
+ pr = proc { @context.invoke_action("foo:loop1", [], {}) }
300
+ ok {pr}.raise?(Benry::CmdApp::ActionError,
301
+ "foo:loop1: Looped action detected.")
302
+ end
303
+
304
+ spec "[!peqk8] raises ActionError if args and opts not matched to action method." do
305
+ pr = proc { @context.invoke_action("foo:err1", [], {}) }
306
+ ok {pr}.raise?(Benry::CmdApp::ActionError,
307
+ "foo:err1: Argument required (but nothing specified).")
308
+ pr = proc { @context.invoke_action("foo:err1", ["X", "Y", "Z"], {}) }
309
+ ok {pr}.raise?(Benry::CmdApp::ActionError,
310
+ "foo:err1: Too much arguments (at most 2 args).")
311
+ end
312
+
313
+ spec "[!kao97] action invocation is nestable." do
314
+ sout, serr = capture_sio do
315
+ @context.invoke_action("foo:nest1", [], {})
316
+ end
317
+ ok {sout} == "nest3\n"
318
+ end
319
+
320
+ spec "[!5jdlh] runs action method with scope object." do
321
+ sout, serr = capture_sio do
322
+ @context.invoke_action("foo:prep", [], {})
323
+ end
324
+ ok {sout} == "foo:prep\n"
325
+ end
326
+
327
+ spec "[!9uue9] reports enter into and exit from action if global '-T' option specified." do
328
+ @config.trace_mode = true
329
+ sout, serr = capture_sio(tty: true) do
330
+ @context.invoke_action("foo:nest1", [], {})
331
+ end
332
+ ok {sout} == <<"END"
333
+ \e[33m### enter: foo:nest1\e[0m
334
+ \e[33m### enter: foo:nest2\e[0m
335
+ \e[33m### enter: foo:nest3\e[0m
336
+ nest3
337
+ \e[33m### exit: foo:nest3\e[0m
338
+ \e[33m### exit: foo:nest2\e[0m
339
+ \e[33m### exit: foo:nest1\e[0m
340
+ END
341
+ #
342
+ sout, serr = capture_sio(tty: false) do
343
+ @context.invoke_action("foo:nest1", [], {})
344
+ end
345
+ ok {sout} == <<"END"
346
+ ### enter: foo:nest1
347
+ ### enter: foo:nest2
348
+ ### enter: foo:nest3
349
+ nest3
350
+ ### exit: foo:nest3
351
+ ### exit: foo:nest2
352
+ ### exit: foo:nest1
353
+ END
354
+ end
355
+
356
+ spec "[!ndxc3] returns true if action invoked." do
357
+ ret = nil
358
+ capture_sio() do
359
+ ret = @context.invoke_action("foo:bar:prep", [], {})
360
+ end
361
+ ok {ret} == true
362
+ end
363
+
364
+ end
365
+
366
+
367
+ topic '#new_scope_object()' do
368
+
369
+ spec "[!1uzs3] creates new scope object." do
370
+ md = Benry::CmdApp::REGISTRY.metadata_get("hello")
371
+ x = @context.__send__(:new_scope_object, md)
372
+ ok {x}.is_a?(md.klass)
373
+ ok {x}.is_a?(MyAction)
374
+ ok {x.instance_variable_get(:@__context__)} == @context
375
+ end
376
+
377
+ end
378
+
379
+ end
380
+
381
+
382
+ end