methadone 0.5.1 → 1.0.0.rc1

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.
@@ -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