shell_mock 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -3
- data/README.md +52 -22
- data/lib/shell_mock.rb +20 -13
- data/lib/shell_mock/backtick_monkey_patch.rb +33 -0
- data/lib/shell_mock/call_verifier.rb +21 -21
- data/lib/shell_mock/command_stub.rb +17 -10
- data/lib/shell_mock/exec_monkey_patch.rb +33 -0
- data/lib/shell_mock/monkey_patch.rb +21 -14
- data/lib/shell_mock/monkey_patches.rb +4 -91
- data/lib/shell_mock/spawn_monkey_patch.rb +45 -0
- data/lib/shell_mock/stub_registry.rb +6 -3
- data/lib/shell_mock/system_monkey_patch.rb +33 -0
- data/lib/shell_mock/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b756c372c9477ca0a21a3894fb4d07c35e87c14e
|
4
|
+
data.tar.gz: 565cb68f40d84a3f2d97ccaa50cad9aa41f16eb1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40cf06dcafcc3e976cedac4a59623a1d47fc4c4f90f437a149d90f26ef62a3b200b6a342273bdd96036070107fa031d234a34a6fc56b457c9b0be99645257383
|
7
|
+
data.tar.gz: 9cdd1c5d0e1a1d8630aa03506493b2e847ad142fe47b4a56bfd5623522b1e8268369e045ea55fa9518f596b77a002730d3e50b5dc4e1d7597848b4a5e87baf5c
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
## ROADMAP 1.0.0
|
2
2
|
|
3
|
-
*
|
4
|
-
*
|
3
|
+
* convert `CommandStub#and_exit` to `CommandStub#will_exit`
|
4
|
+
* convert `CommandStub#and_output` to `CommandStub#will_output`
|
5
|
+
* convert `CommandStub#and_return` to `CommandStub#will_return`
|
6
|
+
* add `CommandStub#will_cause(&blk)` for specifying desired side effects
|
7
|
+
* add `CommandStub#will_stdout(str)` & `CommandStub#will_stderr(str)`. these will work differently for backtick than it will for system & exec.
|
5
8
|
* maybe add `CommandStub#with_stdin(str)` for `spawn`?
|
6
9
|
|
10
|
+
## RELEASE 0.5.0
|
11
|
+
|
12
|
+
* FEATURE: `CommandStub#and_output` can be used on command stubs to set the output of a command without setting the exit status.
|
13
|
+
* ENHANCEMENT: `Kernel#spawn`, `Kernel.spawn`, & `Process.spawn` are now implemented well enough that `Open3.capture3` behaves as though spawn is not being monkey-patched when ShellMock is enabled (and therefore, spawn most definitely is being monkey-patched). This should apply to all of `Open3`'s methods, since they all delegate to `Open3.popen_run` or `Open3.pipeline_run`, both of which internally use `Kernel#spawn`, **however I only have a spec for `Open3.capture3`**.
|
14
|
+
|
7
15
|
## RELEASE 0.4.0
|
8
16
|
|
9
|
-
* FEATURE: `Kernel#spawn` and `Kernel.spawn` are now supported
|
17
|
+
* FEATURE: `Kernel#spawn` and `Kernel.spawn` are now supported
|
10
18
|
|
11
19
|
## RELEASE 0.3.3
|
12
20
|
|
data/README.md
CHANGED
@@ -1,50 +1,80 @@
|
|
1
1
|
# ShellMock
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/shell_mock.png)](http://badge.fury.io/rb/shell_mock)
|
3
|
+
[![Build Status](https://secure.travis-ci.org/yarmiganosca/shell_mock.png)](http://travis-ci.org/yarmiganosca/shell_mock)
|
2
4
|
|
3
5
|
It's [webmock](http://github.com/bblimke/webmock) for shell commands. It's pretty simple. You can do things like this:
|
4
6
|
|
5
7
|
```ruby
|
6
8
|
require 'shell_mock/rspec'
|
7
9
|
|
8
|
-
describe '
|
9
|
-
|
10
|
+
RSpec.describe "shelling out to run 'ls'" do
|
11
|
+
before { ShellMock.enable }
|
12
|
+
after { ShellMock.disable }
|
10
13
|
|
11
|
-
|
14
|
+
let(:stub) { ShellMock.stub_command('ls') }
|
12
15
|
|
13
|
-
|
16
|
+
it "works"
|
17
|
+
expect(system('ls')).to eq true
|
18
|
+
|
19
|
+
expect(stub).to have_been_called
|
20
|
+
end
|
14
21
|
end
|
15
22
|
```
|
16
23
|
|
17
|
-
|
24
|
+
This:
|
25
|
+
1. enables ShellMock's monkey patches during the test
|
26
|
+
2. creates a command stub that will match the command `"ls"` (by default it will exit `0` and have no output)
|
27
|
+
3. shells out to run `"ls"` (in this case using `Kernel#system`)
|
28
|
+
4. correctly expects that our command stub for `"ls"` will have recorded an invocation
|
18
29
|
|
19
|
-
###
|
30
|
+
### You can narrow what invocations are matched to your command stub:
|
20
31
|
|
21
|
-
|
32
|
+
**Match env vars as well as the command:** `ShellMock.stub_command('ls').with_env({'FOO' => 'bar'})`
|
22
33
|
|
23
|
-
|
34
|
+
**Provide a more complete invocation:** `ShellMock.stub_command('ls $HOME')`
|
24
35
|
|
25
|
-
|
36
|
+
Shelling out to run `"ls"` won't match this command stub, but shelling out to run `"ls $HOME"` will. ShellMock always matches as strictly as possible, so if you stubbed `"ls"` and `"ls $HOME"`, invocations of `"ls $HOME'` would only ever match against the latter.
|
26
37
|
|
27
|
-
|
38
|
+
### Setting the behavior of the command invocation:
|
28
39
|
|
29
|
-
|
40
|
+
**Have the mock command invocation write to stdout:** `ShellMock.stub_command('ls').and_output("\n")`
|
30
41
|
|
31
|
-
|
42
|
+
**Set the mock command invocation's exit status:** `ShellMock.stub_command('ls').and_exit(2)`
|
32
43
|
|
33
|
-
|
44
|
+
If you want to both write to stdout and set the exit code (a common pair), `ShellMock.stub_command('ls').and_return("\n")` will both have the command invocation write the passed string to stdout, and will set the mock command invocation's exit status to `0`.
|
34
45
|
|
35
|
-
###
|
46
|
+
### Specifying the expected number of command invocations:
|
36
47
|
|
37
|
-
|
38
|
-
require 'shell_mock/rspec'
|
48
|
+
**Called exactly once:** `expect(stub).to have_been_called.once`
|
39
49
|
|
40
|
-
|
41
|
-
stub = ShellMock.stub_commmand('ls').and_return("\n")
|
50
|
+
**Not called:** `expect(stub).to have_been_called.never`
|
42
51
|
|
43
|
-
|
52
|
+
**Not called (using RSpec expectation negation):** `expect(stub).to_not have_been_called`
|
44
53
|
|
45
|
-
|
46
|
-
|
47
|
-
|
54
|
+
**Called exactly `n` times:** `expect(stub).to have_been_called.times(n)`
|
55
|
+
|
56
|
+
**Called more than `n` times:** `expect(stub).to have_been_called.more_than(n)`
|
57
|
+
|
58
|
+
**Called fewer than `n` times:** `expect(stub).to have_been_called.fewer_than(n)`
|
59
|
+
|
60
|
+
`less_than` can be used as an alias for `fewer_than`
|
61
|
+
## Limitations
|
62
|
+
|
63
|
+
Currently, only exact string matches of the stubbed command string are supported. Basic regex support or more complex matching for arguments and flags may be added later.
|
64
|
+
|
65
|
+
ShellMock supports stubbing these ways of shelling out in Ruby:
|
66
|
+
* [`` Kernel#` ``](https://ruby-doc.org/core/Kernel.html#method-i-60) (aka "backticks")
|
67
|
+
* [`%x` command literal](https://ruby-doc.org/docs/ruby-doc-bundle/Manual/man-1.4/syntax.html#command) (which delegates to backticks)
|
68
|
+
* [`Kernel#system`](https://ruby-doc.org/core/Kernel.html#method-i-system)
|
69
|
+
* [`Kernel#exec`](https://ruby-doc.org/core/Kernel.html#method-i-exec)
|
70
|
+
* [`Kernel#spawn`](https://ruby-doc.org/core/Kernel.html#method-i-spawn)
|
71
|
+
* [`Process.spawn`](https://ruby-doc.org/core/Process.html#method-c-spawn)
|
72
|
+
* [the `Open3` module](https://ruby-doc.org/stdlib/libdoc/open3/rdoc/Open3.html) (since all its methods use `spawn`)
|
73
|
+
|
74
|
+
ShellMock currently DOES NOT support stubbing these ways of shelling out in Ruby (but will):
|
75
|
+
* [`IO.popen`](https://ruby-doc.org/core/IO.html#method-c-popen)
|
76
|
+
* [`PTY.spawn`](https://ruby-doc.org/stdlib/libdoc/pty/rdoc/PTY.html#method-c-spawn)
|
77
|
+
* [passing a string that starts with `"|"`](https://devver.wordpress.com/2009/07/13/a-dozen-or-so-ways-to-start-sub-processes-in-ruby-part-2/) to [`Kernel#open`](https://ruby-doc.org/core/Kernel.html#method-i-open)
|
48
78
|
|
49
79
|
## Installation
|
50
80
|
|
data/lib/shell_mock.rb
CHANGED
@@ -24,32 +24,39 @@ module ShellMock
|
|
24
24
|
@let_commands_run
|
25
25
|
end
|
26
26
|
|
27
|
+
# smell; this is a mistake of a method that will only confuse people
|
27
28
|
def self.dont_let_commands_run?
|
28
29
|
!let_commands_run?
|
29
30
|
end
|
30
31
|
|
31
32
|
def self.enable
|
32
|
-
ShellMock.monkey_patches.each
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
ShellMock.monkey_patches.each(&:enable)
|
34
|
+
|
35
|
+
@enabled = true
|
36
|
+
|
37
|
+
true
|
36
38
|
end
|
37
39
|
|
38
40
|
def self.disable
|
39
|
-
ShellMock.monkey_patches.each
|
40
|
-
patch.disable_for(Kernel.eigenclass) if Kernel.respond_to?(patch.alias_for_original, true)
|
41
|
-
patch.disable_for(Kernel) if Object.new.respond_to?(patch.alias_for_original, true)
|
42
|
-
end
|
41
|
+
ShellMock.monkey_patches.each(&:disable)
|
43
42
|
|
44
43
|
StubRegistry.clear
|
44
|
+
|
45
|
+
@enabled = false
|
46
|
+
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.enabled?
|
51
|
+
@enabled
|
45
52
|
end
|
46
53
|
|
47
54
|
def self.monkey_patches
|
48
|
-
[
|
49
|
-
SpawnMonkeyPatch,
|
50
|
-
SystemMonkeyPatch,
|
51
|
-
ExecMonkeyPatch,
|
52
|
-
BacktickMonkeyPatch
|
55
|
+
@monkey_patches ||= [
|
56
|
+
SpawnMonkeyPatch.new,
|
57
|
+
SystemMonkeyPatch.new,
|
58
|
+
ExecMonkeyPatch.new,
|
59
|
+
BacktickMonkeyPatch.new,
|
53
60
|
]
|
54
61
|
end
|
55
62
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'shell_mock/monkey_patch'
|
2
|
+
require 'shell_mock/stub_registry'
|
3
|
+
require 'shell_mock/no_stub_specified'
|
4
|
+
|
5
|
+
module ShellMock
|
6
|
+
class BacktickMonkeyPatch < MonkeyPatch
|
7
|
+
def method_name
|
8
|
+
"`"
|
9
|
+
end
|
10
|
+
|
11
|
+
def interpolatable_name
|
12
|
+
:backtick
|
13
|
+
end
|
14
|
+
|
15
|
+
def override(command)
|
16
|
+
stub = StubRegistry.stub_matching({}, command, {})
|
17
|
+
|
18
|
+
if stub
|
19
|
+
stub.called_with({}, command, {})
|
20
|
+
|
21
|
+
stub.side_effect.call
|
22
|
+
|
23
|
+
__un_shell_mocked_backtick(stub.to_oneliner)
|
24
|
+
else
|
25
|
+
if ShellMock.let_commands_run?
|
26
|
+
__un_shell_mocked_backtick(command)
|
27
|
+
else
|
28
|
+
raise NoStubSpecified.new({}, command, {})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,21 +1,26 @@
|
|
1
1
|
module ShellMock
|
2
2
|
class CallVerifier
|
3
3
|
def initialize
|
4
|
-
|
4
|
+
more_than(0)
|
5
5
|
end
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def times(n)
|
8
|
+
match_calls_when { |calls| calls == n }
|
9
9
|
|
10
|
-
|
10
|
+
self
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
|
13
|
+
def fewer_than(n)
|
14
|
+
match_calls_when { |calls| calls < n }
|
15
|
+
|
16
|
+
self
|
15
17
|
end
|
18
|
+
alias less_than fewer_than
|
16
19
|
|
17
|
-
def
|
18
|
-
|
20
|
+
def more_than(n)
|
21
|
+
match_calls_when { |calls| calls > n }
|
22
|
+
|
23
|
+
self
|
19
24
|
end
|
20
25
|
|
21
26
|
def once
|
@@ -26,28 +31,23 @@ module ShellMock
|
|
26
31
|
times(0)
|
27
32
|
end
|
28
33
|
|
29
|
-
def
|
30
|
-
|
34
|
+
def matches?(command_stub)
|
35
|
+
@command_stub = command_stub
|
31
36
|
|
32
|
-
|
37
|
+
condition.call(command_stub.calls)
|
33
38
|
end
|
34
39
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
self
|
40
|
+
def failure_message
|
41
|
+
"#{command_stub.command} was expected."
|
39
42
|
end
|
40
|
-
alias less_than fewer_than
|
41
|
-
|
42
|
-
def more_than(n)
|
43
|
-
match_calls_when { |calls| calls.size > n }
|
44
43
|
|
45
|
-
|
44
|
+
def failure_message_when_negated
|
45
|
+
"#{command_stub.command} was not expected."
|
46
46
|
end
|
47
47
|
|
48
48
|
private
|
49
49
|
|
50
|
-
attr_reader :
|
50
|
+
attr_reader :command_stub, :condition
|
51
51
|
|
52
52
|
def match_calls_when(&blk)
|
53
53
|
@condition = blk
|
@@ -3,7 +3,7 @@ require 'shell_mock/stub_registry'
|
|
3
3
|
|
4
4
|
module ShellMock
|
5
5
|
class CommandStub
|
6
|
-
attr_reader :command, :
|
6
|
+
attr_reader :command, :output, :exitstatus, :env, :options, :side_effect
|
7
7
|
|
8
8
|
def initialize(command)
|
9
9
|
@command = command
|
@@ -21,19 +21,24 @@ module ShellMock
|
|
21
21
|
self
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def with_options(options)
|
25
25
|
@options = options
|
26
26
|
|
27
27
|
self
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
@
|
32
|
-
@exitstatus = 0
|
30
|
+
def and_output(output)
|
31
|
+
@output = output
|
33
32
|
|
34
33
|
self
|
35
34
|
end
|
36
35
|
|
36
|
+
def and_return(output)
|
37
|
+
self.
|
38
|
+
and_output(output).
|
39
|
+
and_exit(0)
|
40
|
+
end
|
41
|
+
|
37
42
|
def and_exit(exitstatus)
|
38
43
|
@exitstatus = exitstatus
|
39
44
|
|
@@ -41,24 +46,26 @@ module ShellMock
|
|
41
46
|
end
|
42
47
|
|
43
48
|
def calls
|
44
|
-
@calls ||=
|
49
|
+
@calls ||= 0
|
45
50
|
|
46
51
|
marshaled_signatures.each do |marshaled_signature|
|
47
|
-
@calls
|
52
|
+
@calls += 1
|
48
53
|
end
|
49
54
|
|
50
55
|
@calls
|
51
56
|
end
|
52
57
|
|
53
58
|
def called_with(env, command, options)
|
54
|
-
|
59
|
+
writer.puts("called\n")
|
60
|
+
end
|
55
61
|
|
56
|
-
|
62
|
+
def to_oneliner
|
63
|
+
"echo '#{output}' && exit #{exitstatus}"
|
57
64
|
end
|
58
65
|
|
59
66
|
private
|
60
67
|
|
61
|
-
attr_reader :reader
|
68
|
+
attr_reader :reader, :writer
|
62
69
|
|
63
70
|
def marshaled_signatures
|
64
71
|
messages = ""
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'shell_mock/monkey_patch'
|
2
|
+
require 'shell_mock/stub_registry'
|
3
|
+
require 'shell_mock/no_stub_specified'
|
4
|
+
|
5
|
+
module ShellMock
|
6
|
+
class ExecMonkeyPatch < MonkeyPatch
|
7
|
+
def method_name
|
8
|
+
:exec
|
9
|
+
end
|
10
|
+
|
11
|
+
def override(env, command = nil, **options)
|
12
|
+
env, command = {}, env if command.nil?
|
13
|
+
|
14
|
+
# other arg manipulation can go here if necessary
|
15
|
+
|
16
|
+
stub = StubRegistry.stub_matching(env, command, options)
|
17
|
+
|
18
|
+
if stub
|
19
|
+
stub.called_with(env, command, options)
|
20
|
+
|
21
|
+
stub.side_effect.call
|
22
|
+
|
23
|
+
__un_shell_mocked_exec(stub.to_oneliner)
|
24
|
+
else
|
25
|
+
if ShellMock.let_commands_run?
|
26
|
+
__un_shell_mocked_exec(env, command, **options)
|
27
|
+
else
|
28
|
+
raise NoStubSpecified.new(env, command, options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -2,42 +2,49 @@ require 'shell_mock/no_stub_specified'
|
|
2
2
|
|
3
3
|
module ShellMock
|
4
4
|
class MonkeyPatch
|
5
|
-
|
5
|
+
def enable
|
6
|
+
enable_for(Kernel.eigenclass) unless Kernel.respond_to?(method_alias, true)
|
7
|
+
enable_for(Kernel) unless Object.new.respond_to?(method_alias, true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def disable
|
11
|
+
disable_for(Kernel.eigenclass) if Kernel.respond_to?(method_alias, true)
|
12
|
+
disable_for(Kernel) if Object.new.respond_to?(method_alias, true)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
6
16
|
|
7
|
-
def
|
8
|
-
|
9
|
-
@alias_for_original = "__un_shell_mocked_#{interpolatable_name}"
|
10
|
-
@replacement = "__shell_mocked_#{interpolatable_name}"
|
11
|
-
@block = block
|
17
|
+
def method_alias
|
18
|
+
"__un_shell_mocked_#{interpolatable_name}"
|
12
19
|
end
|
13
20
|
|
14
|
-
def
|
15
|
-
|
21
|
+
def interpolatable_name
|
22
|
+
method_name
|
16
23
|
end
|
17
24
|
|
18
25
|
def enable_for(class_or_module)
|
19
|
-
class_or_module.send(:alias_method,
|
26
|
+
class_or_module.send(:alias_method, method_alias, method_name)
|
20
27
|
|
21
28
|
begin
|
22
29
|
# so we don't have to see method redefinition warnings
|
23
|
-
class_or_module.send(:remove_method,
|
30
|
+
class_or_module.send(:remove_method, method_name)
|
24
31
|
rescue NameError
|
25
32
|
end
|
26
33
|
|
27
|
-
class_or_module.send(:define_method,
|
34
|
+
class_or_module.send(:define_method, method_name, &method(:override))
|
28
35
|
end
|
29
36
|
|
30
37
|
def disable_for(class_or_module)
|
31
38
|
begin
|
32
39
|
# so we don't have to see method redefinition warnings
|
33
|
-
class_or_module.send(:remove_method,
|
40
|
+
class_or_module.send(:remove_method, method_name)
|
34
41
|
rescue NameError
|
35
42
|
end
|
36
43
|
|
37
|
-
class_or_module.send(:alias_method,
|
44
|
+
class_or_module.send(:alias_method, method_name, method_alias)
|
38
45
|
|
39
46
|
begin
|
40
|
-
class_or_module.send(:remove_method,
|
47
|
+
class_or_module.send(:remove_method, method_alias)
|
41
48
|
rescue NameError
|
42
49
|
end
|
43
50
|
end
|
@@ -1,91 +1,4 @@
|
|
1
|
-
require 'shell_mock/
|
2
|
-
require 'shell_mock/
|
3
|
-
require 'shell_mock/
|
4
|
-
|
5
|
-
module ShellMock
|
6
|
-
SpawnMonkeyPatch = MonkeyPatch.new(:spawn) do |env, command = nil, **options|
|
7
|
-
env, command = {}, env if command.nil?
|
8
|
-
|
9
|
-
# other arg manipulation can go here if necessary
|
10
|
-
|
11
|
-
stub = StubRegistry.stub_matching(env, command, options)
|
12
|
-
|
13
|
-
if stub
|
14
|
-
stub.side_effect.call
|
15
|
-
stub.called_with(env, command, options)
|
16
|
-
|
17
|
-
__un_shell_mocked_spawn("exit #{stub.exitstatus}")
|
18
|
-
else
|
19
|
-
if ShellMock.let_commands_run?
|
20
|
-
__un_shell_mocked_spawn(env, command, **options)
|
21
|
-
else
|
22
|
-
raise NoStubSpecified.new(env, command, options)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
SystemMonkeyPatch = MonkeyPatch.new(:system) do |env, command = nil, **options|
|
28
|
-
env, command = {}, env if command.nil?
|
29
|
-
|
30
|
-
# other arg manipulation can go here if necessary
|
31
|
-
|
32
|
-
stub = StubRegistry.stub_matching(env, command, options)
|
33
|
-
|
34
|
-
if stub
|
35
|
-
stub.side_effect.call
|
36
|
-
stub.called_with(env, command, options)
|
37
|
-
__un_shell_mocked_backtick("exit #{stub.exitstatus}")
|
38
|
-
|
39
|
-
return stub.exitstatus == 0
|
40
|
-
else
|
41
|
-
if ShellMock.let_commands_run?
|
42
|
-
__un_shell_mocked_system(env, command, **options)
|
43
|
-
else
|
44
|
-
raise NoStubSpecified.new(env, command, options)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# This feels very boilerplatey because Kernel::system and Kernel::exec
|
50
|
-
# have very similar if not identical method signatures. I'm not sure
|
51
|
-
# whether extracting the commonalities would be worth it or would just
|
52
|
-
# confuse.
|
53
|
-
ExecMonkeyPatch = MonkeyPatch.new(:exec) do |env, command = nil, **options|
|
54
|
-
env, command = {}, env if command.nil?
|
55
|
-
|
56
|
-
# other arg manipulation can go here if necessary
|
57
|
-
|
58
|
-
stub = StubRegistry.stub_matching(env, command, options)
|
59
|
-
|
60
|
-
if stub
|
61
|
-
stub.side_effect.call
|
62
|
-
stub.called_with(env, command, options)
|
63
|
-
|
64
|
-
exit stub.exitstatus
|
65
|
-
else
|
66
|
-
if ShellMock.let_commands_run?
|
67
|
-
__un_shell_mocked_exec(env, command, **options)
|
68
|
-
else
|
69
|
-
raise NoStubSpecified.new(env, command, options)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
BacktickMonkeyPatch = MonkeyPatch.new('`', :backtick) do |command|
|
75
|
-
stub = StubRegistry.stub_matching({}, command, {})
|
76
|
-
|
77
|
-
if stub
|
78
|
-
stub.side_effect.call
|
79
|
-
stub.called_with({}, command, {})
|
80
|
-
__un_shell_mocked_backtick("exit #{stub.exitstatus}")
|
81
|
-
|
82
|
-
return stub.expected_output
|
83
|
-
else
|
84
|
-
if ShellMock.let_commands_run?
|
85
|
-
__un_shell_mocked_backtick(command)
|
86
|
-
else
|
87
|
-
raise NoStubSpecified.new({}, command, {})
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
1
|
+
require 'shell_mock/spawn_monkey_patch'
|
2
|
+
require 'shell_mock/system_monkey_patch'
|
3
|
+
require 'shell_mock/backtick_monkey_patch'
|
4
|
+
require 'shell_mock/exec_monkey_patch'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'shell_mock/monkey_patch'
|
2
|
+
require 'shell_mock/stub_registry'
|
3
|
+
require 'shell_mock/no_stub_specified'
|
4
|
+
|
5
|
+
module ShellMock
|
6
|
+
class SpawnMonkeyPatch < MonkeyPatch
|
7
|
+
def method_name
|
8
|
+
:spawn
|
9
|
+
end
|
10
|
+
|
11
|
+
def override(env, command = nil, **options)
|
12
|
+
env, command = {}, env if command.nil?
|
13
|
+
|
14
|
+
# other arg manipulation can go here if necessary
|
15
|
+
|
16
|
+
stub = StubRegistry.stub_matching(env, command, options)
|
17
|
+
|
18
|
+
if stub
|
19
|
+
stub.called_with(env, command, options)
|
20
|
+
|
21
|
+
stub.side_effect.call
|
22
|
+
|
23
|
+
__un_shell_mocked_spawn(stub.to_oneliner, **options)
|
24
|
+
else
|
25
|
+
if ShellMock.let_commands_run?
|
26
|
+
__un_shell_mocked_spawn(env, command, **options)
|
27
|
+
else
|
28
|
+
raise NoStubSpecified.new(env, command, options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def enable
|
34
|
+
enable_for(Process.eigenclass) unless Process.respond_to?(method_alias, true)
|
35
|
+
|
36
|
+
super
|
37
|
+
end
|
38
|
+
|
39
|
+
def disable
|
40
|
+
super
|
41
|
+
|
42
|
+
disable_for(Process.eigenclass) if Process.respond_to?(method_alias, true)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -9,11 +9,14 @@ module ShellMock
|
|
9
9
|
|
10
10
|
def stub_matching(env, command, options)
|
11
11
|
matching_stubs = command_stubs.select do |command_stub|
|
12
|
-
command_stub.
|
13
|
-
command_stub.
|
14
|
-
command_stub.
|
12
|
+
command_stub.command == command &&
|
13
|
+
command_stub.env <= env &&
|
14
|
+
command_stub.options <= options
|
15
15
|
end
|
16
16
|
|
17
|
+
# question: Should we increment all the stubs that match?
|
18
|
+
# What should users expect when they register a stub that
|
19
|
+
# wholly "covers" another already-registered stub?
|
17
20
|
matching_stubs.max_by do |command_stub|
|
18
21
|
[env.size, options.size]
|
19
22
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'shell_mock/monkey_patch'
|
2
|
+
require 'shell_mock/stub_registry'
|
3
|
+
require 'shell_mock/no_stub_specified'
|
4
|
+
|
5
|
+
module ShellMock
|
6
|
+
class SystemMonkeyPatch < MonkeyPatch
|
7
|
+
def method_name
|
8
|
+
:system
|
9
|
+
end
|
10
|
+
|
11
|
+
def override(env, command = nil, **options)
|
12
|
+
env, command = {}, env if command.nil?
|
13
|
+
|
14
|
+
# other arg manipulation can go here if necessary
|
15
|
+
|
16
|
+
stub = StubRegistry.stub_matching(env, command, options)
|
17
|
+
|
18
|
+
if stub
|
19
|
+
stub.called_with(env, command, options)
|
20
|
+
|
21
|
+
stub.side_effect.call
|
22
|
+
|
23
|
+
__un_shell_mocked_system(stub.to_oneliner)
|
24
|
+
else
|
25
|
+
if ShellMock.let_commands_run?
|
26
|
+
__un_shell_mocked_system(env, command, **options)
|
27
|
+
else
|
28
|
+
raise NoStubSpecified.new(env, command, options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/shell_mock/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shell_mock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hoffman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-09-
|
11
|
+
date: 2018-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,15 +114,19 @@ files:
|
|
114
114
|
- bin/console
|
115
115
|
- bin/setup
|
116
116
|
- lib/shell_mock.rb
|
117
|
+
- lib/shell_mock/backtick_monkey_patch.rb
|
117
118
|
- lib/shell_mock/call_verifier.rb
|
118
119
|
- lib/shell_mock/command_stub.rb
|
119
120
|
- lib/shell_mock/core_ext/module.rb
|
121
|
+
- lib/shell_mock/exec_monkey_patch.rb
|
120
122
|
- lib/shell_mock/monkey_patch.rb
|
121
123
|
- lib/shell_mock/monkey_patches.rb
|
122
124
|
- lib/shell_mock/no_stub_specified.rb
|
123
125
|
- lib/shell_mock/rspec.rb
|
124
126
|
- lib/shell_mock/rspec/matchers.rb
|
127
|
+
- lib/shell_mock/spawn_monkey_patch.rb
|
125
128
|
- lib/shell_mock/stub_registry.rb
|
129
|
+
- lib/shell_mock/system_monkey_patch.rb
|
126
130
|
- lib/shell_mock/version.rb
|
127
131
|
- shell_mock.gemspec
|
128
132
|
homepage: http://www.github.com/yarmiganosca/shell_mock
|