methadone-rehab 1.9.2
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.
- 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
|