methadone-rehab 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +11 -0
  6. data/CHANGES.md +66 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +201 -0
  9. data/README.rdoc +179 -0
  10. data/Rakefile +98 -0
  11. data/TODO.md +3 -0
  12. data/bin/methadone +157 -0
  13. data/features/bootstrap.feature +169 -0
  14. data/features/license.feature +43 -0
  15. data/features/multilevel_commands.feature +125 -0
  16. data/features/readme.feature +26 -0
  17. data/features/rspec_support.feature +27 -0
  18. data/features/step_definitions/bootstrap_steps.rb +47 -0
  19. data/features/step_definitions/license_steps.rb +30 -0
  20. data/features/step_definitions/readme_steps.rb +26 -0
  21. data/features/step_definitions/version_steps.rb +4 -0
  22. data/features/support/env.rb +26 -0
  23. data/features/version.feature +17 -0
  24. data/lib/methadone.rb +15 -0
  25. data/lib/methadone/argv_parser.rb +50 -0
  26. data/lib/methadone/cli.rb +124 -0
  27. data/lib/methadone/cli_logger.rb +133 -0
  28. data/lib/methadone/cli_logging.rb +138 -0
  29. data/lib/methadone/cucumber.rb +174 -0
  30. data/lib/methadone/error.rb +32 -0
  31. data/lib/methadone/execution_strategy/base.rb +34 -0
  32. data/lib/methadone/execution_strategy/jvm.rb +37 -0
  33. data/lib/methadone/execution_strategy/mri.rb +16 -0
  34. data/lib/methadone/execution_strategy/open_3.rb +16 -0
  35. data/lib/methadone/execution_strategy/open_4.rb +22 -0
  36. data/lib/methadone/execution_strategy/rbx_open_4.rb +12 -0
  37. data/lib/methadone/exit_now.rb +40 -0
  38. data/lib/methadone/main.rb +1039 -0
  39. data/lib/methadone/process_status.rb +45 -0
  40. data/lib/methadone/sh.rb +223 -0
  41. data/lib/methadone/version.rb +3 -0
  42. data/methadone-rehab.gemspec +32 -0
  43. data/templates/full/.gitignore.erb +4 -0
  44. data/templates/full/README.rdoc.erb +25 -0
  45. data/templates/full/Rakefile.erb +74 -0
  46. data/templates/full/_license_head.txt.erb +2 -0
  47. data/templates/full/apache_LICENSE.txt.erb +203 -0
  48. data/templates/full/bin/executable.erb +47 -0
  49. data/templates/full/custom_LICENSE.txt.erb +0 -0
  50. data/templates/full/features/executable.feature.erb +13 -0
  51. data/templates/full/features/step_definitions/executable_steps.rb.erb +1 -0
  52. data/templates/full/features/support/env.rb.erb +16 -0
  53. data/templates/full/gplv2_LICENSE.txt.erb +14 -0
  54. data/templates/full/gplv3_LICENSE.txt.erb +15 -0
  55. data/templates/full/mit_LICENSE.txt.erb +7 -0
  56. data/templates/multicommand/bin/executable.erb +52 -0
  57. data/templates/multicommand/lib/command.rb.erb +40 -0
  58. data/templates/multicommand/lib/commands.rb.erb +7 -0
  59. data/templates/rspec/spec/something_spec.rb.erb +5 -0
  60. data/templates/test_unit/test/tc_something.rb.erb +7 -0
  61. data/test/base_test.rb +20 -0
  62. data/test/command_for_tests.sh +7 -0
  63. data/test/execution_strategy/test_base.rb +24 -0
  64. data/test/execution_strategy/test_jvm.rb +77 -0
  65. data/test/execution_strategy/test_mri.rb +32 -0
  66. data/test/execution_strategy/test_open_3.rb +70 -0
  67. data/test/execution_strategy/test_open_4.rb +86 -0
  68. data/test/execution_strategy/test_rbx_open_4.rb +25 -0
  69. data/test/test_cli_logger.rb +219 -0
  70. data/test/test_cli_logging.rb +243 -0
  71. data/test/test_exit_now.rb +37 -0
  72. data/test/test_main.rb +1213 -0
  73. data/test/test_multi.rb +405 -0
  74. data/test/test_sh.rb +404 -0
  75. metadata +321 -0
