methadone-rehab 1.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/CHANGES.md +66 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +201 -0
- data/README.rdoc +179 -0
- data/Rakefile +98 -0
- data/TODO.md +3 -0
- data/bin/methadone +157 -0
- data/features/bootstrap.feature +169 -0
- data/features/license.feature +43 -0
- data/features/multilevel_commands.feature +125 -0
- data/features/readme.feature +26 -0
- data/features/rspec_support.feature +27 -0
- data/features/step_definitions/bootstrap_steps.rb +47 -0
- data/features/step_definitions/license_steps.rb +30 -0
- data/features/step_definitions/readme_steps.rb +26 -0
- data/features/step_definitions/version_steps.rb +4 -0
- data/features/support/env.rb +26 -0
- data/features/version.feature +17 -0
- data/lib/methadone.rb +15 -0
- data/lib/methadone/argv_parser.rb +50 -0
- data/lib/methadone/cli.rb +124 -0
- data/lib/methadone/cli_logger.rb +133 -0
- data/lib/methadone/cli_logging.rb +138 -0
- data/lib/methadone/cucumber.rb +174 -0
- data/lib/methadone/error.rb +32 -0
- data/lib/methadone/execution_strategy/base.rb +34 -0
- data/lib/methadone/execution_strategy/jvm.rb +37 -0
- data/lib/methadone/execution_strategy/mri.rb +16 -0
- data/lib/methadone/execution_strategy/open_3.rb +16 -0
- data/lib/methadone/execution_strategy/open_4.rb +22 -0
- data/lib/methadone/execution_strategy/rbx_open_4.rb +12 -0
- data/lib/methadone/exit_now.rb +40 -0
- data/lib/methadone/main.rb +1039 -0
- data/lib/methadone/process_status.rb +45 -0
- data/lib/methadone/sh.rb +223 -0
- data/lib/methadone/version.rb +3 -0
- data/methadone-rehab.gemspec +32 -0
- data/templates/full/.gitignore.erb +4 -0
- data/templates/full/README.rdoc.erb +25 -0
- data/templates/full/Rakefile.erb +74 -0
- data/templates/full/_license_head.txt.erb +2 -0
- data/templates/full/apache_LICENSE.txt.erb +203 -0
- data/templates/full/bin/executable.erb +47 -0
- data/templates/full/custom_LICENSE.txt.erb +0 -0
- data/templates/full/features/executable.feature.erb +13 -0
- data/templates/full/features/step_definitions/executable_steps.rb.erb +1 -0
- data/templates/full/features/support/env.rb.erb +16 -0
- data/templates/full/gplv2_LICENSE.txt.erb +14 -0
- data/templates/full/gplv3_LICENSE.txt.erb +15 -0
- data/templates/full/mit_LICENSE.txt.erb +7 -0
- data/templates/multicommand/bin/executable.erb +52 -0
- data/templates/multicommand/lib/command.rb.erb +40 -0
- data/templates/multicommand/lib/commands.rb.erb +7 -0
- data/templates/rspec/spec/something_spec.rb.erb +5 -0
- data/templates/test_unit/test/tc_something.rb.erb +7 -0
- data/test/base_test.rb +20 -0
- data/test/command_for_tests.sh +7 -0
- data/test/execution_strategy/test_base.rb +24 -0
- data/test/execution_strategy/test_jvm.rb +77 -0
- data/test/execution_strategy/test_mri.rb +32 -0
- data/test/execution_strategy/test_open_3.rb +70 -0
- data/test/execution_strategy/test_open_4.rb +86 -0
- data/test/execution_strategy/test_rbx_open_4.rb +25 -0
- data/test/test_cli_logger.rb +219 -0
- data/test/test_cli_logging.rb +243 -0
- data/test/test_exit_now.rb +37 -0
- data/test/test_main.rb +1213 -0
- data/test/test_multi.rb +405 -0
- data/test/test_sh.rb +404 -0
- metadata +321 -0
data/test/test_multi.rb
ADDED
@@ -0,0 +1,405 @@
|
|
1
|
+
require 'base_test'
|
2
|
+
require 'methadone'
|
3
|
+
require 'stringio'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
class TestMulti < BaseTest
|
7
|
+
include Methadone::Main
|
8
|
+
include Methadone::CLILogging
|
9
|
+
|
10
|
+
module Commands
|
11
|
+
class Walk
|
12
|
+
include Methadone::Main
|
13
|
+
include Methadone::CLILogging
|
14
|
+
main do |distance|
|
15
|
+
puts "walk called"
|
16
|
+
end
|
17
|
+
options[:direction] = 0
|
18
|
+
description "moves slowly for a given distance"
|
19
|
+
on '-s', '--silly-walk'
|
20
|
+
on '-d', '--direction DIRECTION', Integer, "Compass cardinal direction"
|
21
|
+
arg "distance", "How far to walk"
|
22
|
+
|
23
|
+
end
|
24
|
+
class Run
|
25
|
+
include Methadone::Main
|
26
|
+
include Methadone::CLILogging
|
27
|
+
main do |distance, duty_cycle|
|
28
|
+
puts "run called"
|
29
|
+
end
|
30
|
+
options[:direction] = 0
|
31
|
+
description "moves quickly for a given distance"
|
32
|
+
on '-s', '--silly-walk'
|
33
|
+
on '-d', '--direction DIRECTION', Integer, "Compass cardinal direction"
|
34
|
+
arg "distance", "How far to run"
|
35
|
+
arg "duty_cycle", "Percent of time spent running (default: 100)", :optional
|
36
|
+
end
|
37
|
+
class Greet
|
38
|
+
include Methadone::Main
|
39
|
+
|
40
|
+
options[:lang] = 'es'
|
41
|
+
|
42
|
+
main do
|
43
|
+
msg = case options[:lang]
|
44
|
+
when 'en'
|
45
|
+
'Hello'
|
46
|
+
when 'fr'
|
47
|
+
'Bonjour'
|
48
|
+
when 'es'
|
49
|
+
'Hola'
|
50
|
+
else
|
51
|
+
'????'
|
52
|
+
end
|
53
|
+
|
54
|
+
msg = msg.upcase if options[:yell]
|
55
|
+
puts msg
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Say
|
60
|
+
include Methadone::Main
|
61
|
+
on '--yell', "Be loud", :global
|
62
|
+
command 'greeting' => ::TestMulti::Commands::Greet
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup
|
67
|
+
@original_argv = ARGV.clone
|
68
|
+
ARGV.clear
|
69
|
+
@old_stdout = $stdout
|
70
|
+
$stdout = StringIO.new
|
71
|
+
@logged = StringIO.new
|
72
|
+
@orig_logger = logger
|
73
|
+
@custom_logger = Logger.new(@logged)
|
74
|
+
change_logger @custom_logger
|
75
|
+
|
76
|
+
@original_home = ENV['HOME']
|
77
|
+
fake_home = '/tmp/fake-home'
|
78
|
+
FileUtils.rm_rf(fake_home)
|
79
|
+
FileUtils.mkdir(fake_home)
|
80
|
+
ENV['HOME'] = fake_home
|
81
|
+
end
|
82
|
+
|
83
|
+
def teardown
|
84
|
+
@commands = nil
|
85
|
+
change_logger @orig_logger
|
86
|
+
set_argv @original_argv
|
87
|
+
ENV.delete('DEBUG')
|
88
|
+
ENV.delete('APP_OPTS')
|
89
|
+
$stdout = @old_stdout
|
90
|
+
ENV['HOME'] = @original_home
|
91
|
+
end
|
92
|
+
|
93
|
+
test_that "commands can be specified" do
|
94
|
+
When {
|
95
|
+
command "walk" => Commands::Walk
|
96
|
+
}
|
97
|
+
Then number_of_commands_should_be(1)
|
98
|
+
Then commands_should_include("walk")
|
99
|
+
Then {
|
100
|
+
provider_for_command("walk").should be Commands::Walk
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
test_that "command providers must accept go! message" do
|
105
|
+
Given {
|
106
|
+
module Commands
|
107
|
+
class WontWork
|
108
|
+
end
|
109
|
+
end
|
110
|
+
}
|
111
|
+
When {
|
112
|
+
@error = nil
|
113
|
+
begin
|
114
|
+
command "trythis" => Commands::WontWork
|
115
|
+
rescue Exception => error
|
116
|
+
@error = error
|
117
|
+
end
|
118
|
+
}
|
119
|
+
Then number_of_commands_should_be(0)
|
120
|
+
Then {
|
121
|
+
@error.should be_a_kind_of(::Methadone::InvalidProvider)
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
test_that "command is detected in the arguments" do
|
126
|
+
Given {
|
127
|
+
main do
|
128
|
+
end
|
129
|
+
|
130
|
+
command "walk" => Commands::Walk
|
131
|
+
set_argv %w(walk 10)
|
132
|
+
}
|
133
|
+
When run_go_safely
|
134
|
+
Then {
|
135
|
+
opts.selected_command.should eq('walk')
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
test_that "command in the arguments causes the right command to be called" do
|
140
|
+
Given app_has_subcommands('walk','run')
|
141
|
+
And {
|
142
|
+
version '1.2.3'
|
143
|
+
set_argv %w(walk 10)
|
144
|
+
}
|
145
|
+
When run_go_safely
|
146
|
+
Then {
|
147
|
+
opts.command_names.should include('walk')
|
148
|
+
opts.command_names.should include('run')
|
149
|
+
$stdout.string.should match(/walk called/)
|
150
|
+
}
|
151
|
+
And number_of_commands_should_be(2)
|
152
|
+
end
|
153
|
+
|
154
|
+
test_that "help is displayed if no command on command line" do
|
155
|
+
Given app_has_subcommands('walk','run')
|
156
|
+
And {
|
157
|
+
@main_called = false
|
158
|
+
main do
|
159
|
+
@main_called = true
|
160
|
+
puts 'main called'
|
161
|
+
end
|
162
|
+
}
|
163
|
+
When run_go_safely
|
164
|
+
Then main_should_not_be_called
|
165
|
+
And help_shown
|
166
|
+
And {
|
167
|
+
assert_logged_at_error("You must specify a command")
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
test_that "app with subcommands list subcommands in help" do
|
172
|
+
Given app_has_subcommands('walk','run')
|
173
|
+
When {
|
174
|
+
setup_defaults
|
175
|
+
opts.post_setup
|
176
|
+
}
|
177
|
+
Then {
|
178
|
+
opts.to_s.should match /(?m)Commands:\n.*walk: moves slowly/
|
179
|
+
opts.to_s.should match /(?m)Commands:\n.*run: moves quickly/
|
180
|
+
}
|
181
|
+
And {
|
182
|
+
opts.to_s.should match /Usage:.*command \[command options and args...\]/
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
test_that "app without subcommands do not list command prefix in help" do
|
187
|
+
Given {
|
188
|
+
main do
|
189
|
+
end
|
190
|
+
on '--switch'
|
191
|
+
on '--flag FOO'
|
192
|
+
arg 'must_have'
|
193
|
+
arg 'optionals', :any
|
194
|
+
}
|
195
|
+
When {
|
196
|
+
setup_defaults
|
197
|
+
opts.post_setup
|
198
|
+
}
|
199
|
+
Then {
|
200
|
+
opts.to_s.should_not match /Commands:/m
|
201
|
+
}
|
202
|
+
end
|
203
|
+
|
204
|
+
test_that "subcommand can get its own help" do
|
205
|
+
Given app_has_subcommands('walk','run')
|
206
|
+
And {
|
207
|
+
version '1.2.3'
|
208
|
+
set_argv %w(walk -h)
|
209
|
+
}
|
210
|
+
When run_go_safely
|
211
|
+
Then {
|
212
|
+
$stdout.string.should match /Usage: #{::File.basename($0)} walk \[options\] distance/
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
someday_test_that "rc_file can specify defaults for each subcommand" do
|
217
|
+
end
|
218
|
+
|
219
|
+
test_that "subcommand help shows global options from parent" do
|
220
|
+
Given app_has_subcommands('walk','run')
|
221
|
+
And {
|
222
|
+
version '1.2.3'
|
223
|
+
set_argv %w(walk -h)
|
224
|
+
on '-w','--wow', :global, "This is a global option"
|
225
|
+
}
|
226
|
+
When run_go_safely
|
227
|
+
Then {
|
228
|
+
$stdout.string.should match /Usage: #{::File.basename($0)} \[global options\] walk \[options\] distance/
|
229
|
+
$stdout.string.should match /(?m)Global options:\n.*-w, --wow *This is a global option/
|
230
|
+
$stdout.string.should_not match /(?m)Global options:\n.*-v, --version/
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
test_that "subcommands have access to global options" do
|
236
|
+
Given app_has_subcommands('greet')
|
237
|
+
And {
|
238
|
+
options[:lang] = 'en'
|
239
|
+
on '-l', '--lang LANG','Set the language', :global
|
240
|
+
set_argv %w(-l fr greet)
|
241
|
+
}
|
242
|
+
When run_go_safely
|
243
|
+
Then {
|
244
|
+
$stdout.string.should match /Bonjour/
|
245
|
+
$stdout.string.should_not match /Hello/
|
246
|
+
$stdout.string.should_not match /Hola/
|
247
|
+
$stdout.string.should_not match /\?\?\?\?/
|
248
|
+
}
|
249
|
+
end
|
250
|
+
|
251
|
+
test_that "subcommands of subcommands help shows parents global options" do
|
252
|
+
Given app_is_three_layers_deep_with_middle_layer_having_global_options
|
253
|
+
And {
|
254
|
+
set_argv %w(say --yell greeting)
|
255
|
+
}
|
256
|
+
When run_go_safely
|
257
|
+
Then {
|
258
|
+
cmd_opts = opts.commands['say'].opts.commands['greeting'].opts
|
259
|
+
cmd_opts.to_s.should match /say \[options [f]or say\] greeting/
|
260
|
+
cmd_opts.to_s.should match /(?m)Options [f]or say:\n.*--yell *Be loud/
|
261
|
+
cmd_opts.to_s.should_not match /\[global options\]/
|
262
|
+
cmd_opts.to_s.should_not match /Global options:/
|
263
|
+
$stdout.string.should match /HOLA/
|
264
|
+
}
|
265
|
+
end
|
266
|
+
|
267
|
+
test_that "subcommands of subcommands help shows parents global options and base global options" do
|
268
|
+
Given app_is_three_layers_deep_with_middle_layer_having_global_options
|
269
|
+
And {
|
270
|
+
on '-l', '--lang LANG','Set the language', :global
|
271
|
+
set_argv %w(-l en say --yell greeting)
|
272
|
+
}
|
273
|
+
When run_go_safely
|
274
|
+
Then {
|
275
|
+
cmd_opts = opts.commands['say'].opts.commands['greeting'].opts
|
276
|
+
cmd_opts.to_s.should match /\[global options\] say \[options [f]or say\] greeting/
|
277
|
+
cmd_opts.to_s.should match /(?m)Options [f]or say:\n.*--yell *Be loud/
|
278
|
+
cmd_opts.to_s.should match /(?m)Global options:\n.*-l, --lang LANG *Set the language/
|
279
|
+
$stdout.string.should match /HELLO/
|
280
|
+
}
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
def commands_should_include(cmd)
|
286
|
+
proc { opts.commands.keys.should include(cmd) }
|
287
|
+
end
|
288
|
+
|
289
|
+
def number_of_commands_should_be(num)
|
290
|
+
proc { opts.commands.keys.length.should be(num)}
|
291
|
+
end
|
292
|
+
|
293
|
+
def provider_for_command(cmd)
|
294
|
+
opts.commands[cmd]
|
295
|
+
end
|
296
|
+
|
297
|
+
def app_has_subcommands(*args)
|
298
|
+
proc {
|
299
|
+
args.each do |cmd|
|
300
|
+
command cmd => get_const("TestMulti::Commands::#{cmd.capitalize}")
|
301
|
+
end
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
def app_is_three_layers_deep_with_middle_layer_having_global_options
|
306
|
+
proc {
|
307
|
+
# Requires special resetting to ensure proper behaviour
|
308
|
+
reset!
|
309
|
+
command 'say' => get_const("TestMulti::Commands::Say")
|
310
|
+
opts.commands['say'].instance_variable_get(:@options).delete_if {|k,v| true}
|
311
|
+
opts.commands['say'].opts.commands['greeting'].instance_variable_get(:@options).delete_if {|k,v| true}
|
312
|
+
opts.commands['say'].opts.commands['greeting'].instance_variable_get(:@options)[:lang] = 'es'
|
313
|
+
}
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
def help_shown
|
318
|
+
proc {assert $stdout.string.include?(opts.to_s),"Expected #{$stdout.string} to contain #{opts.to_s}"}
|
319
|
+
end
|
320
|
+
|
321
|
+
def app_to_use_rc_file
|
322
|
+
lambda {
|
323
|
+
@switch = nil
|
324
|
+
@flag = nil
|
325
|
+
@args = nil
|
326
|
+
main do |*args|
|
327
|
+
@switch = options[:switch]
|
328
|
+
@flag = options[:flag]
|
329
|
+
@args = args
|
330
|
+
end
|
331
|
+
|
332
|
+
defaults_from_config_file '.my_app.rc'
|
333
|
+
|
334
|
+
on('--switch','Some Switch')
|
335
|
+
on('--flag FOO','Some Flag')
|
336
|
+
}
|
337
|
+
end
|
338
|
+
|
339
|
+
def main_that_exits(exit_status)
|
340
|
+
proc { main { exit_status } }
|
341
|
+
end
|
342
|
+
|
343
|
+
def app_to_use_environment
|
344
|
+
lambda {
|
345
|
+
@switch = nil
|
346
|
+
@flag = nil
|
347
|
+
@args = nil
|
348
|
+
main do |*args|
|
349
|
+
@switch = options[:switch]
|
350
|
+
@flag = options[:flag]
|
351
|
+
@args = args
|
352
|
+
end
|
353
|
+
|
354
|
+
defaults_from_env_var 'APP_OPTS'
|
355
|
+
|
356
|
+
on('--switch','Some Switch')
|
357
|
+
on('--flag FOO','Some Flag')
|
358
|
+
}
|
359
|
+
end
|
360
|
+
|
361
|
+
def main_should_not_be_called
|
362
|
+
Proc.new { assert !@main_called,"Main block was called?!" }
|
363
|
+
end
|
364
|
+
|
365
|
+
def main_shouldve_been_called
|
366
|
+
Proc.new { assert @main_called,"Main block wasn't called?!" }
|
367
|
+
end
|
368
|
+
|
369
|
+
def run_go_safely
|
370
|
+
Proc.new { safe_go! }
|
371
|
+
end
|
372
|
+
|
373
|
+
# Calls go!, but traps the exit
|
374
|
+
def safe_go!
|
375
|
+
go!
|
376
|
+
rescue SystemExit
|
377
|
+
end
|
378
|
+
|
379
|
+
def run_go!; proc { go! }; end
|
380
|
+
|
381
|
+
def assert_logged_at_error(expected_message)
|
382
|
+
@logged.string.should include expected_message
|
383
|
+
end
|
384
|
+
|
385
|
+
def assert_exits(exit_code,message='',&block)
|
386
|
+
block.call
|
387
|
+
fail "Expected an exit of #{exit_code}, but we didn't even exit!"
|
388
|
+
rescue SystemExit => ex
|
389
|
+
assert_equal exit_code,ex.status,@logged.string
|
390
|
+
end
|
391
|
+
|
392
|
+
def set_argv(args)
|
393
|
+
ARGV.clear
|
394
|
+
args.each { |arg| ARGV << arg }
|
395
|
+
end
|
396
|
+
|
397
|
+
def get_const(class_name)
|
398
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ class_name
|
399
|
+
raise NameError, "#{class_name.inspect} is not a valid constant name!"
|
400
|
+
end
|
401
|
+
|
402
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
403
|
+
end
|
404
|
+
|
405
|
+
end
|
data/test/test_sh.rb
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
require 'base_test'
|
2
|
+
require 'methadone'
|
3
|
+
|
4
|
+
class TestSH < BaseTest
|
5
|
+
include Methadone::SH
|
6
|
+
include Methadone::CLILogging
|
7
|
+
|
8
|
+
class CapturingLogger
|
9
|
+
attr_reader :debugs, :infos, :warns, :errors, :fatals
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@debugs = []
|
13
|
+
@infos = []
|
14
|
+
@warns = []
|
15
|
+
@errors = []
|
16
|
+
@fatals = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def debug(msg); @debugs << msg; end
|
20
|
+
def info(msg); @infos << msg; end
|
21
|
+
def warn(msg); @warns << msg; end
|
22
|
+
def error(msg)
|
23
|
+
# Try to figure out what's going on on Travis
|
24
|
+
STDERR.puts msg if RUBY_PLATFORM == 'java'
|
25
|
+
@errors << msg
|
26
|
+
end
|
27
|
+
def fatal(msg); @fatals << msg; end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
[:sh,:sh!].each do |method|
|
32
|
+
test_that "#{method} runs a successful command and logs about it" do
|
33
|
+
Given {
|
34
|
+
use_capturing_logger
|
35
|
+
@command = test_command
|
36
|
+
}
|
37
|
+
When {
|
38
|
+
@exit_code = self.send(method,@command)
|
39
|
+
}
|
40
|
+
Then {
|
41
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
test_that "#{method}, when the command succeeds and given a block of one argument, gives that block the stdout" do
|
46
|
+
Given {
|
47
|
+
use_capturing_logger
|
48
|
+
@command = test_command
|
49
|
+
@stdout_received = nil
|
50
|
+
}
|
51
|
+
When {
|
52
|
+
@exit_code = self.send(method,@command) do |stdout|
|
53
|
+
@stdout_received = stdout
|
54
|
+
end
|
55
|
+
}
|
56
|
+
Then {
|
57
|
+
@stdout_received.should == test_command_stdout
|
58
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
test_that "#{method}, when the command succeeds and given a block of zero arguments, calls the block" do
|
63
|
+
Given {
|
64
|
+
use_capturing_logger
|
65
|
+
@command = test_command
|
66
|
+
@block_called = false
|
67
|
+
}
|
68
|
+
When {
|
69
|
+
@exit_code = self.send(method,@command) do
|
70
|
+
@block_called = true
|
71
|
+
end
|
72
|
+
}
|
73
|
+
Then {
|
74
|
+
@block_called.should == true
|
75
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
test_that "#{method}, when the command succeeds and given a lambda of zero arguments, calls the lambda" do
|
80
|
+
Given {
|
81
|
+
use_capturing_logger
|
82
|
+
@command = test_command
|
83
|
+
@block_called = false
|
84
|
+
@lambda = lambda { @block_called = true }
|
85
|
+
}
|
86
|
+
When {
|
87
|
+
@exit_code = self.send(method,@command,&@lambda)
|
88
|
+
}
|
89
|
+
Then {
|
90
|
+
@block_called.should == true
|
91
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
test_that "#{method}, when the command succeeds and given a block of two arguments, calls the block with the stdout and stderr" do
|
96
|
+
Given {
|
97
|
+
use_capturing_logger
|
98
|
+
@command = test_command
|
99
|
+
@block_called = false
|
100
|
+
@stdout_received = nil
|
101
|
+
@stderr_received = nil
|
102
|
+
}
|
103
|
+
When {
|
104
|
+
@exit_code = self.send(method,@command) do |stdout,stderr|
|
105
|
+
@stdout_received = stdout
|
106
|
+
@stderr_received = stderr
|
107
|
+
end
|
108
|
+
}
|
109
|
+
Then {
|
110
|
+
@stdout_received.should == test_command_stdout
|
111
|
+
@stderr_received.length.should == 0
|
112
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
test_that "#{method}, when the command succeeds and given a block of three arguments, calls the block with the stdout, stderr, and exit code" do
|
117
|
+
Given {
|
118
|
+
use_capturing_logger
|
119
|
+
@command = test_command
|
120
|
+
@block_called = false
|
121
|
+
@stdout_received = nil
|
122
|
+
@stderr_received = nil
|
123
|
+
@exitstatus_received = nil
|
124
|
+
}
|
125
|
+
When {
|
126
|
+
@exit_code = self.send(method,@command) do |stdout,stderr,exitstatus|
|
127
|
+
@stdout_received = stdout
|
128
|
+
@stderr_received = stderr
|
129
|
+
@exitstatus_received = exitstatus
|
130
|
+
end
|
131
|
+
}
|
132
|
+
Then {
|
133
|
+
@stdout_received.should == test_command_stdout
|
134
|
+
@stderr_received.length.should == 0
|
135
|
+
@exitstatus_received.should == 0
|
136
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
137
|
+
}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
test_that "sh, when the command fails and given a block, doesn't call the block" do
|
142
|
+
Given {
|
143
|
+
use_capturing_logger
|
144
|
+
@command = test_command("foo")
|
145
|
+
@block_called = false
|
146
|
+
}
|
147
|
+
When {
|
148
|
+
@exit_code = sh @command do
|
149
|
+
@block_called = true
|
150
|
+
end
|
151
|
+
}
|
152
|
+
Then {
|
153
|
+
@exit_code.should == 1
|
154
|
+
assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
test_that "sh, when the command fails with an unexpected status, and given a block, doesn't call the block" do
|
159
|
+
Given {
|
160
|
+
use_capturing_logger
|
161
|
+
@command = test_command("foo")
|
162
|
+
@block_called = false
|
163
|
+
}
|
164
|
+
When {
|
165
|
+
@exit_code = sh @command, :expected => [2] do
|
166
|
+
@block_called = true
|
167
|
+
end
|
168
|
+
}
|
169
|
+
Then {
|
170
|
+
@exit_code.should == 1
|
171
|
+
assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
[1,[1],[1,2]].each do |expected|
|
176
|
+
[:sh,:sh!].each do |method|
|
177
|
+
test_that "#{method}, when the command fails with an expected error code (using syntax #{expected}/#{expected.class}), treats it as success" do
|
178
|
+
Given {
|
179
|
+
use_capturing_logger
|
180
|
+
@command = test_command("foo")
|
181
|
+
@block_called = false
|
182
|
+
@exitstatus_received = nil
|
183
|
+
}
|
184
|
+
When {
|
185
|
+
@exit_code = self.send(method,@command,:expected => expected) do |_,_,exitstatus|
|
186
|
+
@block_called = true
|
187
|
+
@exitstatus_received = exitstatus
|
188
|
+
end
|
189
|
+
}
|
190
|
+
Then {
|
191
|
+
@exit_code.should == 1
|
192
|
+
@block_called.should == true
|
193
|
+
@exitstatus_received.should == 1
|
194
|
+
@logger.debugs[0].should == "Executing '#{test_command}foo'"
|
195
|
+
@logger.debugs[1].should == "stdout output of '#{test_command}foo': #{test_command_stdout}"
|
196
|
+
@logger.warns[0].should == "stderr output of '#{test_command}foo': #{test_command_stderr}"
|
197
|
+
}
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
test_that "sh runs a command that will fail and logs about it" do
|
203
|
+
Given {
|
204
|
+
use_capturing_logger
|
205
|
+
@command = test_command("foo")
|
206
|
+
}
|
207
|
+
When {
|
208
|
+
@exit_code = sh @command
|
209
|
+
}
|
210
|
+
Then {
|
211
|
+
@exit_code.should == 1
|
212
|
+
assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
test_that "sh runs a non-existent command that will fail and logs about it" do
|
217
|
+
Given {
|
218
|
+
use_capturing_logger
|
219
|
+
@command = "asdfasdfasdfas"
|
220
|
+
}
|
221
|
+
When {
|
222
|
+
@exit_code = sh @command
|
223
|
+
}
|
224
|
+
Then {
|
225
|
+
@exit_code.should == 127 # consistent with what bash does
|
226
|
+
@logger.errors[0].should match /^Error running '#{@command}': .+$/
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
test_that "sh! runs a command that will fail and logs about it, but throws an exception" do
|
231
|
+
Given {
|
232
|
+
use_capturing_logger
|
233
|
+
@command = test_command("foo")
|
234
|
+
}
|
235
|
+
When {
|
236
|
+
@code = lambda { sh! @command }
|
237
|
+
}
|
238
|
+
Then {
|
239
|
+
exception = assert_raises(Methadone::FailedCommandError,&@code)
|
240
|
+
exception.command.should == @command
|
241
|
+
assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
|
242
|
+
}
|
243
|
+
end
|
244
|
+
|
245
|
+
test_that "sh! runs a command that will fail and includes an error message that appears in the exception" do
|
246
|
+
Given {
|
247
|
+
use_capturing_logger
|
248
|
+
@command = test_command("foo")
|
249
|
+
@custom_error_message = any_sentence
|
250
|
+
}
|
251
|
+
When {
|
252
|
+
@code = lambda { sh! @command, :on_fail => @custom_error_message }
|
253
|
+
}
|
254
|
+
Then {
|
255
|
+
exception = assert_raises(Methadone::FailedCommandError,&@code)
|
256
|
+
exception.command.should == @command
|
257
|
+
exception.message.should == @custom_error_message
|
258
|
+
assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
class MyTestApp
|
263
|
+
include Methadone::SH
|
264
|
+
def initialize(logger=nil)
|
265
|
+
set_sh_logger(logger) if logger
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
test_that "when we don't have CLILogging included, we can still provide our own logger" do
|
270
|
+
Given {
|
271
|
+
@logger = CapturingLogger.new
|
272
|
+
@test_app = MyTestApp.new(@logger)
|
273
|
+
@command = test_command
|
274
|
+
}
|
275
|
+
When {
|
276
|
+
@exit_code = @test_app.sh @command
|
277
|
+
}
|
278
|
+
Then {
|
279
|
+
assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
|
280
|
+
}
|
281
|
+
end
|
282
|
+
|
283
|
+
test_that "when we don't have CLILogging included and fail to provide a logger, an exception is thrown" do
|
284
|
+
Given {
|
285
|
+
@test_app = MyTestApp.new
|
286
|
+
@command = test_command
|
287
|
+
}
|
288
|
+
When {
|
289
|
+
@code = lambda { @test_app.sh @command }
|
290
|
+
}
|
291
|
+
Then {
|
292
|
+
exception = assert_raises(StandardError,&@code)
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
class MyExecutionStrategy
|
297
|
+
include Clean::Test::Any
|
298
|
+
attr_reader :command
|
299
|
+
|
300
|
+
def initialize(exitcode)
|
301
|
+
@exitcode = exitcode
|
302
|
+
@command = nil
|
303
|
+
end
|
304
|
+
|
305
|
+
def run_command(command)
|
306
|
+
@command = command
|
307
|
+
if @exitcode.kind_of? Fixnum
|
308
|
+
[any_string,any_string,OpenStruct.new(:exitstatus => @exitcode)]
|
309
|
+
else
|
310
|
+
[any_string,any_string,@exitcode]
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def exception_meaning_command_not_found
|
315
|
+
RuntimeError
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
class MyExecutionStrategyApp
|
320
|
+
include Methadone::CLILogging
|
321
|
+
include Methadone::SH
|
322
|
+
|
323
|
+
attr_reader :strategy
|
324
|
+
|
325
|
+
def initialize(exit_code)
|
326
|
+
@strategy = MyExecutionStrategy.new(exit_code)
|
327
|
+
set_execution_strategy(@strategy)
|
328
|
+
set_sh_logger(CapturingLogger.new)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
test_that "when I provide a custom execution strategy, it gets used" do
|
333
|
+
Given {
|
334
|
+
@exit_code = any_int :min => 0, :max => 127
|
335
|
+
@app = MyExecutionStrategyApp.new(@exit_code)
|
336
|
+
@command = "ls"
|
337
|
+
}
|
338
|
+
When {
|
339
|
+
@results = @app.sh(@command)
|
340
|
+
}
|
341
|
+
Then {
|
342
|
+
@app.strategy.command.should == @command
|
343
|
+
@results.should == @exit_code
|
344
|
+
}
|
345
|
+
end
|
346
|
+
|
347
|
+
test_that "when the execution strategy returns a non-int, but truthy value, it gets coerced into a 0" do
|
348
|
+
Given {
|
349
|
+
@app = MyExecutionStrategyApp.new(true)
|
350
|
+
@command = "ls"
|
351
|
+
}
|
352
|
+
When {
|
353
|
+
@results = @app.sh(@command)
|
354
|
+
}
|
355
|
+
Then {
|
356
|
+
@app.strategy.command.should == @command
|
357
|
+
@results.should == 0
|
358
|
+
}
|
359
|
+
end
|
360
|
+
|
361
|
+
test_that "when the execution strategy returns a non-int, but falsey value, it gets coerced into a 1" do
|
362
|
+
Given {
|
363
|
+
@app = MyExecutionStrategyApp.new(false)
|
364
|
+
@command = "ls"
|
365
|
+
}
|
366
|
+
When {
|
367
|
+
@results = @app.sh(@command)
|
368
|
+
}
|
369
|
+
Then {
|
370
|
+
@app.strategy.command.should == @command
|
371
|
+
@results.should == 1
|
372
|
+
}
|
373
|
+
end
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
def assert_successful_command_execution(exit_code,logger,command,stdout)
|
378
|
+
exit_code.should == 0
|
379
|
+
logger.debugs[0].should == "Executing '#{command}'"
|
380
|
+
logger.debugs[1].should == "stdout output of '#{command}': #{stdout}"
|
381
|
+
logger.warns.length.should == 0
|
382
|
+
end
|
383
|
+
|
384
|
+
def assert_logger_output_for_failure(logger,command,stdout,stderr)
|
385
|
+
logger.debugs[0].should == "Executing '#{command}'"
|
386
|
+
logger.infos[0].should == "stdout output of '#{command}': #{stdout}"
|
387
|
+
logger.warns[0].should == "stderr output of '#{command}': #{stderr}"
|
388
|
+
logger.warns[1].should == "Error running '#{command}'"
|
389
|
+
end
|
390
|
+
|
391
|
+
def use_capturing_logger
|
392
|
+
@logger = CapturingLogger.new
|
393
|
+
change_logger(@logger)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Runs the test command which exits with the length of ARGV/args
|
397
|
+
def test_command(args='')
|
398
|
+
File.join(File.expand_path(File.dirname(__FILE__)),'command_for_tests.sh') + ' ' + args
|
399
|
+
end
|
400
|
+
|
401
|
+
def test_command_stdout; "standard output"; end
|
402
|
+
def test_command_stderr; "standard error"; end
|
403
|
+
|
404
|
+
end
|