benry-cmdapp 0.2.0 → 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.
data/test/app_test.rb CHANGED
@@ -1,1365 +1,1169 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
2
3
 
3
- require 'oktest'
4
4
 
5
- require 'benry/cmdapp'
6
- require_relative './shared'
5
+ require_relative 'shared'
7
6
 
8
7
 
9
8
  Oktest.scope do
10
9
 
11
10
 
11
+ topic Benry::CmdApp::Application do
12
12
 
13
- topic Benry::CmdApp::Config do
13
+ before do
14
+ @config = Benry::CmdApp::Config.new("test app", "1.2.3", app_command: "testapp")
15
+ @app = Benry::CmdApp::Application.new(@config)
16
+ end
14
17
 
15
18
 
16
- topic '#initialize()' do
19
+ topic '#main()' do
17
20
 
18
- spec "[!uve4e] sets command name automatically if not provided." do
19
- config = Benry::CmdApp::Config.new("test")
20
- ok {config.app_command} != nil
21
- ok {config.app_command} == File.basename($0)
21
+ spec "[!65e9n] returns `0` as status code." do
22
+ ret = nil
23
+ capture_sio { ret = @app.main(["help"]) }
24
+ ok {ret} == 0
22
25
  end
23
26
 
24
- end
25
-
27
+ case_when "[!bkbb4] when error raised..." do
26
28
 
27
- end
29
+ spec "[!k4qov] not catch error if debug mode is enabled." do
30
+ debug_mode = $DEBUG_MODE
31
+ at_end { $DEBUG_MODE = debug_mode }
32
+ pr = proc { @app.main(["--debug", "foobar"]) }
33
+ ok {pr}.raise?(Benry::CmdApp::CommandError, "foobar: Action not found.")
34
+ end
28
35
 
