methadone 0.5.1 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Methadone
2
- VERSION = "0.5.1"
2
+ VERSION = "1.0.0.rc1"
3
3
  end
data/methadone.gemspec CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency("aruba")
27
27
  s.add_development_dependency("simplecov", "~> 0.5")
28
28
  s.add_development_dependency("clean_test", "~> 0.10")
29
+ s.add_development_dependency("mocha")
29
30
  end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ STDOUT.puts "standard output"
4
+ STDERR.puts "standard error" if ARGV.length > 0
5
+ exit ARGV.length
@@ -0,0 +1,24 @@
1
+ require 'base_test'
2
+
3
+ module ExecutionStrategy
4
+ class TestBase < BaseTest
5
+ include Methadone::ExecutionStrategy
6
+
7
+ [
8
+ [:run_command,["ls"]],
9
+ [:exception_meaning_command_not_found,[]],
10
+ ].each do |(method,args)|
11
+ test_that "#{method} isn't implemented" do
12
+ Given {
13
+ @strategy = Base.new
14
+ }
15
+ When {
16
+ @code = lambda { @strategy.send(method,*args) }
17
+ }
18
+ Then {
19
+ assert_raises(RuntimeError,&@code)
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,77 @@
1
+ require 'base_test'
2
+ require 'mocha'
3
+
4
+ # Defined by JRuby, but this test must pass on any Ruby
5
+ class NativeException
6
+ end
7
+
8
+ module ExecutionStrategy
9
+ class TestJVM < BaseTest
10
+ include Methadone::ExecutionStrategy
11
+
12
+ test_that "run_command proxies to Open3.capture3" do
13
+ Given {
14
+ @process = mock('java.lang.Process')
15
+ @runtime = mock('java.lang.Runtime')
16
+ @command = "ls"
17
+ @stdout = any_string
18
+ @stderr = any_string
19
+ @exitstatus = any_int :min => 1, :max => 127
20
+ }
21
+ When the_test_runs
22
+ Then {
23
+ expects_lang = mock()
24
+ JVM.any_instance.expects(:java).returns(expects_lang)
25
+ expects_Runtime = mock()
26
+ expects_lang.expects(:lang).returns(expects_Runtime)
27
+ runtime_klass = mock()
28
+ expects_Runtime.expects(:Runtime).returns(runtime_klass)
29
+ runtime_klass.expects(:get_runtime).returns(@runtime)
30
+ @runtime.expects(:exec).with(@command).returns(@process)
31
+
32
+ stdin = mock()
33
+ @process.expects(:get_output_stream).returns(stdin)
34
+ stdin.expects(:close)
35
+
36
+ stdout_input_stream = mock('InputStream')
37
+ @process.expects(:get_input_stream).returns(stdout_input_stream)
38
+ stdout_input_stream.expects(:read).times(2).returns(
39
+ @stdout,
40
+ -1)
41
+
42
+ stderr_input_stream = mock('InputStream')
43
+ @process.expects(:get_error_stream).returns(stderr_input_stream)
44
+ stderr_input_stream.expects(:read).times(2).returns(
45
+ @stderr,
46
+ -1)
47
+
48
+ @process.expects(:wait_for).returns(@exitstatus)
49
+ }
50
+
51
+ Given new_jvm_strategy
52
+ When {
53
+ @results = @strategy.run_command(@command)
54
+ }
55
+ Then {
56
+ @results[0].should == @stdout
57
+ @results[1].should == @stderr
58
+ @results[2].exitstatus.should == @exitstatus
59
+ }
60
+ end
61
+
62
+ test_that "exception_meaning_command_not_found returns NativeException" do
63
+ Given new_jvm_strategy
64
+ When {
65
+ @klass = @strategy.exception_meaning_command_not_found
66
+ }
67
+ Then {
68
+ @klass.should == NativeException
69
+ }
70
+ end
71
+
72
+ private
73
+ def new_jvm_strategy
74
+ lambda { @strategy = JVM.new }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,32 @@
1
+ require 'base_test'
2
+
3
+ module ExecutionStrategy
4
+ class TestMRI < BaseTest
5
+ include Methadone::ExecutionStrategy
6
+
7
+ test_that "run_command isn't implemented" do
8
+ Given new_mri_strategy
9
+ When {
10
+ @code = lambda { @strategy.run_command("ls") }
11
+ }
12
+ Then {
13
+ assert_raises(RuntimeError,&@code)
14
+ }
15
+ end
16
+
17
+ test_that "exception_meaning_command_not_found returns Errno::ENOENT" do
18
+ Given new_mri_strategy
19
+ When {
20
+ @klass = @strategy.exception_meaning_command_not_found
21
+ }
22
+ Then {
23
+ @klass.should == Errno::ENOENT
24
+ }
25
+ end
26
+
27
+ private
28
+ def new_mri_strategy
29
+ lambda { @strategy = MRI.new }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,47 @@
1
+ require 'base_test'
2
+ require 'mocha'
3
+ require 'open3'
4
+
5
+ module ExecutionStrategy
6
+ class TestOpen_3 < BaseTest
7
+ include Methadone::ExecutionStrategy
8
+
9
+ test_that "run_command proxies to Open3.capture3" do
10
+ Given {
11
+ @command = any_string
12
+ @stdout = any_string
13
+ @stderr = any_string
14
+ @status = stub('Process::Status')
15
+ }
16
+ When the_test_runs
17
+ Then {
18
+ Open3.expects(:capture3).with(@command).returns([@stdout,@stderr,@status])
19
+ }
20
+
21
+ Given new_open_3_strategy
22
+ When {
23
+ @results = @strategy.run_command(@command)
24
+ }
25
+ Then {
26
+ @results[0].should == @stdout
27
+ @results[1].should == @stderr
28
+ @results[2].should be @status
29
+ }
30
+ end
31
+
32
+ test_that "exception_meaning_command_not_found returns Errno::ENOENT" do
33
+ Given new_open_3_strategy
34
+ When {
35
+ @klass = @strategy.exception_meaning_command_not_found
36
+ }
37
+ Then {
38
+ @klass.should == Errno::ENOENT
39
+ }
40
+ end
41
+
42
+ private
43
+ def new_open_3_strategy
44
+ lambda { @strategy = Open_3.new }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,57 @@
1
+ require 'base_test'
2
+ require 'mocha'
3
+
4
+ # Define this symbol without requiring the library;
5
+ # all we're goingn to do is mock calls to it
6
+ module Open4
7
+ end
8
+
9
+ module ExecutionStrategy
10
+ class TestOpen_4 < BaseTest
11
+ include Methadone::ExecutionStrategy
12
+
13
+ test_that "run_command proxies to Open4.capture4" do
14
+ Given {
15
+ @command = any_string
16
+ @stdin_io = mock("IO")
17
+ @stdout = any_string
18
+ @stdout_io = StringIO.new(@stdout)
19
+ @stderr = any_string
20
+ @stderr_io = StringIO.new(@stderr)
21
+ @pid = any_int :min => 2, :max => 65536
22
+ @status = stub('Process::Status')
23
+ }
24
+ When the_test_runs
25
+ Then {
26
+ Open4.expects(:popen4).with(@command).returns([@pid,@stdin_io,@stdout_io,@stderr_io])
27
+ @stdin_io.expects(:close)
28
+ Process.expects(:waitpid2).with(@pid).returns([any_string,@status])
29
+ }
30
+
31
+ Given new_open_4_strategy
32
+ When {
33
+ @results = @strategy.run_command(@command)
34
+ }
35
+ Then {
36
+ @results[0].should == @stdout
37
+ @results[1].should == @stderr
38
+ @results[2].should be @status
39
+ }
40
+ end
41
+
42
+ test_that "exception_meaning_command_not_found returns Errno::ENOENT" do
43
+ Given new_open_4_strategy
44
+ When {
45
+ @klass = @strategy.exception_meaning_command_not_found
46
+ }
47
+ Then {
48
+ @klass.should == Errno::ENOENT
49
+ }
50
+ end
51
+
52
+ private
53
+ def new_open_4_strategy
54
+ lambda { @strategy = Open_4.new }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,25 @@
1
+ require 'base_test'
2
+ require 'mocha'
3
+
4
+ # Define this symbol without requiring the library;
5
+ # all we're goingn to do is mock calls to it
6
+ module Open4
7
+ end
8
+
9
+ module ExecutionStrategy
10
+ class TestRBXOpen_4 < BaseTest
11
+ include Methadone::ExecutionStrategy
12
+
13
+ test_that "exception_meaning_command_not_found returns Errno::EINVAL" do
14
+ Given {
15
+ @strategy = RBXOpen_4.new
16
+ }
17
+ When {
18
+ @klass = @strategy.exception_meaning_command_not_found
19
+ }
20
+ Then {
21
+ @klass.should == Errno::EINVAL
22
+ }
23
+ end
24
+ end
25
+ end
data/test/test_sh.rb ADDED
@@ -0,0 +1,269 @@
1
+ require 'test/unit'
2
+ require 'clean_test/test_case'
3
+
4
+ class TestSH < Clean::Test::TestCase
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); @errors << msg; end
23
+ def fatal(msg); @fatals << msg; end
24
+
25
+ end
26
+
27
+ [:sh,:sh!].each do |method|
28
+ test_that "#{method} runs a successful command and logs about it" do
29
+ Given {
30
+ use_capturing_logger
31
+ @command = test_command
32
+ }
33
+ When {
34
+ @exit_code = self.send(method,@command)
35
+ }
36
+ Then {
37
+ assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
38
+ }
39
+ end
40
+
41
+ test_that "#{method}, when the command succeeds and given a block of one argument, gives that block the stdout" do
42
+ Given {
43
+ use_capturing_logger
44
+ @command = test_command
45
+ @stdout_received = nil
46
+ }
47
+ When {
48
+ @exit_code = self.send(method,@command) do |stdout|
49
+ @stdout_received = stdout
50
+ end
51
+ }
52
+ Then {
53
+ @stdout_received.should == test_command_stdout
54
+ assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
55
+ }
56
+ end
57
+
58
+ test_that "#{method}, when the command succeeds and given a block of zero arguments, calls the block" do
59
+ Given {
60
+ use_capturing_logger
61
+ @command = test_command
62
+ @block_called = false
63
+ }
64
+ When {
65
+ @exit_code = self.send(method,@command) do
66
+ @block_called = true
67
+ end
68
+ }
69
+ Then {
70
+ @block_called.should == true
71
+ assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
72
+ }
73
+ end
74
+
75
+ test_that "#{method}, when the command succeeds and given a lambda of zero arguments, calls the lambda" do
76
+ Given {
77
+ use_capturing_logger
78
+ @command = test_command
79
+ @block_called = false
80
+ @lambda = lambda { @block_called = true }
81
+ }
82
+ When {
83
+ @exit_code = self.send(method,@command,&@lambda)
84
+ }
85
+ Then {
86
+ @block_called.should == true
87
+ assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
88
+ }
89
+ end
90
+
91
+ test_that "#{method}, when the command succeeds and given a block of two arguments, calls the block with the stdout and stderr" do
92
+ Given {
93
+ use_capturing_logger
94
+ @command = test_command
95
+ @block_called = false
96
+ @stdout_received = nil
97
+ @stderr_received = nil
98
+ }
99
+ When {
100
+ @exit_code = self.send(method,@command) do |stdout,stderr|
101
+ @stdout_received = stdout
102
+ @stderr_received = stderr
103
+ end
104
+ }
105
+ Then {
106
+ @stdout_received.should == test_command_stdout
107
+ @stderr_received.length.should == 0
108
+ assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
109
+ }
110
+ end
111
+ end
112
+
113
+ test_that "sh, when the command fails and given a block, doesn't call the block" do
114
+ Given {
115
+ use_capturing_logger
116
+ @command = test_command("foo")
117
+ @block_called = false
118
+ }
119
+ When {
120
+ @exit_code = sh @command do
121
+ @block_called = true
122
+ end
123
+ }
124
+ Then {
125
+ @exit_code.should == 1
126
+ assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
127
+ }
128
+ end
129
+
130
+ test_that "sh runs a command that will fail and logs about it" do
131
+ Given {
132
+ use_capturing_logger
133
+ @command = test_command("foo")
134
+ }
135
+ When {
136
+ @exit_code = sh @command
137
+ }
138
+ Then {
139
+ @exit_code.should == 1
140
+ assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
141
+ }
142
+ end
143
+
144
+ test_that "sh runs a non-existent command that will fail and logs about it" do
145
+ Given {
146
+ use_capturing_logger
147
+ @command = "asdfasdfasdfas"
148
+ }
149
+ When {
150
+ @exit_code = sh @command
151
+ }
152
+ Then {
153
+ @exit_code.should == 127 # consistent with what bash does
154
+ @logger.errors[0].should match /^Error running '#{@command}': .+$/
155
+ }
156
+ end
157
+
158
+ test_that "sh! runs a command that will fail and logs about it, but throws an exception" do
159
+ Given {
160
+ use_capturing_logger
161
+ @command = test_command("foo")
162
+ }
163
+ When {
164
+ @code = lambda { sh! @command }
165
+ }
166
+ Then {
167
+ exception = assert_raises(Methadone::FailedCommandError,&@code)
168
+ exception.command.should == @command
169
+ assert_logger_output_for_failure(@logger,@command,test_command_stdout,test_command_stderr)
170
+ }
171
+ end
172
+
173
+ class MyTestApp
174
+ include Methadone::SH
175
+ def initialize(logger)
176
+ set_sh_logger(logger)
177
+ end
178
+ end
179
+
180
+ test_that "when we don't have CLILogging included, we can still provide our own logger" do
181
+ Given {
182
+ @logger = CapturingLogger.new
183
+ @test_app = MyTestApp.new(@logger)
184
+ @command = test_command
185
+ }
186
+ When {
187
+ @exit_code = @test_app.sh @command
188
+ }
189
+ Then {
190
+ assert_successful_command_execution(@exit_code,@logger,@command,test_command_stdout)
191
+ }
192
+ end
193
+
194
+ class MyExecutionStrategy
195
+ include Clean::Test::Any
196
+ attr_reader :command
197
+
198
+ def initialize(exitcode)
199
+ @exitcode = exitcode
200
+ @command = nil
201
+ end
202
+
203
+ def run_command(command)
204
+ @command = command
205
+ [any_string,any_string,OpenStruct.new(:exitstatus => @exit_code)]
206
+ end
207
+
208
+ def exception_meaning_command_not_found
209
+ RuntimeError
210
+ end
211
+ end
212
+
213
+ class MyExecutionStrategyApp
214
+ include Methadone::CLILogging
215
+ include Methadone::SH
216
+
217
+ attr_reader :strategy
218
+
219
+ def initialize(exit_code)
220
+ @strategy = MyExecutionStrategy.new(exit_code)
221
+ set_execution_strategy(@strategy)
222
+ set_sh_logger(CapturingLogger.new)
223
+ end
224
+ end
225
+
226
+ test_that "when I provide a custom execution strategy, it gets used" do
227
+ Given {
228
+ @exit_code = any_int :min => 0, :max => 127
229
+ @app = MyExecutionStrategyApp.new(@exit_code)
230
+ @command = "ls"
231
+ }
232
+ When {
233
+ @results = @app.sh(@command)
234
+ }
235
+ Then {
236
+ @app.strategy.command.should == @command
237
+ @results.should == @exitstatus
238
+ }
239
+ end
240
+
241
+ private
242
+
243
+ def assert_successful_command_execution(exit_code,logger,command,stdout)
244
+ exit_code.should == 0
245
+ logger.debugs[0].should == "Executing '#{command}'"
246
+ logger.debugs[1].should == "Output of '#{command}': #{stdout}"
247
+ logger.warns.length.should == 0
248
+ end
249
+
250
+ def assert_logger_output_for_failure(logger,command,stdout,stderr)
251
+ logger.debugs[0].should == "Executing '#{command}'"
252
+ logger.infos[0].should == "Output of '#{command}': #{stdout}"
253
+ logger.warns[0].should == "Error output of '#{command}': #{stderr}"
254
+ logger.warns[1].should == "Error running '#{command}'"
255
+ end
256
+
257
+ def use_capturing_logger
258
+ @logger = CapturingLogger.new
259
+ change_logger(@logger)
260
+ end
261
+
262
+ def test_command(args='')
263
+ File.join(File.dirname(__FILE__),'command_for_tests.rb') + ' ' + args
264
+ end
265
+
266
+ def test_command_stdout; "standard output"; end
267
+ def test_command_stderr; "standard error"; end
268
+
269
+ end