@@ -0,0 +1,37 @@
1
+ require 'base_test'
2
+ require 'methadone'
3
+ require 'stringio'
4
+
5
+ class TestExitNow < BaseTest
6
+ include Methadone
7
+ include Methadone::ExitNow
8
+
9
+ test_that "exit_now raises the proper error" do
10
+ Given {
11
+ @exit_code = any_int :min => 1
12
+ @message = any_string
13
+ }
14
+ When {
15
+ @code = lambda { exit_now!(@exit_code,@message) }
16
+ }
17
+ Then {
18
+ exception = assert_raises(Methadone::Error,&@code)
19
+ exception.exit_code.should == @exit_code
20
+ exception.message.should == @message
21
+ }
22
+ end
23
+
24
+ test_that "exit_now without an exit code uses 1 as the exit code" do
25
+ Given {
26
+ @message = any_string
27
+ }
28
+ When {
29
+ @code = lambda { exit_now!(@message) }
30
+ }
31
+ Then {
32
+ exception = assert_raises(Methadone::Error,&@code)
33
+ exception.exit_code.should == 1
34
+ exception.message.should == @message
35
+ }
36
+ end
37
+ end
@@ -0,0 +1,1213 @@
1
+ require 'base_test'
2
+ require 'methadone'
3
+ require 'stringio'
4
+ require 'fileutils'
5
+
6
+ class TestMain < BaseTest
7
+ include Methadone::Main
8
+
9
+ def setup
10
+ @original_argv = ARGV.clone
11
+ ARGV.clear
12
+ @old_stdout = $stdout
13
+ $stdout = StringIO.new
14
+ @logged = StringIO.new
15
+ @custom_logger = Logger.new(@logged)
16
+
17
+ @original_home = ENV['HOME']
18
+ fake_home = '/tmp/fake-home'
19
+ FileUtils.rm_rf(fake_home)
20
+ FileUtils.mkdir(fake_home)
21
+ ENV['HOME'] = fake_home
22
+ end
23
+
24
+ # Override the built-in logger so we can capture it
25
+ def logger
26
+ @custom_logger
27
+ end
28
+
29
+ def teardown
30
+ set_argv @original_argv
31
+ ENV.delete('DEBUG')
32
+ ENV.delete('APP_OPTS')
33
+ $stdout = @old_stdout
34
+ ENV['HOME'] = @original_home
35
+ end
36
+
37
+ test_that "my main block gets called by run and has access to CLILogging" do
38
+ Given {
39
+ @called = false
40
+ main do
41
+ begin
42
+ logger.debug "debug"
43
+ logger.info "info"
44
+ logger.warn "warn"
45
+ logger.error "error"
46
+ logger.fatal "fatal"
47
+ @called = true
48
+ rescue => ex
49
+ puts ex.message
50
+ end
51
+ end
52
+ }
53
+ When run_go_safely
54
+ Then main_shouldve_been_called
55
+ end
56
+
57
+ test_that "my main block does not get command-line parameters that were not specified by args, but they are still available in ARGV" do
58
+ Given {
59
+ @params = []
60
+ @argv = []
61
+ main do |param1,param2,param3|
62
+ @params << param1
63
+ @params << param2
64
+ @params << param3
65
+ @argv = ::ARGV
66
+ end
67
+ set_argv %w(one two three)
68
+ }
69
+ When run_go_safely
70
+ Then {
71
+ @params.should == [nil,nil,nil]
72
+ @argv.should == %w(one two three)
73
+ }
74
+ end
75
+
76
+ test_that "my main block can freely ignore arguments given" do
77
+ Given {
78
+ @called = false
79
+ main do
80
+ @called = true
81
+ end
82
+ set_argv %w(one two three)
83
+ }
84
+ When run_go_safely
85
+ Then main_shouldve_been_called
86
+ end
87
+
88
+ test_that "my main block can ask for arguments that it might not receive" do
89
+ Given {
90
+ @params = []
91
+ @argv = []
92
+ arg 'param1'
93
+ main do |param1,param2,param3|
94
+ @params << param1
95
+ @params << param2
96
+ @params << param3
97
+ @argv = ::ARGV
98
+ end
99
+ set_argv %w(one two)
100
+ }
101
+ When run_go_safely
102
+ Then {
103
+ @params.should == ['one',nil,nil]
104
+ @argv.should == ['two']
105
+ }
106
+ end
107
+
108
+ test_that "go exits zero when main evaluates to nil or some other non number" do
109
+ [nil,'some string',Object.new,[],4.5].each do |non_number|
110
+ Given main_that_exits non_number
111
+ Then {
112
+ assert_exits(0,"for value #{non_number}") { When run_go! }
113
+ }
114
+ end
115
+ end
116
+
117
+ test_that "go exits with the numeric value that main evaluated to" do
118
+ [0,1,2,3].each do |exit_status|
119
+ Given main_that_exits exit_status
120
+ Then {
121
+ assert_exits(exit_status) { When run_go! }
122
+ }
123
+ end
124
+ end
125
+
126
+ test_that "go exits with 70, which is the Linux sysexits.h code for this sort of thing, if there's an exception" do
127
+ Given {
128
+ leak_exceptions false
129
+ main do
130
+ raise "oh noes"
131
+ end
132
+ }
133
+ Then {
134
+ assert_exits(70) { When run_go! }
135
+ assert_logged_at_error "oh noes"
136
+ }
137
+ end
138
+
139
+ test_that "go allows the exception raised to leak through if DEBUG is set in the environment" do
140
+ Given {
141
+ ENV['DEBUG'] = 'true'
142
+ main do
143
+ raise ArgumentError,"oh noes"
144
+ end
145
+ }
146
+ Then {
147
+ assert_raises ArgumentError do
148
+ When run_go!
149
+ end
150
+ }
151
+ end
152
+
153
+ test_that "An exception that's not a StandardError causes the excepteion to break through and raise" do
154
+ Given {
155
+ main do
156
+ raise Exception,"oh noes"
157
+ end
158
+ }
159
+ Then {
160
+ ex = assert_raises Exception do
161
+ When run_go!
162
+ end
163
+ assert_equal "oh noes",ex.message
164
+ }
165
+ end
166
+
167
+ test_that "Non-methadone exceptions leak through if we configure it that way" do
168
+ Given {
169
+ main do
170
+ raise StandardError,"oh noes"
171
+ end
172
+ leak_exceptions true
173
+ }
174
+ Then {
175
+ ex = assert_raises StandardError do
176
+ When run_go!
177
+ end
178
+ assert_equal "oh noes",ex.message
179
+ }
180
+ end
181
+
182
+ test_that "go exits with the exit status included in the special-purpose excepiton" do
183
+ Given {
184
+ main do
185
+ raise Methadone::Error.new(4,"oh noes")
186
+ end
187
+ }
188
+ Then {
189
+ assert_exits(4) { When run_go! }
190
+ assert_logged_at_error "oh noes"
191
+ }
192
+ end
193
+
194
+ test_that "go allows the special methadone exception to leak through if DEBUG is set in the environment" do
195
+ Given {
196
+ ENV['DEBUG'] = 'true'
197
+ main do
198
+ raise Methadone::Error.new(4,"oh noes")
199
+ end
200
+ }
201
+ Then {
202
+ assert_raises Methadone::Error do
203
+ When run_go!
204
+ end
205
+ }
206
+ end
207
+
208
+ test_that "can exit with a specific status by using the helper method instead of making a new exception" do
209
+ Given {
210
+ main do
211
+ exit_now!(4,"oh noes")
212
+ end
213
+ }
214
+ Then {
215
+ assert_exits(4) { When run_go! }
216
+ assert_logged_at_error "oh noes"
217
+ }
218
+ end
219
+
220
+ test_that "when we help_now! we exit and show help" do
221
+ Given {
222
+ @message = any_sentence
223
+ main do
224
+ help_now!(@message)
225
+ end
226
+
227
+ opts.on("--switch") { options[:switch] = true }
228
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
229
+
230
+ set_argv []
231
+ }
232
+
233
+ Then {
234
+ assert_exits(64) { When run_go! }
235
+ assert $stdout.string.include?(opts.to_s),"Expected #{$stdout.string} to contain #{opts.to_s}"
236
+ assert_logged_at_error @message
237
+ }
238
+ end
239
+
240
+ test_that "opts allows us to more expediently set up OptionParser" do
241
+ Given {
242
+ @switch = nil
243
+ @flag = nil
244
+ main do
245
+ @switch = options[:switch]
246
+ @flag = options[:flag]
247
+ end
248
+
249
+ opts.on("--switch") { options[:switch] = true }
250
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
251
+
252
+ set_argv %w(--switch --flag value)
253
+ }
254
+
255
+ When run_go_safely
256
+
257
+ Then {
258
+ @switch.should be true
259
+ @flag.should == 'value'
260
+ }
261
+ end
262
+
263
+ test_that "when the command line is invalid, we exit with 64 and print the CLI help" do
264
+ Given {
265
+ main do
266
+ end
267
+
268
+ opts.on("--switch") { options[:switch] = true }
269
+ opts.on("--flag FLAG") { |value| options[:flag] = value }
270
+
271
+ set_argv %w(--invalid --flag value)
272
+ }
273
+
274
+ Then {
275
+ assert_exits(64) { When run_go! }
276
+ assert $stdout.string.include?(opts.to_s),"Expected #{$stdout.string} to contain #{opts.to_s}"
277
+ }
278
+ end
279
+
280
+ test_that "when setting defaults they get copied to strings/symbols as well" do
281
+ Given {
282
+ @flag_with_string_key_defalt = nil
283
+ @flag_with_symbol_key_defalt = nil
284
+ reset!
285
+ main do
286
+ @flag_with_string_key_defalt = options[:foo]
287
+ @flag_with_symbol_key_defalt = options['bar']
288
+ end
289
+ options['foo'] = 'FOO'
290
+ options[:bar] = 'BAR'
291
+ on("--foo")
292
+ on("--bar")
293
+ }
294
+ When run_go_safely
295
+ Then {
296
+ assert_equal 'FOO',@flag_with_string_key_defalt
297
+ assert_equal 'BAR',@flag_with_symbol_key_defalt
298
+ }
299
+ end
300
+
301
+ test_that "passing non-strings wont' break automagic stuff" do
302
+ Given {
303
+ @foo = nil
304
+ @bar = nil
305
+ main do
306
+ @foo = options[:foo]
307
+ @bar = options[:bar]
308
+ end
309
+ on("--foo ARG",OptionParser::DecimalInteger)
310
+ on("--bar ARG",/^\d/)
311
+ set_argv %w(--foo 88 --bar 4)
312
+ }
313
+ When run_go_safely
314
+ Then {
315
+ assert_equal 88,@foo,@logged.string + $stdout.string
316
+ assert_equal '4',@bar,@logged.string + $stdout.string
317
+ }
318
+ end
319
+
320
+ test_that "omitting the block to opts simply sets the value in the options hash and returns itself" do
321
+ Given {
322
+ @switch = nil
323
+ @negatable = nil
324
+ @flag = nil
325
+ @f = nil
326
+ @other = nil
327
+ @some_other = nil
328
+ @with_dashes = nil
329
+ main do
330
+ @switch = [options[:switch],options['switch']]
331
+ @flag = [options[:flag],options['flag']]
332
+ @f = [options[:f],options['f']]
333
+ @negatable = [options[:negatable],options['negatable']]
334
+ @other = [options[:other],options['other']]
335
+ @some_other = [options[:some_other],options['some_other']]
336
+ @with_dashes = [options[:'flag-with-dashes'],options['flag-with-dashes']]
337
+ end
338
+
339
+ on("--switch")
340
+ on("--[no-]negatable")
341
+ on("--flag FLAG","-f","Some documentation string")
342
+ on("--flag-with-dashes FOO")
343
+ on("--other") do
344
+ options[:some_other] = true
345
+ end
346
+
347
+ set_argv %w(--switch --flag value --negatable --other --flag-with-dashes=BAR)
348
+ }
349
+
350
+ When run_go_safely
351
+
352
+ Then {
353
+ @switch[0].should be true
354
+ @some_other[0].should be true
355
+ @other[0].should_not be true
356
+ @flag[0].should == 'value'
357
+ @f[0].should == 'value'
358
+ @with_dashes[0].should == 'BAR'
359
+
360
+ @switch[1].should be true
361
+ @some_other[1].should be nil # ** this is set manually
362
+ @other[1].should_not be true
363
+ @flag[1].should == 'value'
364
+ @f[1].should == 'value'
365
+ @with_dashes[1].should == 'BAR'
366
+
367
+ opts.to_s.should match /Some documentation string/
368
+ }
369
+ end
370
+
371
+ test_that "without specifying options, options in brackets doesn't show up in our banner" do
372
+ Given {
373
+ main {}
374
+ }
375
+
376
+ Then {
377
+ opts.banner.should_not match /\[options\]/
378
+ }
379
+ end
380
+
381
+ test_that "when specifying an option, [options] shows up in the banner" do
382
+ Given {
383
+ main {}
384
+ on("-s")
385
+ }
386
+
387
+ Then {
388
+ opts.banner.should match /\[options\]/
389
+ }
390
+
391
+ end
392
+
393
+ test_that "I can specify which arguments my app takes and if they are required as well as document them" do
394
+ Given {
395
+ main {}
396
+ @db_name_desc = any_string
397
+ @user_desc = any_string
398
+ @password_desc = any_string
399
+
400
+ arg :db_name, @db_name_desc
401
+ arg :user, :required, @user_desc
402
+ arg :password, :optional, @password_desc
403
+ }
404
+ When run_go_safely
405
+ Then {
406
+ opts.banner.should match /db_name user \[password\]$/
407
+ opts.to_s.should match /#{@db_name_desc}/
408
+ opts.to_s.should match /#{@user_desc}/
409
+ opts.to_s.should match /#{@password_desc}/
410
+ }
411
+ end
412
+
413
+ test_that "I can specify which arguments my app takes and if they are singular or plural" do
414
+ Given {
415
+ main {}
416
+
417
+ arg :db_name
418
+ arg :user, :required, :one
419
+ arg :tables, :many
420
+ }
421
+
422
+ Then {
423
+ opts.banner.should match /db_name user tables...$/
424
+ }
425
+ end
426
+
427
+ test_that "I can specify which arguments my app takes and if they are singular or optional plural" do
428
+ Given {
429
+ main {}
430
+
431
+ arg :db_name
432
+ arg :user, :required, :one
433
+ arg :tables, :any
434
+ }
435
+
436
+ Then {
437
+ opts.banner.should match /db_name user \[tables...\]$/
438
+ }
439
+ end
440
+
441
+ test_that "I can set a description for my app" do
442
+ Given {
443
+ main {}
444
+ description "An app of total awesome"
445
+
446
+ }
447
+ Then {
448
+ opts.banner.should match /^An app of total awesome$/
449
+ }
450
+ end
451
+
452
+ test_that "when I override the banner, we don't automatically do anything" do
453
+ Given {
454
+ main {}
455
+ opts.banner = "FOOBAR"
456
+
457
+ on("-s")
458
+ }
459
+ When {opts}
460
+ Then {
461
+ opts.banner.should == 'FOOBAR'
462
+ }
463
+ end
464
+
465
+ test_that "when I say an argument is required and its omitted, I get an error" do
466
+ Given {
467
+ main {}
468
+ arg :foo
469
+ arg :bar
470
+
471
+ set_argv %w(blah)
472
+ }
473
+
474
+ Then {
475
+ assert_exits(64) { When run_go! }
476
+ assert_logged_at_error("parse error: 'bar' is required")
477
+ }
478
+ end
479
+
480
+ test_that "when I say an argument is many and its omitted, I get an error" do
481
+ Given {
482
+ main {}
483
+ arg :foo
484
+ arg :bar, :many
485
+
486
+ set_argv %w(blah)
487
+ }
488
+
489
+ Then {
490
+ assert_exits(64) { When run_go! }
491
+ assert_logged_at_error("parse error: at least one 'bar' is required")
492
+ }
493
+ end
494
+
495
+ test_that "when I specify a version, it shows up in the banner" do
496
+ Given {
497
+ main{}
498
+ version "0.0.1"
499
+ }
500
+
501
+ Then {
502
+ opts.banner.should match /^v0.0.1/m
503
+ }
504
+ end
505
+
506
+ test_that "when I specify a version, I can get help via --version" do
507
+ Given {
508
+ main{}
509
+ version "0.0.1"
510
+ set_argv(['--version'])
511
+ }
512
+ Then run_go_safely
513
+ And {
514
+ opts.to_s.should match /Show help\/version info/m
515
+ $stdout.string.should match /^#{@version}$/
516
+ }
517
+ end
518
+
519
+ test_that "when I specify a version with custom help, it shows up" do
520
+ @version_message = "SHOW ME VERSIONS"
521
+ Given {
522
+ main{}
523
+ version "0.0.1",@version_message
524
+ set_argv(['--verison'])
525
+ }
526
+ Then run_go_safely
527
+ And {
528
+ opts.to_s.should match /#{@version_message}/
529
+ }
530
+ end
531
+
532
+ test_that "when I specify a version with :basic format, only the command and version show up and the help is 'show version info'" do
533
+ @version = "2.0.9.pre"
534
+ Given {
535
+ version @version, :basic
536
+ set_argv %w(--version)
537
+ }
538
+ When run_go_safely
539
+ Then {
540
+ opts.to_s.should match /^ *--version.*Show version info$/
541
+ $stdout.string.should match /^#{::File.basename($0)} version #{@version}$/
542
+ }
543
+ end
544
+
545
+ test_that "when I specify a version with :terse format, only the version show up and the help is 'show version'" do
546
+ @version = "2.0.9.pre"
547
+ Given {
548
+ version @version, :terse
549
+ set_argv %w(--version)
550
+ }
551
+ When run_go_safely
552
+ Then {
553
+ opts.to_s.should match /^ *--version.*Show version$/
554
+ $stdout.string.should match /^#{@version}$/
555
+ }
556
+ end
557
+
558
+ test_that "default values for boolean options are put into the docstring" do
559
+ Given {
560
+ main {}
561
+ options[:foo] = false
562
+ options[:bar] = true
563
+ on("--foo","Do the foo")
564
+ on("--[no-]bar", "Bar it like crazy")
565
+ }
566
+ When {
567
+ @help_string = opts.to_s
568
+ }
569
+ When {
570
+ @help_string.should match /--foo.*\n.*\(default: false\)/
571
+ @help_string.should match /--\[no-\]bar.*\n.*\(default: true\)/
572
+ }
573
+ end
574
+
575
+ test_that "default values for options are put into the docstring" do
576
+ Given {
577
+ main {}
578
+ options[:foo] = "bar"
579
+ on("--foo ARG","Indicate the type of foo")
580
+ }
581
+ When {
582
+ @help_string = opts.to_s
583
+ }
584
+ When {
585
+ @help_string.should match /\(default: bar\)/
586
+ }
587
+
588
+ end
589
+
590
+ test_that "default values for options with several names are put into the docstring" do
591
+ Given {
592
+ main {}
593
+ options[:foo] = "bar"
594
+ on("-f ARG","--foo","Indicate the type of foo")
595
+ }
596
+ When {
597
+ @help_string = opts.to_s
598
+ }
599
+ When {
600
+ @help_string.should match /\(default: bar\)/
601
+ }
602
+ end
603
+
604
+ test_that "when getting defaults from an environment variable, show it in the help output" do
605
+ Given app_to_use_environment
606
+ When run_go_safely
607
+ And {
608
+ @help_string = opts.to_s
609
+ }
610
+ Then {
611
+ @help_string.should match /Default values can be placed in the APP_OPTS environment variable/
612
+ }
613
+ end
614
+
615
+ test_that "when we want to get opts from the environment, we can" do
616
+ Given app_to_use_environment
617
+ And {
618
+ @flag_value = '56'
619
+ @some_arg = any_string
620
+ set_argv([])
621
+ ENV['APP_OPTS'] = "--switch --flag=#{@flag_value} #{@some_arg}"
622
+ }
623
+ When {
624
+ @code = lambda { go! }
625
+ }
626
+ Then {
627
+ assert_exits(0,'',&@code)
628
+ @switch.should == true
629
+ @flag.should == @flag_value
630
+ @args.should == [@some_arg]
631
+ }
632
+ end
633
+
634
+ test_that "environment args are overridden by the command line" do
635
+ Given app_to_use_environment
636
+ And {
637
+ @flag_value = any_string
638
+ ENV['APP_OPTS'] = "--switch --flag=#{any_string}"
639
+ set_argv(['--flag',@flag_value])
640
+ }
641
+ When {
642
+ @code = lambda { go! }
643
+ }
644
+ Then {
645
+ assert_exits(0,'',&@code)
646
+ @switch.should == true
647
+ @flag.should == @flag_value
648
+ }
649
+ end
650
+
651
+ test_that "environment args correctly handle spaces" do
652
+ Given app_to_use_environment
653
+ And {
654
+ @flag_value = any_string + ' ' + any_string
655
+ ENV['APP_OPTS'] = "--switch --flag='#{@flag_value}'"
656
+ }
657
+ When {
658
+ @code = lambda { go! }
659
+ }
660
+ Then {
661
+ assert_exits(0,'',&@code)
662
+ @switch.should == true
663
+ @flag.should == @flag_value
664
+ }
665
+ end
666
+
667
+ test_that "environment args correctly handle spaces via backslash stuff" do
668
+ Given app_to_use_environment
669
+ And {
670
+ cli_flag_value = any_string(:max => 4) + "\\ " + any_string(:max => 4)
671
+ @flag_value = cli_flag_value.gsub("\\ "," ")
672
+ ENV['APP_OPTS'] = "--switch --flag=#{cli_flag_value}"
673
+ }
674
+ When {
675
+ @code = lambda { go! }
676
+ }
677
+ Then {
678
+ assert_exits(0,'',&@code)
679
+ @switch.should == true
680
+ @flag.should == @flag_value
681
+ }
682
+ end
683
+
684
+ test_that "we can get defaults from a YAML config file if it's specified" do
685
+ Given app_to_use_rc_file
686
+ And {
687
+ @flag_value = any_string
688
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
689
+ File.open(rc_file,'w') do |file|
690
+ file.puts({
691
+ 'switch' => true,
692
+ 'flag' => @flag_value,
693
+ }.to_yaml)
694
+ end
695
+ }
696
+ When {
697
+ @code = lambda { go! }
698
+ }
699
+ Then {
700
+ assert_exits(0,&@code)
701
+ @switch.should == true
702
+ @flag.should == @flag_value
703
+ }
704
+
705
+ end
706
+
707
+ test_that "we can get defaults from an absolute config filename" do
708
+ tempfile = Tempfile.new('methadone_test.rc')
709
+ Given app_to_use_rc_file(tempfile.path)
710
+ And {
711
+ @flag_value = any_string
712
+ rc_file = File.join(tempfile.path)
713
+ File.open(rc_file,'w') do |file|
714
+ file.puts({
715
+ 'switch' => true,
716
+ 'flag' => @flag_value,
717
+ }.to_yaml)
718
+ end
719
+ }
720
+ When {
721
+ @code = lambda { go! }
722
+ }
723
+ Then {
724
+ assert_exits(0,&@code)
725
+ @switch.should == true
726
+ @flag.should == @flag_value
727
+ }
728
+
729
+ end
730
+
731
+
732
+ test_that "we can specify an rc file even if it doesn't exist" do
733
+ Given app_to_use_rc_file
734
+ And {
735
+ @flag_value = any_string
736
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
737
+ raise "Something's wrong, expection rc file not to exist" if File.exists?(rc_file)
738
+ }
739
+ When {
740
+ @code = lambda { go! }
741
+ }
742
+ Then {
743
+ assert_exits(0,&@code)
744
+ @switch.should == nil
745
+ @flag.should == nil
746
+ }
747
+ end
748
+
749
+ test_that "we can use a simpler, text format for the rc file" do
750
+ Given app_to_use_rc_file
751
+ And {
752
+ @flag_value = any_string
753
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
754
+ File.open(rc_file,'w') do |file|
755
+ file.puts "--switch --flag=#{@flag_value}"
756
+ end
757
+ }
758
+ When {
759
+ @code = lambda { go! }
760
+ }
761
+ Then {
762
+ assert_exits(0,&@code)
763
+ @switch.should == true
764
+ @flag.should == @flag_value
765
+ }
766
+
767
+ end
768
+
769
+ test_that "the text format for the rc file attempts to respect quoted arguments" do
770
+ Given app_to_use_rc_file
771
+ And {
772
+ @flag_value = any_string(:max => 10) + " " + any_string(:max => 10)
773
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
774
+ File.open(rc_file,'w') do |file|
775
+ file.puts "--switch --flag='#{@flag_value}'"
776
+ end
777
+ }
778
+ When {
779
+ @code = lambda { go! }
780
+ }
781
+ Then {
782
+ assert_exits(0,&@code)
783
+ @switch.should == true
784
+ @flag.should == @flag_value
785
+ }
786
+ end
787
+
788
+ test_that "with an ill-formed rc file, we get a reasonable error message" do
789
+ Given app_to_use_rc_file
790
+ And {
791
+ @flag_value = any_string
792
+ rc_file = File.join(ENV['HOME'],'.my_app.rc')
793
+ File.open(rc_file,'w') do |file|
794
+ file.puts OpenStruct.new(:foo => :bar).to_yaml
795
+ end
796
+ }
797
+ When {
798
+ @code = lambda { go! }
799
+ }
800
+ Then {
801
+ assert_exits(64,&@code)
802
+ }
803
+
804
+ end
805
+
806
+ test_that "we can call methods prior to calling main without a problem" do
807
+ Given {
808
+ description "An app of total awesome"
809
+ opts.on("--switch") { options[:switch] = true }
810
+ main {}
811
+ }
812
+ Then {
813
+ opts.banner.should match /^An app of total awesome$/
814
+ opts.to_s.should match /--switch/
815
+ }
816
+ end
817
+
818
+ test_that "usage is displayed if called with no arguments given when help_if_bare specified" do
819
+ Given {
820
+ help_if_bare
821
+ on("--required VALUE")
822
+ main {puts 'main called'}
823
+ }
824
+ When {
825
+ @code = lambda {go!}
826
+ }
827
+ Then {
828
+ assert_exits(64,&@code)
829
+ $stdout.string.should_not match /main called/
830
+ $stdout.string.should match /Usage:.*--required VALUE/m
831
+ }
832
+ end
833
+
834
+ test_that "arguments are filled according to their need" do
835
+ Given {
836
+ @can_use_one = nil
837
+ @any_will_do = nil
838
+ @need_1 = nil
839
+ @at_least_one = nil
840
+ @if_youve_got_one = nil
841
+ @any_left = nil
842
+ @last_one = nil
843
+ arg 'can_use_one', :optional, Integer
844
+ arg 'any_will_do', :any
845
+ arg 'need_1', :required
846
+ arg 'at_least_one', :many
847
+ arg 'if_youve_got_one', :optional
848
+ arg 'any_left', :any
849
+ arg 'last_one', :required
850
+ main do |can_use_one,any_will_do,need_1,at_least_one,if_youve_got_one,any_left,last_one|
851
+ @can_use_one = can_use_one
852
+ @any_will_do = any_will_do
853
+ @need_1 = need_1
854
+ @at_least_one = at_least_one
855
+ @if_youve_got_one = if_youve_got_one
856
+ @any_left = any_left
857
+ @last_one = last_one
858
+ end
859
+ set_argv %w(1 2 3 4 5 6 7 8 9 10)
860
+ }
861
+ When run_go_safely
862
+ Then {
863
+ @can_use_one.should == '1'
864
+ @any_will_do.should == %w(2 3 4 5 6 7)
865
+ @need_1.should == '8'
866
+ @at_least_one.should == ['9']
867
+ @if_youve_got_one.should == nil
868
+ @any_left.should == []
869
+ @last_one.should == '10'
870
+ }
871
+ end
872
+
873
+ test_that "arguments can be given valid values" do
874
+ Given {
875
+ arg "foo", %w(one two three)
876
+ @foo = nil
877
+ main do |foo|
878
+ @foo = foo
879
+ end
880
+ set_argv %w(two)
881
+ }
882
+ When run_go_safely
883
+ Then {
884
+ @foo = 'two'
885
+ }
886
+ end
887
+
888
+ test_that "arguments can be given valid values, and raise errors when they don't match" do
889
+ Given {
890
+ arg "foo", %w(one two three)
891
+ @foo = nil
892
+ main do |foo|
893
+ @foo = foo
894
+ end
895
+ set_argv %w(four)
896
+ }
897
+ When run_go_safely
898
+ Then {
899
+ assert_logged_at_error("parse error: foo: 'four' is invalid")
900
+ @foo = nil
901
+ }
902
+ end
903
+
904
+ test_that "arguments that take multiple values can be given valid values" do
905
+ Given {
906
+ arg "foo", :many, %w(one two three)
907
+ @foo = nil
908
+ main do |foo|
909
+ @foo = foo
910
+ end
911
+ set_argv %w(two one two)
912
+ }
913
+ When run_go_safely
914
+ Then {
915
+ @foo = %w(two one two)
916
+ }
917
+ end
918
+
919
+ test_that "arguments that take multiple values can be given valid values, and raise errors when they don't match" do
920
+ Given {
921
+ arg "foo", :many, %w(one two three)
922
+ @foo = nil
923
+ main do |foo|
924
+ @foo = foo
925
+ end
926
+ set_argv %w(four one two seven)
927
+ }
928
+ When run_go_safely
929
+ Then {
930
+ assert_logged_at_error("parse error: foo: The following value(s) were invalid: 'four seven'")
931
+ @foo = nil
932
+ }
933
+ end
934
+ test_that "arguments can be given regexp filters" do
935
+ Given {
936
+ arg "foo", /^th/
937
+ @foo = nil
938
+ main do |foo|
939
+ @foo = foo
940
+ end
941
+ set_argv %w(this)
942
+ }
943
+ When run_go_safely
944
+ Then {
945
+ @foo = 'this'
946
+ }
947
+ end
948
+
949
+ test_that "arguments can be given regexp filters, and raise errors when they don't match" do
950
+ Given {
951
+ arg "foo", :optional, /^th/
952
+ @foo = nil
953
+ main do |foo|
954
+ @foo = foo
955
+ end
956
+ set_argv %w(width)
957
+ }
958
+ When run_go_safely
959
+ Then {
960
+ assert_logged_at_error("parse error: foo: 'width' is invalid")
961
+ @foo = nil
962
+ }
963
+ end
964
+
965
+ test_that "arguments that take multiple values can be given regexp filters" do
966
+ Given {
967
+ arg "foo", :many, /^th/
968
+ @foo = nil
969
+ main do |foo|
970
+ @foo = foo
971
+ end
972
+ set_argv %w(this that the\ other)
973
+ }
974
+ When run_go_safely
975
+ Then {
976
+ @foo = %w(this that the\ other)
977
+ }
978
+ end
979
+
980
+ test_that "arguments that take multiple values can be given regexp filters, and raise errors when they don't match" do
981
+ Given {
982
+ arg "foo", :any, /^th/
983
+ @foo = nil
984
+ main do |foo|
985
+ @foo = foo
986
+ end
987
+ set_argv %w(there is this theory on things that grow)
988
+ }
989
+ When run_go_safely
990
+ Then {
991
+ assert_logged_at_error("parse error: foo: The following value(s) were invalid: 'is on grow'")
992
+ @foo = nil
993
+ }
994
+ end
995
+
996
+ test_that "arguments can be given multiple filters, requiring only one be accepted" do
997
+ Given {
998
+ arg "foo", :any, /^....$/, ['one', 'two', 'six'], /\A..?e.?e.?\z/
999
+ @foo = nil
1000
+ main do |foo|
1001
+ @foo = foo
1002
+ end
1003
+ set_argv %w(one two three four five six seven)
1004
+ }
1005
+ When run_go_safely
1006
+ Then {
1007
+ @foo = %w(one two three four five six seven)
1008
+ }
1009
+ end
1010
+
1011
+ test_that "options can be mutually exclusive" do
1012
+ Given {
1013
+ @first = nil
1014
+ @second = nil
1015
+ reset!
1016
+ main do
1017
+ @first = options[:f]
1018
+ @second = options[:s]
1019
+ end
1020
+
1021
+ on("-f", "--first", "First Option")
1022
+ on("-s", "Second Option", :excludes => :f)
1023
+ }
1024
+ When {
1025
+ set_argv %w(-f)
1026
+ @code = lambda { go! }
1027
+ }
1028
+ Then {
1029
+ assert_exits(0,&@code)
1030
+ @first.should be_truthy
1031
+ @second.should be_nil
1032
+ }
1033
+ end
1034
+
1035
+ test_that "options can be mutually exclusive, which raise an error if both are used" do
1036
+ Given {
1037
+
1038
+ @first = nil
1039
+ @second = nil
1040
+ reset!
1041
+ main do
1042
+ @first = options[:f]
1043
+ @second = options[:s]
1044
+ end
1045
+ on("-f", "--first", "First Option")
1046
+ on("-s", "Second Option", :excludes => :f)
1047
+ }
1048
+ When {
1049
+ set_argv %w(-f -s)
1050
+ @code = lambda { go! }
1051
+ }
1052
+ Then {
1053
+ assert_exits(64,&@code)
1054
+ assert_logged_at_error("parse error: -s cannot be used if already using -f|--first")
1055
+ $stdout.string.should match /Usage:/
1056
+ }
1057
+ end
1058
+
1059
+ test_that "options can be mutually exclusive, which raise an error if both are used - order doesn't matter" do
1060
+ Given {
1061
+
1062
+ @first = nil
1063
+ @second = nil
1064
+ reset!
1065
+ main do
1066
+ @first = options[:f]
1067
+ @second = options[:s]
1068
+ end
1069
+ on("-f", "--first", "First Option")
1070
+ on("-s", "Second Option", :excludes => :f)
1071
+ }
1072
+ When {
1073
+ set_argv %w(-s -f)
1074
+ @code = lambda { go! }
1075
+ }
1076
+ Then {
1077
+ assert_exits(64,&@code)
1078
+ assert_logged_at_error("parse error: -s cannot be used if already using -f|--first")
1079
+ $stdout.string.should match /Usage:/
1080
+ }
1081
+ end
1082
+
1083
+ test_that "options can be mutually exclusive, the enemy of my enemy is my friend" do
1084
+ Given {
1085
+
1086
+ @first = nil
1087
+ @second = nil
1088
+ @third = nil
1089
+ reset!
1090
+ main do
1091
+ @first = options[:f]
1092
+ @second = options[:s]
1093
+ @third = options[:third]
1094
+ end
1095
+ on("-f", "--first", "First Option")
1096
+ on("--another-one", "Second Option", :excludes => :f)
1097
+ on("--third TEST", "Second Option", :excludes => "another-one")
1098
+ }
1099
+ When {
1100
+ set_argv %w(-f --third value)
1101
+ @code = lambda { go! }
1102
+ }
1103
+ Then {
1104
+ assert_exits(0,&@code)
1105
+ @first.should be_truthy
1106
+ @second.should be_nil
1107
+ @third.should == 'value'
1108
+ }
1109
+ end
1110
+
1111
+ test_that "options can be options can require other options to be present" do
1112
+ Given {
1113
+
1114
+ @first = nil
1115
+ @second = nil
1116
+ @third = nil
1117
+ reset!
1118
+ main do
1119
+ @first = options[:f]
1120
+ @second = options[:s]
1121
+ @third = options[:third]
1122
+ end
1123
+ on("-f", "--first", "First Option")
1124
+ on("-a","--another-one", "Second Option")
1125
+ on("--third TEST", "Second Option", :requires => [:first,'another-one'])
1126
+ }
1127
+ When {
1128
+ set_argv %w(-f --third value)
1129
+ @code = lambda { go! }
1130
+ }
1131
+ Then {
1132
+ assert_exits(64,&@code)
1133
+ assert_logged_at_error("parse error: Missing option -a|--another-one required by option --third TEST")
1134
+ $stdout.string.should match /Usage:/
1135
+ }
1136
+ end
1137
+
1138
+ private
1139
+
1140
+ def app_to_use_rc_file(rc_file = '.my_app.rc')
1141
+ lambda {
1142
+ reset!
1143
+ @switch = nil
1144
+ @flag = nil
1145
+ @args = nil
1146
+ main do |*args|
1147
+ @switch = options[:switch]
1148
+ @flag = options[:flag]
1149
+ @args = args
1150
+ end
1151
+
1152
+ defaults_from_config_file rc_file
1153
+
1154
+ on('--switch','Some Switch')
1155
+ on('--flag FOO','Some Flag')
1156
+ }
1157
+ end
1158
+
1159
+ def main_that_exits(exit_status)
1160
+ proc { main { exit_status } }
1161
+ end
1162
+
1163
+ def app_to_use_environment
1164
+ lambda {
1165
+ @switch = nil
1166
+ @flag = nil
1167
+ @args = nil
1168
+ arg :args, :any
1169
+ main do |args|
1170
+ @switch = options[:switch]
1171
+ @flag = options[:flag]
1172
+ @args = args
1173
+ end
1174
+
1175
+ defaults_from_env_var 'APP_OPTS'
1176
+
1177
+ on('--switch','Some Switch')
1178
+ on('--flag FOO','Some Flag')
1179
+ }
1180
+ end
1181
+
1182
+ def main_shouldve_been_called
1183
+ Proc.new { assert @called,"Main block wasn't called?!" }
1184
+ end
1185
+
1186
+ def run_go_safely
1187
+ Proc.new { safe_go! }
1188
+ end
1189
+
1190
+ # Calls go!, but traps the exit
1191
+ def safe_go!
1192
+ go!
1193
+ rescue SystemExit
1194
+ end
1195
+
1196
+ def run_go!; proc { go! }; end
1197
+
1198
+ def assert_logged_at_error(expected_message)
1199
+ @logged.string.should include expected_message
1200
+ end
1201
+
1202
+ def assert_exits(exit_code,message='',&block)
1203
+ block.call
1204
+ fail "Expected an exit of #{exit_code}, but we didn't even exit!"
1205
+ rescue SystemExit => ex
1206
+ assert_equal exit_code,ex.status,@logged.string
1207
+ end
1208
+
1209
+ def set_argv(args)
1210
+ ARGV.clear
1211
+ args.each { |arg| ARGV << arg }
1212
+ end
1213
+ end