36
+ spec "[!lhlff] catches error if BaseError raised or `should_rescue?()` returns true." do
37
+ pr = proc { @app.main(["testerr1"]) }
38
+ ok {pr}.raise?(ZeroDivisionError)
39
+ #
40
+ r = recorder()
41
+ r.fake_method(@app, :should_rescue? => true)
42
+ sout, serr = capture_sio(tty: true) do
43
+ pr = proc { @app.main(["testerr1"]) }
44
+ ok {pr}.NOT.raise?(ZeroDivisionError)
45
+ end
46
+ ok {sout} == ""
47
+ ok {serr} =~ /\A\e\[31m\[ERROR\]\e\[0m divided by 0$/
48
+ end
29
49
 
30
- topic Benry::CmdApp::AppOptionSchema do
50
+ spec "[!35x5p] prints error into stderr." do
51
+ sout, serr = capture_sio(tty: true) { @app.main(["foobar"]) }
52
+ ok {sout} == ""
53
+ ok {serr} =~ /^\e\[31m\[ERROR\]\e\[0m foobar: Action not found.\n/
54
+ end
31
55
 
56
+ spec "[!z39bh] prints backtrace unless error is a CommandError." do
57
+ sout, serr = capture_sio { @app.main(["testerr2"]) }
58
+ ok {sout} == ""
59
+ ok {serr} =~ /^\[ERROR\] testerr2: Looped action detected.\n/
60
+ ok {serr} =~ /^ From .*:\d+:in /
61
+ end
32
62
 
33
- topic '#initialize()' do
63
+ spec "[!dzept] returns `1` as status code." do
64
+ ret = nil
65
+ capture_sio { ret = @app.main(["help", "foo"]) }
66
+ ok {ret} == 1
67
+ end
34
68
 
35
- def new_gschema(desc="", version=nil, **kwargs)
36
- config = Benry::CmdApp::Config.new(desc, version, **kwargs)
37
- x = Benry::CmdApp::AppOptionSchema.new(config)
38
- return x
39
69
  end
40
70
 
41
- spec "[!3ihzx] do nothing when config is nil." do
42
- x = nil
43
- pr = proc { x = Benry::CmdApp::AppOptionSchema.new(nil) }
44
- ok {pr}.NOT.raise?(Exception)
45
- ok {x}.is_a?(Benry::CmdApp::AppOptionSchema)
46
- end
71
+ end
47
72
 
48
- spec "[!tq2ol] adds '-h, --help' option if 'config.option_help' is set." do
49
- x = new_gschema(option_help: true)
50
- ok {x.find_long_option("help")} != nil
51
- ok {x.find_short_option("h")} != nil
52
- x = new_gschema(option_help: false)
53
- ok {x.find_long_option("help")} == nil
54
- ok {x.find_short_option("h")} == nil
55
- end
56
73
 
57
- spec "[!mbtw0] adds '-V, --version' option if 'config.app_version' is set." do
58
- x = new_gschema("", "0.0.0")
59
- ok {x.find_long_option("version")} != nil
60
- ok {x.find_short_option("V")} != nil
61
- x = new_gschema("", nil)
62
- ok {x.find_long_option("version")} == nil
63
- ok {x.find_short_option("V")} == nil
64
- end
74
+ topic '#run()' do
65
75
 
66
- spec "[!f5do6] adds '-a, --all' option if 'config.option_all' is set." do
67
- x = new_gschema(option_all: true)
68
- ok {x.find_long_option("all")} != nil
69
- ok {x.find_short_option("a")} != nil
70
- x = new_gschema(option_all: false)
71
- ok {x.find_long_option("all")} == nil
72
- ok {x.find_short_option("a")} == nil
76
+ spec "[!etbbc] calls setup method at beginning of this method." do
77
+ r = recorder()
78
+ r.record_method(@app, :setup)
79
+ capture_sio { @app.run("help") }
80
+ ok {r[0].name} == :setup
81
+ ok {r[0].args} == []
73
82
  end
74
83
 
75
- spec "[!cracf] adds '-v, --verbose' option if 'config.option_verbose' is set." do
76
- x = new_gschema(option_verbose: true)
77
- ok {x.find_long_option("verbose")} != nil
78
- ok {x.find_short_option("v")} != nil
79
- x = new_gschema(option_verbose: false)
80
- ok {x.find_long_option("verbose")} == nil
81
- ok {x.find_short_option("v")} == nil
84
+ spec "[!hguvb] handles global options." do
85
+ sout, serr = capture_sio { @app.run("--version") }
86
+ ok {sout} == "1.2.3\n"
87
+ ok {serr} == ""
88
+ sout, serr = capture_sio { @app.run("--help") }
89
+ ok {sout} =~ /^Usage:\n/
90
+ ok {serr} == ""
82
91
  end
83
92
 
84
- spec "[!2vil6] adds '-q, --quiet' option if 'config.option_quiet' is set." do
85
- x = new_gschema(option_quiet: true)
86
- ok {x.find_long_option("quiet")} != nil
87
- ok {x.find_short_option("q")} != nil
88
- x = new_gschema(option_quiet: false)
89
- ok {x.find_long_option("quiet")} == nil
90
- ok {x.find_short_option("q")} == nil
93
+ spec "[!pf1d2] calls teardown method at end of this method." do
94
+ r = recorder()
95
+ r.record_method(@app, :teardown)
96
+ capture_sio { @app.run("hello", "Alice") }
97
+ ok {r.length} == 1
98
+ ok {r[0].name} == :teardown
99
+ ok {r[0].args} == []
100
+ #
101
+ begin
102
+ capture_sio { @app.run("testerr1") }
103
+ rescue ZeroDivisionError
104
+ nil
105
+ end
106
+ ok {r.length} == 2
107
+ ok {r[0].name} == :teardown
108
+ ok {r[0].args} == []
91
109
  end
92
110
 
93
- spec "[!6zw3j] adds '--color=<on|off>' option if 'config.option_color' is set." do
94
- x = new_gschema(option_color: true)
95
- ok {x.find_long_option("color")} != nil
96
- x = new_gschema(option_quiet: false)
97
- ok {x.find_long_option("color")} == nil
98
- end
111
+ end
99
112
 
100
- spec "[!29wfy] adds '-D, --debug' option if 'config.option_debug' is set." do
101
- x = new_gschema(option_debug: true)
102
- ok {x.find_long_option("debug")} != nil
103
- ok {x.find_short_option("D")} != nil
104
- x = new_gschema(option_debug: false)
105
- ok {x.find_long_option("debug")} == nil
106
- ok {x.find_short_option("D")} == nil
107
- end
108
113
 
109
- spec "[!s97go] adds '-T, --trace' option if 'config.option_trace' is set." do
110
- x = new_gschema(option_trace: true)
111
- ok {x.find_long_option("trace")} != nil
112
- ok {x.find_short_option("T")} != nil
113
- x = new_gschema(option_debug: false)
114
- ok {x.find_long_option("trace")} == nil
115
- ok {x.find_short_option("T")} == nil
116
- end
114
+ topic '#handle_action()' do
117
115
 
118
- end
116
+ case_when "[!3qw3p] when no arguments specified..." do
119
117
 
118
+ spec "[!zl9em] lists actions if default action is not set." do
119
+ sout, serr = capture_sio { @app.run() }
120
+ ok {sout} =~ /\AActions:\n/
121
+ ok {sout} =~ /^ hello +: greeting message$/
122
+ ok {serr} == ""
123
+ end
120
124
 
121
- topic '#sort_options_in_this_order()' do
125
+ spec "[!89hqb] lists all actions including hidden ones if `-a` or `--all` specified." do
126
+ sout, serr = capture_sio { @app.run() }
127
+ ok {sout} !~ /^ debuginfo/
128
+ sout, serr = capture_sio { @app.run("-a") }
129
+ ok {sout} =~ /^ debuginfo +: hidden action$/
130
+ end
122
131
 
123
- def new_gschema(desc="app test", version="1.0.0")
124
- config = Benry::CmdApp::Config.new(desc, version)
125
- config.option_all = true
126
- config.option_verbose = true
127
- config.option_quiet = true
128
- config.option_debug = true
129
- return Benry::CmdApp::AppOptionSchema.new(config)
130
- end
132
+ spec "[!k4xxp] runs default action if it is set." do
133
+ @config.default_action = "hello"
134
+ sout, serr = capture_sio { @app.run() }
135
+ ok {sout} !~ /^\AActions:/
136
+ ok {sout} == "Hello, world!\n"
137
+ end
131
138
 
132
- spec "[!6udxr] sorts options in order of keys specified." do
133
- x = new_gschema()
134
- keys1 = x.each.collect(&:key)
135
- ok {keys1} == [:help, :version, :all, :verbose, :quiet, :debug]
136
- x.sort_options_in_this_order(:help, :quiet, :verbose, :all, :trace, :debug, :version)
137
- keys2 = x.each.collect(&:key)
138
- ok {keys2} == [:help, :quiet, :verbose, :all, :debug, :version]
139
139
  end
140
140
 
141
- spec "[!8hhuf] options which key doesn't appear in keys are moved at end of options." do
142
- x = new_gschema()
143
- x.sort_options_in_this_order(:quiet, :verbose, :all, :debug) # missing :help and :version
144
- keys = x.each.collect(&:key)
145
- ok {keys[-2]} == :help
146
- ok {keys[-1]} == :version
147
- ok {keys} == [:quiet, :verbose, :all, :debug, :help, :version]
148
- end
141
+ case_when "[!xaamy] when prefix specified..." do
149
142
 
150
- end
143
+ spec "[!7l3fh] lists actions starting with prefix." do
144
+ sout, serr = capture_sio { @app.run("git:") }
145
+ ok {sout} == <<END
146
+ Actions:
147
+ git:stage : same as `git add -p`
148
+ git:staged : same as `git diff --cached`
149
+ git:unstage : same as `git reset HEAD`
150
+ END
151
+ end
151
152
 
153
+ spec "[!g0k1g] lists all actions including hidden ones if `-a` or `--all` specified." do
154
+ sout, serr = capture_sio { @app.run("-a", "git:") }
155
+ ok {sout} == <<END
156
+ Actions:
157
+ git:correct : same as `git commit --amend`
158
+ git:stage : same as `git add -p`
159
+ git:staged : same as `git diff --cached`
160
+ git:unstage : same as `git reset HEAD`
161
+ END
162
+ end
152
163
 
153
- end
164
+ end
154
165
 
166
+ case_when "[!vphz3] else..." do
155
167
 
156
- topic Benry::CmdApp::Application do
157
- include CommonTestingHelper
158
-
159
- class AppTest < Benry::CmdApp::ActionScope
160
- @action.("print greeting message")
161
- @option.(:lang, "-l, --lang=<en|fr|it>", "language")
162
- def sayhello(user="world", lang: "en")
163
- case lang
164
- when "en" ; puts "Hello, #{user}!"
165
- when "fr" ; puts "Bonjour, #{user}!"
166
- when "it" ; puts "Ciao, #{user}!"
167
- else ; raise "#{lang}: unknown language."
168
+ spec "[!bq39a] runs action with arguments." do
169
+ sout, serr = capture_sio { @app.run("hello", "Alice") }
170
+ ok {sout} == "Hello, Alice!\n"
171
+ end
172
+
173
+ spec "[!5yd8x] returns 0 when action invoked successfully." do
174
+ ret = nil
175
+ capture_sio { ret = @app.run("hello", "Alice") }
176
+ ok {ret} == 0
168
177
  end
178
+
169
179
  end
170
- end
171
180
 
172
- before do
173
- @config = Benry::CmdApp::Config.new("test app", "1.0.0",
174
- app_name: "TestApp", app_command: "testapp",
175
- default_action: nil,
176
- option_all: true, option_debug: true)
177
- @app = Benry::CmdApp::Application.new(@config)
178
181
  end
179
182
 
180
- def _run_app(*args)
181
- sout, serr = capture_sio { @app.run(*args) }
182
- ok {serr} == ""
183
- return sout
184
- end
185
183
 
184
+ topic '#render_help_message()' do
186
185
 
187
- topic '#initialize()' do
186
+ spec "[!2oax5] returns action help message if action name is specified." do
187
+ ret = @app.render_help_message("hello")
188
+ ok {ret} == <<"END"
189
+ \e[1mtestapp hello\e[0m --- greeting message
188
190
 
189
- spec "[!jkprn] creates option schema object according to config." do
190
- c = Benry::CmdApp::Config.new("test", "1.0.0", option_debug: true)
191
- app = Benry::CmdApp::Application.new(c)
192
- schema = app.instance_variable_get('@schema')
193
- ok {schema}.is_a?(Benry::CmdOpt::Schema)
194
- items = schema.each.to_a()
195
- ok {items[0].key} == :help
196
- ok {items[1].key} == :version
197
- ok {items[2].key} == :debug
198
- ok {schema.option_help()} == <<END
199
- -h, --help : print help message
200
- -V, --version : print version
201
- -D, --debug : debug mode (set $DEBUG_MODE to true)
191
+ \e[1;34mUsage:\e[0m
192
+ $ \e[1mtestapp hello\e[0m [<options>] [<name>]
193
+
194
+ \e[1;34mOptions:\e[0m
195
+ -l, --lang=<lang> : language name (en/fr/it)
202
196
  END
203
197
  end
204
198
 
205
- spec "[!h786g] acceps callback block." do
206
- config = Benry::CmdApp::Config.new("test app")
207
- n = 0
208
- app = Benry::CmdApp::Application.new(config) do |args|
209
- n += 1
210
- end
211
- ok {app.callback}.is_a?(Proc)
212
- ok {n} == 0
213
- app.callback.call([])
214
- ok {n} == 1
215
- app.callback.call([])
216
- ok {n} == 2
199
+ spec "[!d6veb] returns application help message if action name is not specified." do
200
+ ret = @app.render_help_message(nil)
201
+ ok {ret} =~ /^\e\[1;34mUsage:\e\[0m$/
202
+ ok {ret} =~ /^\e\[1;34mOptions:\e\[0m$/
203
+ ok {ret} =~ /^\e\[1;34mActions:\e\[0m$/
204
+ end
205
+
206
+ spec "[!tf2wp] includes hidden actions and options into help message if `all: true` passed." do
207
+ rexp1 = /^\e\[2m --debug : debug mode\e\[0m/
208
+ rexp2 = /^\e\[2m debuginfo : hidden action\e\[0m/
209
+ #
210
+ s = @app.render_help_message(nil, all: true)
211
+ ok {s} =~ rexp1
212
+ ok {s} =~ rexp2
213
+ #
214
+ s = @app.render_help_message(nil, all: false)
215
+ ok {s} !~ rexp1
216
+ ok {s} !~ rexp2
217
217
  end
218
218
 
219
219
  end
220
220
 
221
221
 
222
- topic '#main()' do
222
+ topic '#setup()' do
223
223
 
224
- after do
225
- $cmdapp_config = nil
224
+ spec "[!6hi1y] stores current application." do
225
+ at_end { Benry::CmdApp._set_current_app(nil) }
226
+ ok {Benry::CmdApp.current_app()} == nil
227
+ @app.__send__(:setup)
228
+ ok {Benry::CmdApp.current_app()} == @app
226
229
  end
227
230
 
228
- spec "[!y6q9z] runs action with options." do
229
- sout, serr = capture_sio { @app.main(["sayhello", "-l", "it", "Alice"]) }
230
- ok {serr} == ""
231
- ok {sout} == "Ciao, Alice!\n"
232
- end
231
+ end
233
232
 
234
- spec "[!a7d4w] prints error message with '[ERROR]' prompt." do
235
- sout, serr = capture_sio { @app.main(["sayhello", "Alice", "Bob"]) }
236
- ok {serr} == "\e[0;31m[ERROR]\e[0m sayhello: Too much arguments (at most 1).\n"
237
- ok {sout} == ""
238
- end
239
233
 
240
- spec "[!r7opi] prints filename and line number on where error raised if DefinitionError." do
241
- class MainTest1 < Benry::CmdApp::ActionScope
242
- prefix "main1"
243
- @action.("test")
244
- def err1
245
- MainTest1.class_eval do
246
- @action.("test")
247
- @option.(:foo, "--foo", "foo")
248
- def err2(bar: nil) # should have keyword parameter 'foo'
249
- end
250
- end
251
- end
252
- end
253
- lineno = __LINE__ - 5
254
- sout, serr = capture_sio { @app.main(["main1:err1"]) }
255
- ok {sout} == ""
256
- serr = serr.sub(/file: \/.*?test\//, 'file: test/')
257
- ok {serr} == <<"END"
258
- \e[0;31m[ERROR]\e[0m def err2(): Should have keyword parameter 'foo' for '@option.(:foo)', but not.
259
- \t\(file: test\/app_test\.rb, line: #{lineno})
260
- END
261
- end
234
+ topic '#teardown()' do
262
235
 
263
- spec "[!v0zrf] error location can be filtered by block." do
264
- class MainTest2 < Benry::CmdApp::ActionScope
265
- prefix "main2"
266
- @action.("test")
267
- def err2
268
- _err2()
269
- end
270
- def _err2()
271
- MainTest2.class_eval do # == lineno2
272
- @action.("test")
273
- @option.(:foo, "--foo", "foo")
274
- def err2x(bar: nil) # == lineno1
275
- end
276
- end
277
- end
278
- end
279
- lineno1 = __LINE__ - 5
280
- lineno2 = lineno1 - 3
281
- ## no filter
282
- sout, serr = capture_sio { @app.main(["main2:err2"]) }
283
- ok {sout} == ""
284
- ok {serr} =~ /\t\(file: .*\/app_test\.rb, line: #{lineno1}\)\n/
285
- ## filter by block
286
- sout, serr = capture_sio {
287
- @app.main(["main2:err2"]) {|exc| exc.lineno == lineno2 }
288
- }
289
- ok {sout} == ""
290
- ok {serr} =~ /\t\(file: .*\/app_test\.rb, line: #{lineno2}\)\n/
236
+ spec "[!t44mv] removes current applicatin from data store." do
237
+ at_end { Benry::CmdApp._set_current_app(nil) }
238
+ ok {Benry::CmdApp.current_app()} == nil
239
+ @app.__send__(:setup)
240
+ ok {Benry::CmdApp.current_app()} == @app
291
241
  end
292
242
 
293
- spec "[!6ro6n] not catch error when $DEBUG_MODE is on." do
294
- bkup = $DEBUG_MODE
295
- begin
296
- pr = proc { @app.main(["-D", "sayhello", "Alice", "Bob"]) }
297
- ok {pr}.raise?(Benry::CmdApp::CommandError,
298
- "sayhello: Too much arguments (at most 1).")
299
- ensure
300
- $DEBUG_MODE = bkup
301
- end
302
- end
243
+ end
303
244
 
304
- spec "[!5oypr] returns 0 as exit code when no errors occurred." do
305
- ret = nil
306
- sout, serr = capture_sio do
307
- ret = @app.main(["sayhello", "Alice", "-l", "it"])
308
- end
309
- ok {ret} == 0
310
- ok {serr} == ""
311
- ok {sout} == "Ciao, Alice!\n"
312
- end
313
245
 
314
- spec "[!qk5q5] returns 1 as exit code when error occurred." do
315
- ret = nil
316
- sout, serr = capture_sio do
317
- ret = @app.main(["sayhello", "Alice", "Bob"])
318
- end
319
- ok {ret} == 1
320
- ok {serr} == "\e[0;31m[ERROR]\e[0m sayhello: Too much arguments (at most 1).\n"
321
- ok {sout} == ""
246
+ topic '#parse_global_options()' do
247
+
248
+ spec "[!9c9r8] parses global options." do
249
+ args = ["-hl", "foo"]
250
+ opts = @app.instance_eval { parse_global_options(args) }
251
+ ok {opts} == {help: true, list: true}
252
+ ok {args} == ["foo"]
322
253
  end
323
254
 
324
255
  end
325
256
 
326
257
 
327
- topic '#run()' do
258
+ topic '#toggle_global_options()' do
328
259
 
329
- class AppRunTest < Benry::CmdApp::ActionScope
330
- #
331
- @action.("test config")
332
- def check_config()
333
- puts "$cmdapp_config.class=#{$cmdapp_config.class.name}"
334
- end
335
- #
336
- @action.("test global option parseing")
337
- @option.(:help, "-h, --help", "print help")
338
- def test_globalopt(help: false)
339
- puts "help=#{help}"
340
- end
341
- #
342
- @action.("test debug option")
343
- def test_debugopt(help: false)
344
- puts "$DEBUG_MODE=#{$DEBUG_MODE}"
345
- end
346
- #
347
- @action.("arity test")
348
- def test_arity1(xx, yy, zz=nil)
349
- end
260
+ spec "[!xwcyl] sets `$VERBOSE_MODE` and `$QUIET_MODE` according to global options." do
261
+ at_end { $VERBOSE_MODE = nil; $QUIET_MODE = nil }
350
262
  #
351
- @action.("arity test with variable args")
352
- def test_arity2(xx, yy, zz=nil, *rest)
353
- end
263
+ opts = {verbose: true}
264
+ @app.instance_eval { toggle_global_options(opts) }
265
+ ok {$VERBOSE_MODE} == true
266
+ ok {$QUIET_MODE} == false
354
267
  #
355
- @action.("raises exception")
356
- def test_exception1()
357
- 1/0
358
- end
359
- #
360
- @action.("loop test")
361
- def test_loop1()
362
- run_action_once("test-loop2")
363
- end
364
- @action.("loop test")
365
- def test_loop2()
366
- run_action_once("test-loop1")
367
- end
268
+ opts = {quiet: true}
269
+ @app.instance_eval { toggle_global_options(opts) }
270
+ ok {$VERBOSE_MODE} == false
271
+ ok {$QUIET_MODE} == true
368
272
  end
369
273
 
370
- spec "[!t4ypg] sets $cmdapp_config at beginning." do
371
- sout, serr = capture_sio { @app.run("check-config") }
372
- ok {serr} == ""
373
- ok {sout} == "$cmdapp_config.class=Benry::CmdApp::Config\n"
274
+ spec "[!sucqp] sets `$DEBUG_MODE` according to global options." do
275
+ at_end { $DEBUG_MODE = nil }
276
+ opts = {debug: true}
277
+ @app.instance_eval { toggle_global_options(opts) }
278
+ ok {$DEBUG_MODE} == true
374
279
  end
375
280
 
376
- spec "[!pyotc] sets global options to '@global_options'." do
377
- ok {@app.instance_variable_get('@global_options')} == nil
378
- capture_sio { @app.run("--help") }
379
- ok {@app.instance_variable_get('@global_options')} == {:help=>true}
281
+ spec "[!510eb] sets `$COLOR_MODE` according to global option." do
282
+ bkup = $COLOR_MODE
283
+ at_end { $COLOR_MODE = bkup }
284
+ $COLOR_MODE = nil
285
+ #
286
+ opts = {color: true}
287
+ @app.instance_eval { toggle_global_options(opts) }
288
+ ok {$COLOR_MODE} == true
289
+ #
290
+ opts = {color: false}
291
+ @app.instance_eval { toggle_global_options(opts) }
292
+ ok {$COLOR_MODE} == false
380
293
  end
381
294
 
382
- spec "[!go9kk] sets global variables according to global options." do
383
- config = Benry::CmdApp::Config.new("test app", "1.0.0",
384
- option_verbose: true,
385
- option_quiet: true,
386
- option_debug: true,
387
- option_color: true)
388
- app = Benry::CmdApp::Application.new(config)
389
- bkup = [$QUIET_MODE, $DEBUG_MODE, $COLOR_MODE]
390
- begin
391
- ['-v', '--verbose'].each do |x|
392
- $QUIET_MODE, $DEBUG_MODE, $COLOR_MODE = nil, nil, nil
393
- capture_sio { app.run(x, '-h') }
394
- ok {[$QUIET_MODE, $DEBUG_MODE, $COLOR_MODE]} == [false, nil, nil]
395
- end
396
- #
397
- ['-q', '--quiet'].each do |x|
398
- $QUIET_MODE, $DEBUG_MODE, $COLOR_MODE = nil, nil, nil
399
- capture_sio { app.run(x, '-h') }
400
- ok {[$QUIET_MODE, $DEBUG_MODE, $COLOR_MODE]} == [true, nil, nil]
401
- end
402
- #
403
- ['-D', '--debug'].each do |x|
404
- $QUIET_MODE, $DEBUG_MODE, $COLOR_MODE = nil, nil, nil
405
- capture_sio { app.run(x, '-h') }
406
- ok {[$QUIET_MODE, $DEBUG_MODE, $COLOR_MODE]} == [nil, true, nil]
407
- end
408
- #
409
- ['--color', '--color=on'].each do |x|
410
- $QUIET_MODE, $DEBUG_MODE, $COLOR_MODE = nil, nil, nil
411
- capture_sio { app.run(x, '-h') }
412
- ok {[$QUIET_MODE, $DEBUG_MODE, $COLOR_MODE]} == [nil, nil, true]
413
- end
414
- ['--color=off'].each do |x|
415
- $QUIET_MODE, $DEBUG_MODE, $COLOR_MODE = nil, nil, nil
416
- capture_sio { app.run(x, '-h') }
417
- ok {[$QUIET_MODE, $DEBUG_MODE, $COLOR_MODE]} == [nil, nil, false]
418
- end
419
- ensure
420
- $QUIET_MODE, $DEBUG_MODE, $COLOR_MODE = bkup
421
- end
295
+ spec "[!y9fow] sets `config.trace_mode` if global option specified." do
296
+ opts = {trace: true}
297
+ @app.instance_eval { toggle_global_options(opts) }
298
+ ok {@config.trace_mode} == true
299
+ #
300
+ opts = {trace: false}
301
+ @app.instance_eval { toggle_global_options(opts) }
302
+ ok {@config.trace_mode} == false
422
303
  end
423
304
 
424
- spec "[!5iczl] skip actions if help option or version option specified." do
425
- def @app.do_callback(args, global_opts)
426
- @_called_ = args.dup
427
- end
428
- capture_sio { @app.run("--help") }
429
- ok {@app.instance_variable_get('@_called_')} == nil
430
- capture_sio { @app.run("--version") }
431
- ok {@app.instance_variable_get('@_called_')} == nil
432
- capture_sio { @app.run("sayhello") }
433
- ok {@app.instance_variable_get('@_called_')} == ["sayhello"]
305
+ spec "[!dply7] sets `$DRYRUN_MODE` according to global option." do
306
+ ok {$DRYRUN_MODE} == nil
307
+ at_end { $DRYRUN_MODE = nil }
308
+ #
309
+ opts = {dryrun: true}
310
+ @app.instance_eval { toggle_global_options(opts) }
311
+ ok {$DRYRUN_MODE} == true
434
312
  end
435
313
 
436
- spec "[!w584g] calls callback method." do
437
- def @app.do_callback(args, global_opts)
438
- @_called_ = args.dup
314
+ end
315
+
316
+
317
+ topic '#handle_global_options()' do
318
+
319
+ spec "[!366kv] prints help message if global option `-h, --help` specified." do
320
+ opts = {help: true}
321
+ sout, serr = capture_sio do
322
+ @app.instance_eval { handle_global_options(opts, []) }
439
323
  end
440
- ok {@app.instance_variable_get('@_called_')} == nil
441
- capture_sio { @app.run("sayhello") }
442
- ok {@app.instance_variable_get('@_called_')} == ["sayhello"]
324
+ ok {sout} =~ /^Usage:/
325
+ ok {sout} =~ /^Options:/
326
+ ok {sout} =~ /^Actions:/
443
327
  end
444
328
 
445
- spec "[!pbug7] skip actions if callback method throws `:SKIP`." do
446
- def @app.do_callback(args, global_opts)
447
- @_called1 = args.dup
448
- throw :SKIP
449
- end
450
- def @app.do_find_action(args, global_opts)
451
- super
452
- @_called2 = args.dup
329
+ spec "[!7mapy] includes hidden actions into help message if `-a, --all` specified." do
330
+ rexp1 = /^ --debug : debug mode$/
331
+ rexp2 = /^ debuginfo : hidden action$/
332
+ #
333
+ opts = {help: true}
334
+ sout, serr = capture_sio do
335
+ @app.instance_eval { handle_global_options(opts, []) }
453
336
  end
454
- ok {@app.instance_variable_get('@_called1')} == nil
455
- ok {@app.instance_variable_get('@_called2')} == nil
456
- capture_sio { @app.run("sayhello") }
457
- ok {@app.instance_variable_get('@_called1')} == ["sayhello"]
458
- ok {@app.instance_variable_get('@_called2')} == nil
459
- end
460
-
461
- spec "[!avxos] prints candidate actions if action name ends with ':'." do
462
- class CandidateTest1 < Benry::CmdApp::ActionScope
463
- prefix "candi:date1"
464
- @action.("test")
465
- def bbb(); end
466
- @action.("test")
467
- def aaa(); end
337
+ ok {sout} !~ rexp1
338
+ ok {sout} !~ rexp2
339
+ #
340
+ opts = {help: true, all: true}
341
+ sout, serr = capture_sio do
342
+ @app.instance_eval { handle_global_options(opts, []) }
468
343
  end
469
- ## without tty
470
- sout, serr = capture_sio(tty: false) { @app.run("candi:date1:") }
471
- ok {serr} == ""
472
- ok {sout} == <<"END"
473
- Actions:
474
- candi:date1:aaa : test
475
- candi:date1:bbb : test
476
- END
477
- ## with tty
478
- sout, serr = capture_sio(tty: true) { @app.run("candi:date1:") }
479
- ok {serr} == ""
480
- ok {sout} == <<"END"
481
- \e[34mActions:\e[0m
482
- \e[1mcandi:date1:aaa \e[0m : test
483
- \e[1mcandi:date1:bbb \e[0m : test
484
- END
344
+ ok {sout} =~ rexp1
345
+ ok {sout} =~ rexp2
485
346
  end
486
347
 
487
- spec "[!eeh0y] candidates are not printed if 'config.feat_candidate' is false." do
488
- class CandidateTest5 < Benry::CmdApp::ActionScope
489
- prefix "candi:date5"
490
- @action.("test b")
491
- def bbb(); end
492
- @action.("test a")
493
- def aaa(); end
348
+ spec "[!dkjw8] prints version number if global option `-V, --version` specified." do
349
+ opts = {version: true}
350
+ sout, serr = capture_sio do
351
+ @app.instance_eval { handle_global_options(opts, []) }
494
352
  end
495
- ## flag is on
496
- @app.config.feat_candidate = false
497
- pr = proc { @app.run("candi:date5:") }
498
- ok {pr}.raise?(Benry::CmdApp::CommandError,
499
- "candi:date5:: Unknown action.")
500
- ## flag is off
501
- @app.config.feat_candidate = true
502
- sout, serr = capture_sio(tty: false) { @app.run("candi:date5:") }
503
- ok {serr} == ""
504
- ok {sout} == <<"END"
505
- Actions:
506
- candi:date5:aaa : test a
507
- candi:date5:bbb : test b
508
- END
353
+ ok {sout} == "1.2.3\n"
509
354
  end
510
355
 
511
- spec "[!l0g1l] skip actions if no action specified and 'config.default_help' is set." do
512
- def @app.do_find_action(args, global_opts)
513
- ret = super
514
- @_args1 = args.dup
515
- @_result = ret
516
- ret
356
+ spec "[!hj4hf] prints action and alias list if global option `-l, --list` specified." do
357
+ opts = {list: true}
358
+ sout, serr = capture_sio(tty: true) do
359
+ @app.instance_eval { handle_global_options(opts, []) }
517
360
  end
518
- def @app.do_run_action(metadata, args, global_opts)
519
- ret = super
520
- @_args2 = args.dup
521
- ret
361
+ ok {sout} =~ /\A\e\[1;34mActions:\e\[0m$/
362
+ if Benry::CmdApp::REGISTRY.metadata_each.any? {|md| md.alias? }
363
+ ok {sout} =~ /\n\n\e\[1;34mAliases:\e\[0m$/
364
+ else
365
+ ok {sout} !~ /Aliases:/
522
366
  end
523
- @app.config.default_help = true
524
- capture_sio { @app.run() }
525
- ok {@app.instance_variable_get('@_args1')} == []
526
- ok {@app.instance_variable_get('@_result')} == nil
527
- ok {@app.instance_variable_get('@_args2')} == nil
528
367
  end
529
368
 
530
- spec "[!x1xgc] run action with options and arguments." do
531
- sout, serr = capture_sio { @app.run("sayhello", "Alice", "-l", "it") }
532
- ok {serr} == ""
533
- ok {sout} == "Ciao, Alice!\n"
534
- end
535
-
536
- spec "[!agfdi] reports error when action not found." do
537
- pr = proc { @app.run("xxx-yyy") }
538
- ok {pr}.raise?(Benry::CmdApp::CommandError,
539
- "xxx-yyy: Unknown action.")
540
- end
541
-
542
- spec "[!v5k56] runs default action if action not specified." do
543
- @config.default_action = "sayhello"
544
- sout, serr = capture_sio { @app.run() }
545
- ok {serr} == ""
546
- ok {sout} == "Hello, world!\n"
547
- end
548
-
549
- spec "[!o5i3w] reports error when default action not found." do
550
- @config.default_action = "xxx-zzz"
551
- pr = proc { @app.run() }
552
- ok {pr}.raise?(Benry::CmdApp::CommandError,
553
- "xxx-zzz: Unknown default action.")
554
- end
555
-
556
- spec "[!7h0ku] prints help if no action but 'config.default_help' is true." do
557
- expected, serr = capture_sio { @app.run("-h") }
558
- ok {serr} == ""
559
- ok {expected} =~ /^Usage:/
369
+ spec "[!tyxwo] includes hidden actions into action list if `-a, --all` specified." do
370
+ rexp = /^ debuginfo +: hidden action$/
560
371
  #
561
- @config.default_help = true
562
- sout, serr = capture_sio { @app.run() }
563
- ok {serr} == ""
564
- ok {sout} == expected
372
+ opts = {list: true}
373
+ sout, serr = capture_sio do
374
+ @app.instance_eval { handle_global_options(opts, []) }
375
+ end
376
+ ok {sout} !~ rexp
377
+ #
378
+ opts = {list: true, all: true}
379
+ sout, serr = capture_sio do
380
+ @app.instance_eval { handle_global_options(opts, []) }
381
+ end
382
+ ok {sout} =~ rexp
565
383
  end
566
384
 
567
- spec "[!n60o0] reports error when action nor default action not specified." do
568
- @config.default_action = nil
569
- pr = proc { @app.run() }
570
- ok {pr}.raise?(Benry::CmdApp::CommandError,
571
- "testapp: Action name required (run `testapp -h` for details).")
385
+ spec "[!ooiaf] prints topic list if global option '-L <topic>' specified." do
386
+ Benry::CmdApp.define_alias('h0053', 'hello')
387
+ Benry::CmdApp.define_abbrev('g0053:', 'git:')
388
+ at_end {
389
+ Benry::CmdApp.undef_alias('h0053')
390
+ #Benry::CmdApp.undef_abbrev('g0053:')
391
+ Benry::CmdApp::REGISTRY.instance_eval { @abbrev_dict.delete('g0053:') }
392
+ }
393
+ #
394
+ chead = '\e\[1;34mCategories:\e\[0m'
395
+ data = [
396
+ [/\A\e\[1;34mActions:\e\[0m$/ , ["action" , "actions" ]],
397
+ [/\A\e\[1;34mAliases:\e\[0m$/ , ["alias" , "aliases" ]],
398
+ [/\A\e\[1;34mAbbreviations:\e\[0m$/ , ["abbrev" , "abbrevs" ]],
399
+ [/\A#{chead} \e\[2m\(depth=0\)\e\[0m$/, ["category" , "categories" ]],
400
+ [/\A#{chead} \e\[2m\(depth=1\)\e\[0m$/, ["category1", "categories1"]],
401
+ [/\A#{chead} \e\[2m\(depth=2\)\e\[0m$/, ["category2", "categories2"]],
402
+ [/\A#{chead} \e\[2m\(depth=3\)\e\[0m$/, ["category3", "categories3"]],
403
+ ]
404
+ data.each do |rexp, topics|
405
+ topics.each do |topic|
406
+ g_opts = {topic: topic}
407
+ sout, serr = capture_sio(tty: true) do
408
+ @app.instance_eval { handle_global_options(g_opts, []) }
409
+ end
410
+ ok {sout} =~ rexp
411
+ end
412
+ end
572
413
  end
573
414
 
574
- spec "[!hk6iu] unsets $cmdapp_config at end." do
575
- bkup = $cmdapp_config
576
- $cmdapp_config = nil
577
- begin
578
- sout, serr = capture_sio { @app.run("check-config") }
579
- ok {sout} == "$cmdapp_config.class=Benry::CmdApp::Config\n"
580
- ok {$cmdapp_config} == nil
581
- ensure
582
- $cmdapp_config = bkup
415
+ spec "[!ymifi] includes hidden actions into topic list if `-a, --all` specified." do
416
+ g_opts = {topic: "action", all: true}
417
+ sout, serr = capture_sio do
418
+ @app.instance_eval { handle_global_options(g_opts, []) }
583
419
  end
420
+ ok {sout} =~ /\AActions:$/
421
+ ok {sout} =~ /^ debuginfo : hidden action$/
584
422
  end
585
423
 
586
- spec "[!wv22u] calls teardown method at end of running action." do
587
- def @app.do_teardown(*args)
588
- @_args = args
424
+ spec "[!k31ry] returns `0` if help or version or actions printed." do
425
+ keys = [:help, :version, :list]
426
+ keys.each do |key|
427
+ ret = nil
428
+ opts = {key => true}
429
+ capture_sio do
430
+ ret = @app.instance_eval { handle_global_options(opts, []) }
431
+ end
432
+ ok {ret} == 0
589
433
  end
590
- ok {@app.instance_variable_get('@_args')} == nil
591
- sout, serr = capture_sio { @app.run("check-config") }
592
- ok {@app.instance_variable_get('@_args')} == [nil]
593
434
  end
594
435
 
595
- spec "[!dhba4] calls teardown method even if exception raised." do
596
- def @app.do_teardown(*args)
597
- @_args = args
598
- end
599
- ok {@app.instance_variable_get('@_args')} == nil
600
- pr = proc { @app.run("test-exception1") }
601
- ok {pr}.raise?(ZeroDivisionError) do |exc|
602
- ok {@app.instance_variable_get('@_args')} == [exc]
436
+ spec "[!9agnb] returns `nil` if do nothing." do
437
+ ret = nil
438
+ opts = {color: true, debug: true}
439
+ capture_sio do
440
+ ret = @app.instance_eval { handle_global_options(opts, []) }
603
441
  end
442
+ ok {ret} == nil
604
443
  end
605
444
 
606
445
  end
607
446
 
608
447
 
609
- topic '#help_message()' do
448
+ topic '#render_action_help()' do
610
449
 
611
- spec "[!owg9y] returns help message." do
612
- msg = @app.help_message()
613
- msg = uncolorize(msg)
614
- ok {msg}.start_with?(<<END)
615
- TestApp (1.0.0) -- test app
450
+ spec "[!c510c] returns action help message." do
451
+ s = @app.instance_eval { render_action_help("hello") }
452
+ ok {s} == <<"END"
453
+ \e[1mtestapp hello\e[0m --- greeting message
616
454
 
617
- Usage:
618
- $ testapp [<options>] [<action> [<arguments>...]]
455
+ \e[1;34mUsage:\e[0m
456
+ $ \e[1mtestapp hello\e[0m [<options>] [<name>]
619
457
 
620
- Options:
621
- -h, --help : print help message
622
- -V, --version : print version
623
- -a, --all : list all actions including private (hidden) ones
624
- -D, --debug : debug mode (set $DEBUG_MODE to true)
625
-
626
- Actions:
458
+ \e[1;34mOptions:\e[0m
459
+ -l, --lang=<lang> : language name (en/fr/it)
627
460
  END
628
461
  end
629
462
 
630
463
  end
631
464
 
632
465
 
633
- topic '#do_create_global_option_schema()' do
634
-
635
- spec "[!u3zdg] creates global option schema object according to config." do
636
- config = Benry::CmdApp::Config.new("test app", "1.0.0",
637
- option_all: true, option_quiet: true)
638
- app = Benry::CmdApp::Application.new(config)
639
- x = app.__send__(:do_create_global_option_schema, config)
640
- ok {x}.is_a?(Benry::CmdApp::AppOptionSchema)
641
- ok {x.find_long_option("all")} != nil
642
- ok {x.find_long_option("quiet")} != nil
643
- ok {x.find_long_option("verbose")} == nil
644
- ok {x.find_long_option("debug")} == nil
645
- end
466
+ topic '#render_application_help()' do
646
467
 
647
- end
468
+ spec "[!iyxxb] returns application help message." do
469
+ actual = @app.instance_eval { render_application_help() }
470
+ expected = <<"END"
471
+ \e[1mtestapp\e[0m \e[2m(1.2.3)\e[0m --- test app
648
472
 
473
+ \e[1;34mUsage:\e[0m
474
+ $ \e[1mtestapp\e[0m [<options>] <action> [<arguments>...]
649
475
 
650
- topic '#do_create_help_message_builder()' do
476
+ \e[1;34mOptions:\e[0m
477
+ -h, --help : print help message (of action if specified)
478
+ -V, --version : print version
479
+ -l, --list : list actions and aliases
480
+ -a, --all : list hidden actions/options, too
651
481
 
652
- spec "[!pk5da] creates help message builder object." do
653
- config = Benry::CmdApp::Config.new("test app", "1.0.0",
654
- option_all: true, option_quiet: true)
655
- app = Benry::CmdApp::Application.new(config)
656
- x = app.__send__(:do_create_help_message_builder, config, app.schema)
657
- ok {x}.is_a?(Benry::CmdApp::AppHelpBuilder)
482
+ \e[1;34mActions:\e[0m
483
+ END
484
+ ok {actual}.start_with?(expected)
485
+ ok {actual} =~ /^ hello : greeting message$/
486
+ ok {actual} =~ /^ git:stage : same as `git add -p`$/
658
487
  end
659
488
 
660
489
  end
661
490
 
662
491
 
663
- topic '#do_parse_global_options()' do
664
-
665
- spec "[!5br6t] parses only global options and not parse action options." do
666
- sout, serr = capture_sio { @app.run("test-globalopt", "--help") }
667
- ok {serr} == ""
668
- ok {sout} == "help=true\n"
669
- end
492
+ topic '#render_version()' do
670
493
 
671
- spec "[!kklah] raises InvalidOptionError if global option value is invalid." do
672
- pr = proc { @app.run("-hoge", "test-globalopt") }
673
- ok {pr}.raise?(Benry::CmdApp::InvalidOptionError, "-o: Unknown option.")
494
+ spec "[!bcp2g] returns version number string." do
495
+ s = @app.instance_eval { render_version() }
496
+ ok {s} == "1.2.3\n"
674
497
  end
675
498
 
676
499
  end
677
500
 
678
501
 
679
- topic '#do_toggle_global_switches()' do
502
+ topic '#render_item_list()' do
680
503
 
681
- before do
682
- @config = Benry::CmdApp::Config.new("test app", "1.0.0",
683
- option_verbose: true,
684
- option_quiet: true,
685
- option_debug: true,
686
- option_color: true,
687
- option_trace: true)
688
- @app = Benry::CmdApp::Application.new(@config)
504
+ class FakeAppHelpBuilder < Benry::CmdApp::ApplicationHelpBuilder
505
+ def section_availables(include=true, all: false); return nil; end
506
+ def section_candidates(prefix, all: false); return nil; end
507
+ def section_categories(depth=1, all: false); return nil; end
689
508
  end
690
509
 
691
- spec "[!j6u5x] sets $QUIET_MODE to false if '-v' or '--verbose' specified." do
692
- bkup = $QUIET_MODE
693
- begin
694
- ["-v", "--verbose"].each do |opt|
695
- $QUIET_MODE = true
696
- sout, serr = capture_sio { @app.run(opt, "test-debugopt") }
697
- ok {serr} == ""
698
- ok {$QUIET_MODE} == false
699
- end
700
- ensure
701
- $QUIET_MODE = bkup
510
+ def fake_app_help_builder(&b)
511
+ Benry::CmdApp.module_eval do
512
+ remove_const :APPLICATION_HELP_BUILDER_CLASS
513
+ const_set :APPLICATION_HELP_BUILDER_CLASS, FakeAppHelpBuilder
514
+ end
515
+ yield
516
+ ensure
517
+ Benry::CmdApp.module_eval do
518
+ remove_const :APPLICATION_HELP_BUILDER_CLASS
519
+ const_set :APPLICATION_HELP_BUILDER_CLASS, Benry::CmdApp::ApplicationHelpBuilder
702
520
  end
703
521
  end
704
522
 
705
- spec "[!p1l1i] sets $QUIET_MODE to true if '-q' or '--quiet' specified." do
706
- bkup = $QUIET_MODE
707
- begin
708
- ["-q", "--quiet"].each do |opt|
709
- $QUIET_MODE = nil
710
- sout, serr = capture_sio { @app.run(opt, "test-debugopt") }
711
- ok {serr} == ""
712
- ok {$QUIET_MODE} == true
713
- end
714
- ensure
715
- $QUIET_MODE = bkup
523
+ case_when "[!tftl5] when prefix is not specified..." do
524
+
525
+ spec "[!36vz6] returns action list string if any actions defined." do
526
+ s = @app.instance_eval { render_item_list(nil) }
527
+ ok {s} !~ /\A\e\[1;34mUsage:\e\[0m$/
528
+ ok {s} !~ /\A\e\[1;34mOptions:\e\[0m$/
529
+ ok {s} =~ /\A\e\[1;34mActions:\e\[0m$/
716
530
  end
717
- end
718
531
 
719
- spec "[!2zvf9] sets $COLOR_MODE to true/false according to '--color' option." do
720
- bkup = $COLOR_MODE
721
- begin
722
- [["--color", true], ["--color=on", true], ["--color=off", false]].each do |opt, val|
723
- $COLOR_MODE = !val
724
- sout, serr = capture_sio { @app.run(opt, "test-debugopt") }
725
- ok {serr} == ""
726
- ok {$COLOR_MODE} == val
532
+ spec "[!znuy4] raises CommandError if no actions defined." do
533
+ fake_app_help_builder() do
534
+ pr = proc { @app.instance_eval { render_item_list(nil) } }
535
+ ok {pr}.raise?(Benry::CmdApp::CommandError,
536
+ "No actions defined.")
727
537
  end
728
- ensure
729
- $COLOR_MODE = bkup
730
538
  end
539
+
731
540
  end
732
541
 
733
- spec "[!ywl1a] sets $DEBUG_MODE to true if '-D' or '--debug' specified." do
734
- bkup = $DEBUG_MODE
735
- begin
736
- ["-D", "--debug"].each do |opt|
737
- $DEBUG_MODE = false
738
- sout, serr = capture_sio { @app.run(opt, "test-debugopt") }
739
- ok {serr} == ""
740
- ok {sout} == "$DEBUG_MODE=true\n"
741
- ok {$DEBUG_MODE} == true
742
- end
743
- ensure
744
- $DEBUG_MODE = bkup
542
+ case_when "[!jcq4z] when separator is specified..." do
543
+
544
+ spec "[!w1j1e] returns top prefix list if ':' specified." do
545
+ s = @app.instance_eval { render_item_list(":") }
546
+ ok {s} !~ /\A\e\[1;34mUsage:\e\[0m$/
547
+ ok {s} !~ /\A\e\[1;34mOptions:\e\[0m$/
548
+ ok {s} !~ /\A\e\[1;34mActions:\e\[0m$/
549
+ ok {s} =~ /\A\e\[1;34mCategories:\e\[0m \e\[2m\(depth=\d+\)\e\[0m$/
550
+ #
551
+ ok {s} !~ /^ hello/
552
+ #ok {s} =~ /^ foo: \(\d+\)$/
553
+ ok {s} =~ /^ git: \(\d+\)$/
554
+ ok {s} =~ /^ giit: \(\d+\)/
555
+ ok {s} !~ /^ hello/
556
+ #
557
+ ok {s} =~ /^ giit: \(\d\d\) : gitt commands$/
558
+ ok {s} !~ /^ giit:branch: \(\d+\)$/
559
+ ok {s} !~ /^ giit:commit: \(\d+\)$/
560
+ ok {s} !~ /^ giit:repo: \(\d+\)$/
561
+ ok {s} !~ /^ giit:staging: \(\d+\)$/
745
562
  end
746
- end
747
563
 
748
- spec "[!8trmz] sets $TRACE_MODE to true if '-T' or '--trace' specified." do
749
- bkup = $TRACE_MODE
750
- begin
751
- ["-T", "--trace"].each do |opt|
752
- $TRACE_MODE = false
753
- sout, serr = capture_sio { @app.run(opt, "test-debugopt") }
754
- ok {serr} == ""
755
- ok {$TRACE_MODE} == true
564
+ spec "[!bgput] returns two depth prefix list if '::' specified." do
565
+ s = @app.instance_eval { render_item_list("::") }
566
+ ok {s} !~ /\A\e\[1;34mUsage:\e\[0m$/
567
+ ok {s} !~ /\A\e\[1;34mOptions:\e\[0m$/
568
+ ok {s} !~ /\A\e\[1;34mActions:\e\[0m$/
569
+ ok {s} =~ /\A\e\[1;34mCategories:\e\[0m \e\[2m\(depth=\d+\)\e\[0m$/
570
+ #
571
+ #ok {s} =~ /^ foo: \(\d+\)$/
572
+ ok {s} =~ /^ git: \(\d+\)$/
573
+ ok {s} =~ /^ giit: \(\d\) : gitt commands$/
574
+ ok {s} !~ /^ hello/
575
+ #
576
+ ok {s} =~ /^ giit:branch: \(\d+\)$/
577
+ ok {s} =~ /^ giit:commit: \(\d+\)$/
578
+ ok {s} =~ /^ giit:repo: \(\d+\)$/
579
+ ok {s} =~ /^ giit:staging: \(\d+\)$/
580
+ end
581
+
582
+ spec "[!tiihg] raises CommandError if no actions found having prefix." do
583
+ fake_app_help_builder() do
584
+ pr = proc { @app.instance_eval { render_item_list(":") } }
585
+ ok {pr}.raise?(Benry::CmdApp::CommandError,
586
+ "Prefix of actions not found.")
756
587
  end
757
- ensure
758
- $TRACE_MODE = bkup
759
588
  end
589
+
760
590
  end
761
591
 
762
- end
592
+ case_when "[!xut9o] when prefix is specified..." do
763
593
 
594
+ spec "[!z4dqn] filters action list by prefix if specified." do
595
+ s = @app.instance_eval { render_item_list("git:") }
596
+ ok {s} == <<"END"
597
+ \e[1;34mActions:\e[0m
598
+ git:stage : same as `git add -p`
599
+ git:staged : same as `git diff --cached`
600
+ git:unstage : same as `git reset HEAD`
601
+ END
602
+ end
764
603
 
765
- topic '#do_handle_global_options()' do
604
+ spec "[!1834c] raises CommandError if no actions found with names starting with that prefix." do
605
+ fake_app_help_builder() do
606
+ pr = proc { @app.instance_eval { render_item_list("git:") } }
607
+ ok {pr}.raise?(Benry::CmdApp::CommandError,
608
+ "No actions found with names starting with 'git:'.")
609
+ end
610
+ end
766
611
 
767
- def new_app()
768
- kws = {
769
- app_name: "TestApp",
770
- app_command: "testapp",
771
- option_all: true,
772
- option_verbose: true,
773
- option_quiet: true,
774
- option_color: true,
775
- option_debug: true,
776
- default_action: nil,
777
- }
778
- config = Benry::CmdApp::Config.new("test app", "1.0.0", **kws)
779
- return Benry::CmdApp::Application.new(config)
780
612
  end
781
613
 
782
- spec "[!xvj6s] prints help message if '-h' or '--help' specified." do
783
- expected = <<"END"
784
- TestApp (1.0.0) -- test app
785
-
786
- Usage:
787
- $ testapp [<options>] [<action> [<arguments>...]]
614
+ case_else "[!xjdrm] else..." do
788
615
 
789
- Options:
790
- -h, --help : print help message
791
- -V, --version : print version
792
- -a, --all : list all actions including private (hidden) ones
793
- -v, --verbose : verbose mode
794
- -q, --quiet : quiet mode
795
- --color[=<on|off>] : enable/disable color
796
- -D, --debug : debug mode (set $DEBUG_MODE to true)
797
-
798
- Actions:
799
- END
800
- app = new_app()
801
- ["-h", "--help"].each do |opt|
802
- sout, serr = capture_sio { app.run(opt) }
803
- ok {serr} == ""
804
- ok {sout}.start_with?(expected)
616
+ spec "[!9r4w9] raises ArgumentError." do
617
+ pr = proc { @app.instance_eval { render_item_list("git") } }
618
+ ok {pr}.raise?(ArgumentError,
619
+ "\"git\": Invalid value as a prefix.")
805
620
  end
621
+
806
622
  end
807
623
 
808
- spec "[!lpoz7] prints help message of action if action name specified with help option." do
809
- expected = <<"END"
810
- testapp sayhello -- print greeting message
624
+ end
811
625
 
812
- Usage:
813
- $ testapp sayhello [<options>] [<user>]
814
626
 
815
- Options:
816
- -l, --lang=<en|fr|it> : language
817
- END
818
- app = new_app()
819
- ["-h", "--help"].each do |opt|
820
- sout, serr = capture_sio { app.run(opt, "sayhello") }
821
- ok {serr} == ""
822
- ok {sout} == expected
823
- end
824
- end
627
+ topic '#render_topic_list()' do
825
628
 
826
- spec "[!fslsy] prints version if '-V' or '--version' specified." do
827
- app = new_app()
828
- ["-V", "--version"].each do |opt|
829
- sout, serr = capture_sio { app.run(opt, "xxx") }
830
- ok {serr} == ""
831
- ok {sout} == "1.0.0\n"
832
- end
629
+ spec "[!uzmml] renders topic list." do
630
+ x = @app.__send__(:render_topic_list, "action")
631
+ ok {x} =~ /\A\e\[1;34mActions:\e\[0m$/
632
+ ok {x} =~ /^ hello : greeting message$/
633
+ #
634
+ Benry::CmdApp.define_alias("chiaou", ["hello", "-l", "it"])
635
+ x = @app.__send__(:render_topic_list, "alias")
636
+ ok {x} =~ /\A\e\[1;34mAliases:\e\[0m$/
637
+ ok {x} =~ /^ chiaou : alias for 'hello -l it'$/
638
+ #
639
+ x = @app.__send__(:render_topic_list, "category")
640
+ ok {x} =~ /\A\e\[1;34mCategories:\e\[0m \e\[2m\(depth=0\)\e\[0m$/
641
+ ok {x} =~ /^ git: \(3\)$/
642
+ ok {x} =~ /^ giit: \(\d+\) +: gitt commands$/
643
+ #
644
+ Benry::CmdApp.define_abbrev("g31:", "git:")
645
+ x = @app.__send__(:render_topic_list, "abbrev")
646
+ ok {x} =~ /\A\e\[1;34mAbbreviations:\e\[0m$/
647
+ ok {x} =~ /^ g31: +=> git:$/
648
+ end
649
+
650
+ spec "[!vrzu0] topic 'category1' or 'categories2' is acceptable." do
651
+ x = @app.__send__(:render_topic_list, "category1")
652
+ ok {x} =~ /\A\e\[1;34mCategories:\e\[0m \e\[2m\(depth=1\)\e\[0m$/
653
+ ok {x} =~ /^ git: \(3\)$/
654
+ ok {x} =~ /^ giit: \(\d+\) +: gitt commands$/
655
+ ok {x} !~ /^ giit:branch:/
656
+ ok {x} !~ /^ giit:repo:/
657
+ #
658
+ x = @app.__send__(:render_topic_list, "category2")
659
+ ok {x} =~ /\A\e\[1;34mCategories:\e\[0m \e\[2m\(depth=2\)\e\[0m$/
660
+ ok {x} =~ /^ git: \(3\)$/
661
+ ok {x} =~ /^ giit: \(0\) +: gitt commands$/
662
+ ok {x} =~ /^ giit:branch: \(2\)$/
663
+ ok {x} =~ /^ giit:repo: \(7\)$/
664
+ ok {x} !~ /^ giit:repo:config:/
665
+ ok {x} !~ /^ giit:repo:remote:/
666
+ #
667
+ x = @app.__send__(:render_topic_list, "categories3")
668
+ ok {x} =~ /\A\e\[1;34mCategories:\e\[0m \e\[2m\(depth=3\)\e\[0m$/
669
+ ok {x} =~ /^ git: \(3\)$/
670
+ ok {x} =~ /^ giit: \(0\) +: gitt commands$/
671
+ ok {x} =~ /^ giit:branch: \(2\)$/
672
+ ok {x} =~ /^ giit:repo: \(2\)$/
673
+ ok {x} =~ /^ giit:repo:config: \(3\)$/
674
+ ok {x} =~ /^ giit:repo:remote: \(2\)$/
675
+ end
676
+
677
+ spec "[!xyn5g] global option '-L metadata' renders registry data in YAML format." do
678
+ config = Benry::CmdApp::Config.new("test app", "0.0.0")
679
+ app = Benry::CmdApp::Application.new(config)
680
+ sout, serr = capture_sio { app.run("-L", "metadata") }
681
+ ok {serr} == ""
682
+ require 'yaml'
683
+ ydoc = YAML.load(sout)
684
+ ok {ydoc["actions"]}.is_a?(Array)
685
+ ok {ydoc["aliases"]}.is_a?(Array)
686
+ ok {ydoc["categories"]}.is_a?(Array)
687
+ ok {ydoc["abbreviations"]}.is_a?(Array)
833
688
  end
834
689
 
835
690
  end
836
691
 
837
692
 
838
- topic '#do_callback()' do
693
+ topic '#handle_blank_action()' do
839
694
 
840
- def new_app(&block)
841
- @config = Benry::CmdApp::Config.new("test app", "1.0.0")
842
- return Benry::CmdApp::Application.new(@config, &block)
843
- end
844
-
845
- spec "[!xwo0v] calls callback if provided." do
846
- called = nil
847
- app = new_app do |args, global_opts, config|
848
- called = [args.dup, global_opts, config]
849
- end
850
- ok {called} == nil
851
- without_tty { app.run("sayhello") }
852
- ok {called} != nil
853
- ok {called[0]} == ["sayhello"]
854
- ok {called[1]} == {}
855
- ok {called[2]} == @config
856
- end
857
-
858
- spec "[!lljs1] calls callback only once." do
859
- n = 0
860
- app = new_app do |args, global_opts, config|
861
- n += 1
695
+ spec "[!seba7] prints action list and returns `0`." do
696
+ ret = nil
697
+ sout, serr = capture_sio do
698
+ ret = @app.instance_eval { handle_blank_action() }
862
699
  end
863
- ok {n} == 0
864
- without_tty { app.run("sayhello") }
865
- ok {n} == 1
866
- without_tty { app.run("sayhello") }
867
- ok {n} == 1
868
- without_tty { app.run("sayhello") }
869
- ok {n} == 1
700
+ ok {sout} !~ /\AUsage:$/
701
+ ok {sout} !~ /\AOptions:$/
702
+ ok {sout} =~ /\AActions:$/
703
+ ok {ret} == 0
870
704
  end
871
705
 
872
706
  end
873
707
 
874
708
 
875
- topic '#do_find_action()' do
709
+ topic '#handle_prefix()' do
876
710
 
877
- spec "[!bm8np] returns action metadata." do
878
- x = @app.__send__(:do_find_action, ["sayhello"], {})
879
- ok {x}.is_a?(Benry::CmdApp::ActionMetadata)
880
- ok {x.name} == "sayhello"
711
+ spec "[!8w301] prints action list starting with prefix and returns `0`." do
712
+ ret = nil
713
+ sout, serr = capture_sio do
714
+ ret = @app.instance_eval { handle_prefix("git:") }
715
+ end
716
+ ok {ret} == 0
717
+ ok {sout} == <<"END"
718
+ Actions:
719
+ git:stage : same as `git add -p`
720
+ git:staged : same as `git diff --cached`
721
+ git:unstage : same as `git reset HEAD`
722
+ END
881
723
  end
882
724
 
883
- spec "[!vl0zr] error when action not found." do
884
- pr = proc { @app.__send__(:do_find_action, ["hiyo"], {}) }
885
- ok {pr}.raise?(Benry::CmdApp::CommandError,
886
- "hiyo: Unknown action.")
887
- end
725
+ end
888
726
 
889
- spec "[!gucj7] if no action specified, finds default action instead." do
890
- @app.config.default_action = "sayhello"
891
- x = @app.__send__(:do_find_action, [], {})
892
- ok {x}.is_a?(Benry::CmdApp::ActionMetadata)
893
- ok {x.name} == "sayhello"
894
- end
895
727
 
896
- spec "[!388rs] error when default action not found." do
897
- @app.config.default_action = "hiyo"
898
- pr = proc { @app.__send__(:do_find_action, [], {}) }
899
- ok {pr}.raise?(Benry::CmdApp::CommandError,
900
- "hiyo: Unknown default action.")
901
- end
728
+ topic '#start_action()' do
902
729
 
903
- spec "[!drmls] returns nil if no action specified but 'config.default_help' is set." do
904
- @app.config.default_action = nil
905
- @app.config.default_help = true
906
- x = @app.__send__(:do_find_action, [], {})
907
- ok {x} == nil
730
+ spec "[!vbymd] runs action with args and returns `0`." do
731
+ ret = nil
732
+ sout, serr = capture_sio do
733
+ ret = @app.instance_eval { start_action("hello", ["-l", "it", "Alice"]) }
734
+ end
735
+ ok {ret} == 0
736
+ ok {sout} == "Chao, Alice!\n"
908
737
  end
909
738
 
910
- spec "[!hs589] error when action nor default action not specified." do
911
- @app.config.default_action = nil
912
- @app.config.default_help = false
913
- pr = proc { @app.__send__(:do_find_action, [], {}) }
914
- ok {pr}.raise?(Benry::CmdApp::CommandError,
915
- "testapp: Action name required (run `testapp -h` for details).")
739
+ spec "[!6htva] supports abbreviation of prefix." do
740
+ Benry::CmdApp.define_abbrev("g1:", "git:")
741
+ sout, serr = capture_sio do
742
+ @app.instance_eval do
743
+ start_action("g1:stage", ["."])
744
+ start_action("g1:unstage", [])
745
+ end
746
+ end
747
+ ok {sout} == <<'END'
748
+ git add -p .
749
+ git reset HEAD
750
+ END
916
751
  end
917
752
 
918
753
  end
919
754
 
920
755
 
921
- topic '#do_run_action()' do
756
+ topic '#new_context()' do
922
757
 
923
- spec "[!62gv9] parses action options even if specified after args." do
924
- sout, serr = capture_sio { @app.run("sayhello", "Alice", "-l", "it") }
925
- ok {serr} == ""
926
- ok {sout} == "Ciao, Alice!\n"
758
+ spec "[!9ddcl] creates new context object with config object." do
759
+ x = @app.instance_eval { new_context() }
760
+ ok {x}.is_a?(Benry::CmdApp::ApplicationContext)
761
+ ok {x.instance_variable_get(:@config)} == @config
927
762
  end
928
763
 
929
- spec "[!6mlol] error if action requries argument but nothing specified." do
930
- pr = proc { @app.run("test-arity1") }
931
- ok {pr}.raise?(Benry::CmdApp::CommandError,
932
- "test-arity1: Argument required.")
933
- end
764
+ end
934
765
 
935
- spec "[!72jla] error if action requires N args but specified less than N args." do
936
- pr = proc { @app.run("test-arity1", "foo") }
937
- ok {pr}.raise?(Benry::CmdApp::CommandError,
938
- "test-arity1: Too less arguments (at least 2).")
939
- end
940
766
 
941
- spec "[!zawxe] error if action requires N args but specified over than N args." do
942
- pr = proc { @app.run("test-arity1", "foo", "bar", "baz", "boo") }
943
- ok {pr}.raise?(Benry::CmdApp::CommandError,
944
- "test-arity1: Too much arguments (at most 3).")
945
- end
767
+ topic '#print_str()' do
946
768
 
947
- spec "[!y97o3] action can take any much args if action has variable arg." do
948
- pr = proc {
949
- capture_sio { @app.run("test-arity2", "foo", "bar", "baz", "boo") }
950
- }
951
- ok {pr}.NOT.raise?(Exception)
769
+ spec "[!yiabh] do nothing if str is nil." do
770
+ sout, serr = capture_sio do
771
+ @app.instance_eval { print_str nil }
772
+ end
773
+ ok {sout} == ""
774
+ ok {serr} == ""
952
775
  end
953
776
 
954
- spec "[!cf45e] runs action with arguments and options." do
955
- sout, serr = capture_sio { @app.run("sayhello", "-l", "it", "Bob") }
956
- ok {serr} == ""
957
- ok {sout} == "Ciao, Bob!\n"
777
+ spec "[!6kyv9] prints string as is if color mode is enabled." do
778
+ bkup = $COLOR_MODE; at_end { $COLOR_MODE = bkup }
779
+ $COLOR_MODE = true
780
+ sout, serr = capture_sio do
781
+ @app.instance_eval { print_str("\e[1mHello\e[0m") }
782
+ end
783
+ ok {sout} == "\e[1mHello\e[0m"
958
784
  end
959
785
 
960
- spec "[!tsal4] detects looped action." do
961
- pr = proc { @app.run("test-loop1") }
962
- ok {pr}.raise?(Benry::CmdApp::LoopedActionError,
963
- "test-loop1: Action loop detected.")
786
+ spec "[!lxhvq] deletes escape characters from string and prints it if color mode is disabled." do
787
+ bkup = $COLOR_MODE; at_end { $COLOR_MODE = bkup }
788
+ $COLOR_MODE = false
789
+ sout, serr = capture_sio do
790
+ @app.instance_eval { print_str("\e[1mHello\e[0m") }
791
+ end
792
+ ok {sout} == "Hello"
964
793
  end
965
794
 
966
795
  end
967
796
 
968
797
 
969
- topic '#do_print_help_message()' do
798
+ topic '#print_error()' do
970
799
 
971
- spec "[!eabis] prints help message of action if action name provided." do
972
- sout, serr = capture_sio { @app.run("-h", "sayhello") }
973
- ok {serr} == ""
974
- ok {sout} == <<'END'
975
- testapp sayhello -- print greeting message
800
+ fixture(:err) {
801
+ begin
802
+ 1/0
803
+ rescue ZeroDivisionError => err
804
+ end
805
+ err
806
+ }
976
807
 
977
- Usage:
978
- $ testapp sayhello [<options>] [<user>]
808
+ spec "[!sdbj8] prints exception as error message." do |err|
809
+ sout, serr = capture_sio(tty: true) do
810
+ @app.instance_eval { print_error(err) }
811
+ end
812
+ ok {sout} == ""
813
+ ok {serr} == "\e[31m[ERROR]\e[0m divided by 0\n"
814
+ end
979
815
 
980
- Options:
981
- -l, --lang=<en|fr|it> : language
982
- END
816
+ spec "[!6z0mu] prints colored error message if stderr is a tty." do |err|
817
+ sout, serr = capture_sio(tty: true) do
818
+ @app.instance_eval { print_error(err) }
819
+ end
820
+ ok {sout} == ""
821
+ ok {serr} == "\e[31m[ERROR]\e[0m divided by 0\n"
983
822
  end
984
823
 
985
- spec "[!cgxkb] error if action for help option not found." do
986
- ["-h", "--help"].each do |opt|
987
- pr = proc { @app.run(opt, "xhello") }
988
- ok {pr}.raise?(Benry::CmdApp::CommandError,
989
- "xhello: Action not found.")
824
+ spec "[!k1s3o] prints non-colored error message if stderr is not a tty." do |err|
825
+ sout, serr = capture_sio(tty: false) do
826
+ @app.instance_eval { print_error(err) }
990
827
  end
828
+ ok {sout} == ""
829
+ ok {serr} == "[ERROR] divided by 0\n"
991
830
  end
992
831
 
993
- spec "[!nv0x3] prints help message of command if action name not provided." do
994
- sout, serr = capture_sio { @app.run("-h") }
995
- ok {serr} == ""
996
- ok {sout}.start_with?(<<'END')
997
- TestApp (1.0.0) -- test app
832
+ end
998
833
 
999
- Usage:
1000
- $ testapp [<options>] [<action> [<arguments>...]]
1001
834
 
1002
- Options:
1003
- -h, --help : print help message
1004
- -V, --version : print version
1005
- -a, --all : list all actions including private (hidden) ones
1006
- -D, --debug : debug mode (set $DEBUG_MODE to true)
835
+ topic '#print_backtrace()' do
1007
836
 
1008
- Actions:
1009
- END
1010
- end
837
+ fixture(:err) {
838
+ begin
839
+ 1/0
840
+ rescue ZeroDivisionError => err
841
+ end
842
+ err
843
+ }
1011
844
 
1012
- spec "[!4qs7y] shows private (hidden) actions/options if '--all' option specified." do
1013
- class HiddenTest < Benry::CmdApp::ActionScope
1014
- private
1015
- @action.("hidden test")
1016
- @option.(:trace, "-T", "enable tracing", hidden: true)
1017
- def hidden1(trace: false)
1018
- end
845
+ spec "[!i010e] skips backtrace in `benry/cmdapp.rb`." do |err|
846
+ sout, serr = capture_sio() do
847
+ @app.instance_eval { print_backtrace(err) }
1019
848
  end
1020
- #
1021
- ok {_run_app("-h", "--all")} =~ /^ hidden1 +: hidden test$/
1022
- ok {_run_app("--help", "-a")} =~ /^ hidden1 +: hidden test$/
1023
- ok {_run_app("-h")} !~ /^ hidden1 +: hidden test$/
1024
- #
1025
- ok {_run_app("-ha", "hidden1")} =~ /^ -T +: enable tracing$/
1026
- ok {_run_app("-h", "--all", "hidden1")} =~ /^ -T +: enable tracing$/
1027
- ok {_run_app("--help", "hidden1")} !~ /^ -T +: enable tracing$/
849
+ ok {sout} == ""
850
+ ok {serr} !~ /benry\/cmdapp\.rb/
851
+ ok {serr} =~ /app_test\.rb/
1028
852
  end
1029
853
 
1030
- spec "[!l4d6n] `all` flag should be true or false, not nil." do
1031
- config = Benry::CmdApp::Config.new("test app", "1.0.0", option_all: true)
1032
- app = Benry::CmdApp::Application.new(config)
1033
- def app.help_message(all)
1034
- @_all_ = all
1035
- super
854
+ spec "[!ilaxg] skips backtrace if `#skip_backtrace?()` returns truthy value." do |err|
855
+ sout1, serr1 = capture_sio() do
856
+ @app.instance_eval { print_backtrace(err) }
1036
857
  end
1037
- msg = without_tty { app.run("-h") }
1038
- ok {app.instance_variable_get('@_all_')} != nil
1039
- ok {app.instance_variable_get('@_all_')} == false
858
+ ok {serr1} =~ /app_test\.rb/
1040
859
  #
1041
- msg = without_tty { app.run("-ha") }
1042
- ok {app.instance_variable_get('@_all_')} != nil
1043
- ok {app.instance_variable_get('@_all_')} == true
860
+ r = recorder()
861
+ r.fake_method(@app, :'skip_backtrace?' => true)
862
+ sout2, serr2 = capture_sio() do
863
+ @app.instance_eval { print_backtrace(err) }
864
+ end
865
+ ok {serr2} == ""
1044
866
  end
1045
867
 
1046
- spec "[!efaws] prints colorized help message when stdout is a tty." do
1047
- sout, serr = capture_sio(tty: true) { @app.run("-h") }
1048
- ok {serr} == ""
1049
- ok {sout}.include?(<<"END")
1050
- \e[34mUsage:\e[0m
1051
- $ \e[1mtestapp\e[0m [<options>] [<action> [<arguments>...]]
1052
- END
1053
- ok {sout}.include?(<<"END")
1054
- \e[34mOptions:\e[0m
1055
- \e[1m-h, --help \e[0m : print help message
1056
- \e[1m-V, --version \e[0m : print version
1057
- END
1058
- ok {sout}.include?(<<"END")
1059
- \e[34mActions:\e[0m
1060
- END
868
+ spec "[!5sa5k] prints filename and line number in slant format if stdout is a tty." do |err|
869
+ sout, serr = capture_sio(tty: true) do
870
+ @app.instance_eval { print_backtrace(err) }
871
+ end
872
+ ok {sout} == ""
873
+ ok {serr} =~ /\A \e\[3mFrom test\/app_test\.rb:\d+:in `\/'\e\[0m/
1061
874
  end
1062
875
 
1063
- spec "[!9vdy1] prints non-colorized help message when stdout is not a tty." do
1064
- sout, serr = capture_sio(tty: false) { @app.run("-h") }
1065
- ok {serr} == ""
1066
- ok {sout}.include?(<<"END")
1067
- Usage:
1068
- $ testapp [<options>] [<action> [<arguments>...]]
1069
- END
1070
- ok {sout}.include?(<<"END")
1071
- Options:
1072
- -h, --help : print help message
1073
- -V, --version : print version
1074
- END
1075
- ok {sout}.include?(<<"END")
1076
- Actions:
876
+ spec "[!2sg9r] not to try to read file content if file not found." do |err|
877
+ newbt = [
878
+ "#{__FILE__}:#{__LINE__}:in `foo`",
879
+ "-e:#{__LINE__}:in `bar`",
880
+ "#{__FILE__}:#{__LINE__}:in `baz`",
881
+ ]
882
+ n = __LINE__ - 4 # base lineno
883
+ err.set_backtrace(newbt)
884
+ sout, serr = capture_sio(tty: true) do
885
+ @app.instance_eval { print_backtrace(err) }
886
+ end
887
+ ok {sout} == ""
888
+ ok {serr} == <<"END"
889
+ \e[3mFrom test/app_test.rb:#{n+0}:in `foo`\e[0m
890
+ "\#{__FILE__}:\#{__LINE__}:in `foo`",
891
+ \e[3mFrom -e:#{n+1}:in `bar`\e[0m
892
+ \e[3mFrom test/app_test.rb:#{n+2}:in `baz`\e[0m
893
+ "\#{__FILE__}:\#{__LINE__}:in `baz`",
1077
894
  END
1078
895
  end
1079
896
 
1080
- spec "[!gsdcu] prints colorized help message when '--color[=on]' specified." do
1081
- @config.option_color = true
1082
- app = Benry::CmdApp::Application.new(@config)
1083
- bkup = $COLOR_MODE
1084
- begin
1085
- sout, serr = capture_sio(tty: false) { app.run("-h", "--color") }
1086
- ok {serr} == ""
1087
- ok {sout}.include?(<<"END")
1088
- \e[34mUsage:\e[0m
1089
- $ \e[1mtestapp\e[0m [<options>] [<action> [<arguments>...]]
1090
- END
1091
- ok {sout}.include?(<<"END")
1092
- \e[34mOptions:\e[0m
1093
- \e[1m-h, --help \e[0m : print help message
1094
- \e[1m-V, --version \e[0m : print version
1095
- END
1096
- ok {sout}.include?(<<"END")
1097
- \e[34mActions:\e[0m
1098
- END
1099
- ensure
1100
- $COLOR_MODE = bkup
897
+ spec "[!ihizf] prints lines of each backtrace entry." do |err|
898
+ sout, serr = capture_sio(tty: true) do
899
+ @app.instance_eval { print_backtrace(err) }
1101
900
  end
901
+ ok {sout} == ""
902
+ ok {serr} =~ /^ 1\/0\n/
1102
903
  end
1103
904
 
1104
- spec "[!be8y2] prints non-colorized help message when '--color=off' specified." do
1105
- @config.option_color = true
1106
- app = Benry::CmdApp::Application.new(@config)
1107
- bkup = $COLOR_MODE
1108
- begin
1109
- sout, serr = capture_sio(tty: true) { app.run("-h", "--color=off") }
1110
- ok {serr} == ""
1111
- ok {sout}.include?(<<"END")
1112
- Usage:
1113
- $ testapp [<options>] [<action> [<arguments>...]]
1114
- END
1115
- ok {sout}.include?(<<"END")
1116
- Options:
1117
- -h, --help : print help message
1118
- -V, --version : print version
1119
- END
1120
- ok {sout}.include?(<<"END")
1121
- Actions:
1122
- END
1123
- ensure
1124
- $COLOR_MODE = bkup
905
+ spec "[!8wzxg] prints backtrace of exception." do |err|
906
+ sout, serr = capture_sio() do
907
+ @app.instance_eval { print_backtrace(err) }
1125
908
  end
909
+ ok {sout} == ""
910
+ ok {serr} =~ /\A From test\/app_test\.rb:\d+:in `\/'\n 1\/0\n/
1126
911
  end
1127
912
 
1128
913
  end
1129
914
 
1130
915
 
1131
- topic '#do_validate_actions()' do
916
+ topic '#skip_backtrace?()' do
1132
917
 
1133
- spec "[!6xhvt] reports warning at end of help message." do
1134
- class ValidateActionTest1 < Benry::CmdApp::ActionScope
1135
- prefix "validate1", alias_of: :test
1136
- @action.("test")
1137
- def test1(); end
918
+ spec "[!r2fmv] ignores backtraces if matched to 'config.backtrace_ignore_rexp'." do
919
+ @config.backtrace_ignore_rexp = /\/foobar\.rb/
920
+ bt1 = "/home/yourname/foobar.rb:123:"
921
+ bt2 = "/home/yourname/blabla.rb:123:"
922
+ _ = self
923
+ @app.instance_eval do
924
+ _.ok {skip_backtrace?(bt1)} == 14
925
+ _.ok {skip_backtrace?(bt2)} == nil
1138
926
  end
1139
- @app.config.default_help = true
1140
- begin
1141
- [["-h"], []].each do |args|
1142
- sout, serr = capture_sio { @app.run(*args) }
1143
- ok {serr} == <<'END'
927
+ end
1144
928
 
1145
- ** [warning] in 'ValidateActionTest1' class, `alias_of: :test` specified but corresponding action not exist.
1146
- END
1147
- end
1148
- ensure
1149
- ValidateActionTest1.class_eval { @__aliasof__ = nil }
929
+ spec "[!c6f11] not ignore backtraces if 'config.backtrace_ignore_rexp' is not set." do
930
+ @config.backtrace_ignore_rexp = nil
931
+ bt1 = "/home/yourname/foobar.rb:123:"
932
+ bt2 = "/home/yourname/blabla.rb:123:"
933
+ _ = self
934
+ @app.instance_eval do
935
+ _.ok {skip_backtrace?(bt1)} == false
936
+ _.ok {skip_backtrace?(bt2)} == false
1150
937
  end
1151
938
  end
1152
939
 
1153
- spec "[!iy241] reports warning if `alias_of:` specified in action class but corresponding action not exist." do
1154
- class ValidateActionTest2 < Benry::CmdApp::ActionScope
1155
- prefix "validate2", alias_of: :test2
1156
- @action.("test")
1157
- def test(); end
1158
- end
1159
- begin
1160
- sout, serr = capture_sio { @app.__send__(:do_validate_actions, [], {}) }
1161
- ok {serr} == <<'END'
940
+ end
1162
941
 
1163
- ** [warning] in 'ValidateActionTest2' class, `alias_of: :test2` specified but corresponding action not exist.
1164
- END
1165
- ensure
1166
- ValidateActionTest2.class_eval { @__aliasof__ = nil }
1167
- end
1168
- end
1169
942
 
1170
- spec "[!h7lon] reports warning if `action:` specified in action class but corresponding action not exist." do
1171
- class ValidateActionTest3 < Benry::CmdApp::ActionScope
1172
- prefix "validate3", action: :test3
1173
- @action.("test")
1174
- def test(); end
1175
- end
1176
- begin
1177
- sout, serr = capture_sio { @app.__send__(:do_validate_actions, [], {}) }
1178
- ok {serr} == <<'END'
943
+ topic '#read_file_as_lines()' do
1179
944
 
1180
- ** [warning] in 'ValidateActionTest3' class, `action: :test3` specified but corresponding action not exist.
1181
- END
1182
- ensure
1183
- ValidateActionTest3.class_eval { @__default__ = nil }
1184
- end
945
+ spec "[!e9c74] reads file content as an array of line." do
946
+ lines = @app.instance_eval { read_file_as_lines(__FILE__) }
947
+ ok {lines}.is_a?(Array)
948
+ ok {lines[__LINE__ - 2]} == " ok {lines}.is_a?(Array)\n"
1185
949
  end
1186
950
 
1187
951
  end
1188
952
 
1189
953
 
1190
- topic '#do_print_candidates()' do
954
+ topic '#should_rescue?()' do
1191
955
 
1192
- spec "[!0e8vt] prints candidate action names including prefix name without tailing ':'." do
1193
- class CandidateTest2 < Benry::CmdApp::ActionScope
1194
- prefix "candi:date2", action: :eee
1195
- @action.("test1")
1196
- def ddd(); end
1197
- @action.("test2")
1198
- def ccc(); end
1199
- @action.("test3")
1200
- def eee(); end
1201
- end
1202
- sout, serr = capture_sio do
1203
- @app.__send__(:do_print_candidates, ["candi:date2:"], {})
1204
- end
1205
- ok {serr} == ""
1206
- ok {sout} == <<"END"
1207
- Actions:
1208
- candi:date2 : test3
1209
- candi:date2:ccc : test2
1210
- candi:date2:ddd : test1
1211
- END
956
+ spec "[!8lwyn] returns trueif exception is a BaseError." do
957
+ x = @app.__send__(:should_rescue?, Benry::CmdApp::DefinitionError.new)
958
+ ok {x} == true
959
+ x = @app.__send__(:should_rescue?, RuntimeError.new)
960
+ ok {x} == false
1212
961
  end
1213
962
 
1214
- spec "[!85i5m] candidate actions should include alias names." do
1215
- class CandidateTest3 < Benry::CmdApp::ActionScope
1216
- prefix "candi:date3", action: :ggg
1217
- @action.("test1")
1218
- def hhh(); end
1219
- @action.("test2")
1220
- def fff(); end
1221
- @action.("test3")
1222
- def ggg(); end
1223
- end
1224
- Benry::CmdApp.action_alias("pupu", "candi:date3:fff")
1225
- Benry::CmdApp.action_alias("popo", "candi:date3:fff")
1226
- Benry::CmdApp.action_alias("candi:date3:xxx", "candi:date3:hhh")
1227
- sout, serr = capture_sio do
1228
- @app.__send__(:do_print_candidates, ["candi:date3:"], {})
1229
- end
1230
- ok {serr} == ""
1231
- ok {sout} == <<"END"
1232
- Actions:
1233
- candi:date3 : test3
1234
- candi:date3:fff : test2
1235
- (alias: pupu, popo)
1236
- candi:date3:hhh : test1
1237
- (alias: candi:date3:xxx)
1238
- candi:date3:xxx : alias of 'candi:date3:hhh' action
1239
- END
1240
- end
963
+ end
1241
964
 
1242
- spec "[!i2azi] raises error when no candidate actions found." do
1243
- pr = proc do
1244
- @app.__send__(:do_print_candidates, ["candi:date9:"], {})
1245
- end
1246
- ok {pr}.raise?(Benry::CmdApp::CommandError,
1247
- "No actions starting with 'candi:date9:'.")
1248
- end
1249
-
1250
- spec "[!k3lw0] private (hidden) action should not be printed as candidates." do
1251
- class CandidateTest4 < Benry::CmdApp::ActionScope
1252
- prefix "candi:date4"
1253
- @action.("test1")
1254
- def kkk(); end
1255
- private
1256
- @action.("test2")
1257
- def iii(); end
1258
- public
1259
- @action.("test3")
1260
- def jjj(); end
1261
- end
1262
- sout, serr = capture_sio do
1263
- @app.__send__(:do_print_candidates, ["candi:date4:"], {})
1264
- end
1265
- ok {serr} == ""
1266
- ok {sout} == <<"END"
1267
- Actions:
1268
- candi:date4:jjj : test3
1269
- candi:date4:kkk : test1
1270
- END
1271
- end
1272
965
 
1273
- spec "[!j4b54] shows candidates in strong format if important." do
1274
- class CandidateTest6 < Benry::CmdApp::ActionScope
1275
- prefix "candi:date6"
1276
- @action.("test1")
1277
- def t1(); end
1278
- @action.("test2", important: true)
1279
- def t2(); end
1280
- end
1281
- Benry::CmdApp.action_alias("candi:date6", "candi:date6:t2")
1282
- sout, serr = capture_sio(tty: true) do
1283
- @app.__send__(:do_print_candidates, ["candi:date6:"], {})
1284
- end
1285
- ok {serr} == ""
1286
- ok {sout} == <<END
1287
- \e[34mActions:\e[0m
1288
- \e[1m\e[4mcandi:date6\e[0m \e[0m : alias of 'candi:date6:t2' action
1289
- \e[1mcandi:date6:t1 \e[0m : test1
1290
- \e[1m\e[4mcandi:date6:t2\e[0m \e[0m : test2
1291
- (alias: candi:date6)
966
+ end
967
+
968
+
969
+ topic Benry::CmdApp::GlobalOptionSchema do
970
+
971
+ def new_schema(config)
972
+ return Benry::CmdApp::GlobalOptionSchema.new(config)
973
+ end
974
+
975
+
976
+ topic '#initialize()' do
977
+
978
+ spec "[!ppcvp] adds options according to config object." do
979
+ config = Benry::CmdApp::Config.new("sample app", "1.2.3")
980
+ schema = new_schema(config)
981
+ ok {schema.get(:help)} != nil
982
+ ok {schema.get(:version)} != nil
983
+ ok {schema.get(:list)} != nil
984
+ ok {schema.get(:all)} != nil
985
+ ok {schema.get(:verbose)} == nil
986
+ ok {schema.get(:quiet)} == nil
987
+ ok {schema.get(:color)} == nil
988
+ ok {schema.get(:debug)} != nil
989
+ ok {schema.get(:debug)}.hidden?
990
+ ok {schema.to_s} == <<"END"
991
+ -h, --help : print help message (of action if specified)
992
+ -V, --version : print version
993
+ -l, --list : list actions and aliases
994
+ -a, --all : list hidden actions/options, too
995
+ END
996
+ ok {schema.get(:trace)} == nil
997
+ #
998
+ config.option_help = false
999
+ config.option_version = false
1000
+ config.option_list = false
1001
+ config.option_all = false
1002
+ config.option_verbose = true
1003
+ config.option_quiet = true
1004
+ config.option_color = true
1005
+ config.option_debug = true
1006
+ config.option_trace = true
1007
+ #
1008
+ schema = new_schema(config)
1009
+ ok {schema.get(:help)} == nil
1010
+ ok {schema.get(:version)} == nil
1011
+ ok {schema.get(:list)} == nil
1012
+ ok {schema.get(:all)} == nil
1013
+ ok {schema.get(:verbose)} != nil
1014
+ ok {schema.get(:quiet)} != nil
1015
+ ok {schema.get(:color)} != nil
1016
+ ok {schema.get(:trace)} != nil
1017
+ ok {schema.get(:debug)}.NOT.hidden?
1018
+ end
1019
+
1020
+ spec "[!doj0k] if config option is `:hidden`, makes option as hidden." do
1021
+ config = Benry::CmdApp::Config.new("sample app", "1.2.3")
1022
+ config.option_help = :hidden
1023
+ config.option_version = :hidden
1024
+ config.option_list = :hidden
1025
+ config.option_topic = :hidden
1026
+ config.option_all = :hidden
1027
+ config.option_verbose = :hidden
1028
+ config.option_quiet = :hidden
1029
+ config.option_color = :hidden
1030
+ config.option_debug = :hidden
1031
+ config.option_trace = :hidden
1032
+ #
1033
+ schema = new_schema(config)
1034
+ ok {schema.get(:help ).hidden?} == true
1035
+ ok {schema.get(:version).hidden?} == true
1036
+ ok {schema.get(:list ).hidden?} == true
1037
+ ok {schema.get(:topic ).hidden?} == true
1038
+ ok {schema.get(:all ).hidden?} == true
1039
+ ok {schema.get(:verbose).hidden?} == true
1040
+ ok {schema.get(:quiet ).hidden?} == true
1041
+ ok {schema.get(:color ).hidden?} == true
1042
+ ok {schema.get(:debug ).hidden?} == true
1043
+ ok {schema.get(:trace ).hidden?} == true
1044
+ #
1045
+ ok {schema.option_help()} == ""
1046
+ ok {schema.option_help(all: true)} == <<'END'
1047
+ --help : print help message (of action if specified)
1048
+ --version : print version
1049
+ --list : list actions and aliases
1050
+ -L <topic> : topic list (actions|aliases|categories|abbrevs)
1051
+ --all : list hidden actions/options, too
1052
+ --verbose : verbose mode
1053
+ --quiet : quiet mode
1054
+ --color[=<on|off>] : color mode
1055
+ --debug : debug mode
1056
+ --trace : trace mode
1292
1057
  END
1293
1058
  end
1294
1059
 
1295
- spec "[!q3819] shows candidates in weak format if not important." do
1296
- class CandidateTest7 < Benry::CmdApp::ActionScope
1297
- prefix "candi:date7"
1298
- @action.("test1")
1299
- def t1(); end
1300
- @action.("test2", important: false)
1301
- def t2(); end
1302
- end
1303
- Benry::CmdApp.action_alias("candi:date7", "candi:date7:t2")
1304
- sout, serr = capture_sio(tty: true) do
1305
- @app.__send__(:do_print_candidates, ["candi:date7:"], {})
1306
- end
1307
- ok {serr} == ""
1308
- ok {sout} == <<END
1309
- \e[34mActions:\e[0m
1310
- \e[1m\e[2mcandi:date7\e[0m \e[0m : alias of 'candi:date7:t2' action
1311
- \e[1mcandi:date7:t1 \e[0m : test1
1312
- \e[1m\e[2mcandi:date7:t2\e[0m \e[0m : test2
1313
- (alias: candi:date7)
1314
- END
1060
+ spec "[!umjw5] add nothing if config is nil." do
1061
+ schema = new_schema(nil)
1062
+ ok {schema}.empty?
1315
1063
  end
1316
1064
 
1317
1065
  end
1318
1066
 
1319
1067
 
1320
- topic '#do_setup()' do
1321
-
1322
- spec "[!pkio4] sets config object to '$cmdapp_config'." do
1323
- $cmdapp_config = nil
1324
- @app.__send__(:do_setup,)
1325
- ok {$cmdapp_config} != nil
1326
- ok {$cmdapp_config} == @app.config
1327
- end
1068
+ topic '#reorder_options(*keys)' do
1328
1069
 
1329
- spec "[!qwjjv] sets application object to '$cmdapp_application'." do
1330
- $cmdapp_application = nil
1331
- @app.__send__(:do_setup,)
1332
- ok {$cmdapp_application} != nil
1333
- ok {$cmdapp_application} == @app
1070
+ spec "[!2cp9s] sorts options in order of keys specified." do
1071
+ config = Benry::CmdApp::Config.new("sample app", "1.2.3", option_debug: true)
1072
+ schema = new_schema(config)
1073
+ ok {schema.to_s} == <<'END'
1074
+ -h, --help : print help message (of action if specified)
1075
+ -V, --version : print version
1076
+ -l, --list : list actions and aliases
1077
+ -a, --all : list hidden actions/options, too
1078
+ --debug : debug mode
1079
+ END
1080
+ #
1081
+ schema.reorder_options(:list, :topic, :help, :all, :debug, :version)
1082
+ ok {schema.to_s} == <<'END'
1083
+ -l, --list : list actions and aliases
1084
+ -h, --help : print help message (of action if specified)
1085
+ -a, --all : list hidden actions/options, too
1086
+ --debug : debug mode
1087
+ -V, --version : print version
1088
+ END
1334
1089
  end
1335
1090
 
1336
- spec "[!kqfn1] remove built-in 'help' action if `config.help_action == false`." do
1337
- ameta = Benry::CmdApp::INDEX.get_action("help")
1338
- ok {Benry::CmdApp::INDEX.action_exist?("help")} == true
1339
- begin
1340
- @config.help_action = false
1341
- @app.__send__(:do_setup,)
1342
- ok {Benry::CmdApp::INDEX.action_exist?("help")} == false
1343
- ensure
1344
- Benry::CmdApp::INDEX.register_action("help", ameta)
1345
- end
1091
+ spec "[!xe7e1] moves options which are not included in specified keys to end of option list." do
1092
+ config = Benry::CmdApp::Config.new("sample app", "1.2.3", option_debug: true)
1093
+ schema = new_schema(config)
1094
+ schema.reorder_options(:list, :help)
1095
+ ok {schema.to_s} == <<'END'
1096
+ -l, --list : list actions and aliases
1097
+ -h, --help : print help message (of action if specified)
1098
+ -V, --version : print version
1099
+ -a, --all : list hidden actions/options, too
1100
+ --debug : debug mode
1101
+ END
1346
1102
  end
1347
1103
 
1348
1104
  end
1349
1105
 
1350
1106
 
1351
- topic '#do_teardown()' do
1107
+ end
1352
1108
 
1353
- spec "[!zxeo7] clears '$cmdapp_config'." do
1354
- $cmdapp_config = "AAA"
1355
- @app.__send__(:do_teardown, nil)
1356
- ok {$cmdapp_config} == nil
1357
- end
1358
1109
 
1359
- spec "[!ufm1d] clears '$cmdapp_application'." do
1360
- $cmdapp_application = @app
1361
- @app.__send__(:do_teardown, nil)
1362
- ok {$cmdapp_application} == nil
1110
+ topic Benry::CmdApp::MetadataRenderer do
1111
+
1112
+
1113
+ topic '#render_metadata()' do
1114
+
1115
+ before do
1116
+ reg = Benry::CmdApp::REGISTRY
1117
+ @registry = Benry::CmdApp::Registry.new
1118
+ r = @registry
1119
+ r.metadata_add(reg.metadata_get("hello"))
1120
+ r.metadata_add(Benry::CmdApp::AliasMetadata.new("hi", "hello", []))
1121
+ r.category_add("cat:", "test category")
1122
+ r.abbrev_add("c:", "cat:")
1123
+ end
1124
+
1125
+ EXPECTED = <<'END'
1126
+ actions:
1127
+ - action: hello
1128
+ desc: "greeting message"
1129
+ class: MyAction
1130
+ method: hello
1131
+ hidden: false
1132
+ paramstr: "[<name>]"
1133
+ parameters:
1134
+ - param: name
1135
+ type: opt
1136
+ - param: lang
1137
+ type: key
1138
+ options:
1139
+ - key: lang
1140
+ desc: "language name (en/fr/it)"
1141
+ optdef: "-l, --lang=<lang>"
1142
+ short: l
1143
+ long: lang
1144
+ param: "<lang>"
1145
+ paramreq: required
1146
+ hidden: false
1147
+
1148
+ aliases:
1149
+ - alias: hi
1150
+ desc: "alias for 'hello'"
1151
+ action: hello
1152
+
1153
+ categories:
1154
+ - prefix: "cat:"
1155
+ count: 0
1156
+ desc: "test category"
1157
+
1158
+ abbreviations:
1159
+ - abbrev: "c:"
1160
+ prefix: "cat:"
1161
+ END
1162
+
1163
+ spec "[!gduge] renders registry data in YAML format." do
1164
+ renderer = Benry::CmdApp::MetadataRenderer.new(@registry)
1165
+ yaml = renderer.render_metadata()
1166
+ ok {yaml} == EXPECTED
1363
1167
  end
1364
1168
 
1365
1169
  end