muack 1.4.0 → 1.5.0
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 +5 -5
- data/.travis.yml +19 -9
- data/CHANGES.md +12 -0
- data/Gemfile +0 -4
- data/README.md +17 -16
- data/Rakefile +3 -4
- data/lib/muack/block.rb +4 -15
- data/lib/muack/block_26.rb +16 -0
- data/lib/muack/block_27.rb +16 -0
- data/lib/muack/coat.rb +1 -1
- data/lib/muack/definition.rb +4 -3
- data/lib/muack/failure.rb +5 -4
- data/lib/muack/mock.rb +123 -65
- data/lib/muack/spy.rb +3 -3
- data/lib/muack/stub.rb +7 -5
- data/lib/muack/test.rb +42 -11
- data/lib/muack/version.rb +1 -1
- data/muack.gemspec +65 -57
- data/task/README.md +8 -8
- data/task/gemgem.rb +29 -7
- data/test/test_any_instance_of.rb +16 -2
- data/test/test_from_readme.rb +5 -7
- data/test/test_keyargs.rb +111 -0
- data/test/test_modifier.rb +6 -6
- data/test/test_prepend.rb +121 -0
- data/test/test_proxy.rb +19 -4
- data/test/test_satisfying.rb +12 -12
- data/test/test_spy.rb +4 -4
- data/test/test_visibility.rb +120 -0
- metadata +15 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e65f1260993f94d96899086e3975500583ec077bfae332a8fd4cefc5a2c7da49
|
4
|
+
data.tar.gz: 736468eb986e1925f8f83973fec00c2142df16ca80aee9ce051bbdc255cbbfc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3bb1281eab20185484890205696cbed1d3996ed6392b76e08297af8e742bd76a80f2e3a26fffec265848d18a5373dffd517db6d2127b4e511dfdc5a2b475937
|
7
|
+
data.tar.gz: 2c148e33b0dce444708d09a58f6364e160a926f73e5432120fa8668c7ee0dd65c83128e69723673fd6c95bb62db879b8ff3636af29c93e783954f8409a882ebd
|
data/.travis.yml
CHANGED
@@ -1,11 +1,21 @@
|
|
1
|
-
|
1
|
+
sudo: false
|
2
2
|
language: ruby
|
3
|
-
rvm:
|
4
|
-
- 2.0
|
5
|
-
- 2.1
|
6
|
-
- 2.2
|
7
|
-
- rbx-2
|
8
|
-
- jruby
|
9
3
|
|
10
|
-
install: 'bundle install --retry=3'
|
11
|
-
|
4
|
+
install: 'gem update --system; gem install bundler; bundle install --retry=3'
|
5
|
+
before_script: unset CI
|
6
|
+
script: 'ruby -vwr bundler/setup -S rake test'
|
7
|
+
|
8
|
+
matrix:
|
9
|
+
include:
|
10
|
+
- rvm: 2.4
|
11
|
+
env: RUBYOPT=--enable-frozen-string-literal
|
12
|
+
- rvm: 2.5
|
13
|
+
env: RUBYOPT=--enable-frozen-string-literal
|
14
|
+
- rvm: 2.6
|
15
|
+
env: RUBYOPT=--enable-frozen-string-literal
|
16
|
+
- rvm: 2.7
|
17
|
+
env: RUBYOPT=--enable-frozen-string-literal
|
18
|
+
- rvm: ruby-head
|
19
|
+
env: RUBYOPT=--enable-frozen-string-literal
|
20
|
+
- rvm: jruby
|
21
|
+
env: RUBYOPT=--enable-frozen-string-literal
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Muack 1.5.0 -- 2020-11-28
|
4
|
+
|
5
|
+
### Bugs fixed
|
6
|
+
|
7
|
+
* Properly handle prepended objects
|
8
|
+
* Properly restore method visibilities for singleton methods
|
9
|
+
|
10
|
+
### Enhancement
|
11
|
+
|
12
|
+
* Eliminated any potential keyword arguments warnings to be future proof
|
13
|
+
* Some major internal restructure
|
14
|
+
|
3
15
|
## Muack 1.4.0 -- 2015-11-21
|
4
16
|
|
5
17
|
### Incompatible changes / Enhancement
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Muack [](http://travis-ci.org/godfat/muack) [](http://travis-ci.org/godfat/muack) [](https://coveralls.io/github/godfat/muack) [](https://gitter.im/godfat/muack)
|
2
2
|
|
3
3
|
by Lin Jen-Shin ([godfat](http://godfat.org))
|
4
4
|
|
@@ -7,6 +7,7 @@ by Lin Jen-Shin ([godfat](http://godfat.org))
|
|
7
7
|
* [github](https://github.com/godfat/muack)
|
8
8
|
* [rubygems](https://rubygems.org/gems/muack)
|
9
9
|
* [rdoc](http://rdoc.info/github/godfat/muack)
|
10
|
+
* [issues](https://github.com/godfat/muack/issues) (feel free to ask for support)
|
10
11
|
|
11
12
|
## DESCRIPTION:
|
12
13
|
|
@@ -25,7 +26,7 @@ Muack is much simpler and thus much faster and much more consistent.
|
|
25
26
|
|
26
27
|
## REQUIREMENTS:
|
27
28
|
|
28
|
-
* Tested with MRI (official CRuby)
|
29
|
+
* Tested with MRI (official CRuby) and JRuby.
|
29
30
|
|
30
31
|
## INSTALLATION:
|
31
32
|
|
@@ -46,7 +47,7 @@ describe 'Hello' do
|
|
46
47
|
after { Muack.verify }
|
47
48
|
|
48
49
|
would 'say world!' do
|
49
|
-
str = 'Hello'
|
50
|
+
str = 'Hello'.dup
|
50
51
|
mock(str).say('!'){ |arg| "World#{arg}" }
|
51
52
|
str.say('!').should.eq 'World!'
|
52
53
|
end
|
@@ -172,7 +173,7 @@ affecting the others. This is helpful in the cases of mocking some very
|
|
172
173
|
basic objects like Time, without causing too much side effect.
|
173
174
|
|
174
175
|
``` ruby
|
175
|
-
name = 'str'
|
176
|
+
name = 'str'.dup
|
176
177
|
stub(name).to_s{ 'hi' }
|
177
178
|
stub(Time).new { Time.at(0) }
|
178
179
|
mock(Time).now { Time.new }
|
@@ -274,7 +275,7 @@ into proxy mode we simply do not provide any block to the injected method,
|
|
274
275
|
but just name it. Here's an example:
|
275
276
|
|
276
277
|
``` ruby
|
277
|
-
str = 'str'
|
278
|
+
str = 'str'.dup
|
278
279
|
mock(str).reverse
|
279
280
|
p str.reverse # 'rts'
|
280
281
|
p Muack.verify # true
|
@@ -284,7 +285,7 @@ Note that if reverse was not called exactly once, the mock would complain.
|
|
284
285
|
We could also use stub + spy to do the same thing as well:
|
285
286
|
|
286
287
|
``` ruby
|
287
|
-
str = 'str'
|
288
|
+
str = 'str'.dup
|
288
289
|
stub(str).reverse
|
289
290
|
p str.reverse # 'rts'
|
290
291
|
spy(str).reverse
|
@@ -623,7 +624,7 @@ our own behaviour, then we already have full control of the arguments.
|
|
623
624
|
There's no points to use both. This also applies to `peek_return`.
|
624
625
|
|
625
626
|
``` ruby
|
626
|
-
str = 'ff'
|
627
|
+
str = 'ff'.dup
|
627
628
|
mock(str).to_i.with_any_args.peek_args{ |radix| radix * 2 }
|
628
629
|
p str.to_i(8) # 255
|
629
630
|
p Muack.verify # true
|
@@ -654,7 +655,7 @@ might just want to take a look at the return? Here's an example using
|
|
654
655
|
`peek_return` to modify the original return value.
|
655
656
|
|
656
657
|
``` ruby
|
657
|
-
str = 'ff'
|
658
|
+
str = 'ff'.dup
|
658
659
|
mock(str).to_i.with_any_args.peek_return{ |int| int * 2 }
|
659
660
|
p str.to_i(16) # 510
|
660
661
|
p Muack.verify # true
|
@@ -832,7 +833,7 @@ p Muack.verify # true
|
|
832
833
|
|
833
834
|
``` ruby
|
834
835
|
obj = Object.new
|
835
|
-
mock(obj).say(where(:a => is_a(
|
836
|
+
mock(obj).say(where(:a => is_a(Integer))){ |arg| arg }
|
836
837
|
p obj.say(:a => 0) # {:a => 0}
|
837
838
|
p Muack.verify # true
|
838
839
|
```
|
@@ -841,7 +842,7 @@ Note that this could be recursive.
|
|
841
842
|
|
842
843
|
``` ruby
|
843
844
|
obj = Object.new
|
844
|
-
mock(obj).say(where(:a => {:b => [is_a(
|
845
|
+
mock(obj).say(where(:a => {:b => [is_a(Integer)]})){ |arg| arg[:a] }
|
845
846
|
p obj.say(:a => {:b => [0]}) # {:b => [0]}
|
846
847
|
p Muack.verify # true
|
847
848
|
```
|
@@ -862,7 +863,7 @@ Note that this could be recursive.
|
|
862
863
|
|
863
864
|
``` ruby
|
864
865
|
obj = Object.new
|
865
|
-
mock(obj).say(having(:a => {:b => [is_a(
|
866
|
+
mock(obj).say(having(:a => {:b => [is_a(Integer)]})){ |arg| arg[:c] }
|
866
867
|
p obj.say(:a => {:b => [1]}, :c => 2) # 2
|
867
868
|
p Muack.verify # true
|
868
869
|
```
|
@@ -883,7 +884,7 @@ Note that this could be recursive.
|
|
883
884
|
|
884
885
|
``` ruby
|
885
886
|
obj = Object.new
|
886
|
-
mock(obj).say(allowing(:a => {:b => is_a(
|
887
|
+
mock(obj).say(allowing(:a => {:b => is_a(Integer), :c => 1})){ |arg| arg[:a] }
|
887
888
|
p obj.say(:a => {:b => 2}) # {:b => 2}
|
888
889
|
p Muack.verify # true
|
889
890
|
```
|
@@ -1209,8 +1210,8 @@ Or, I am happy to break legacy.
|
|
1209
1210
|
## USERS:
|
1210
1211
|
|
1211
1212
|
* [Rib][]
|
1212
|
-
* [rest-core](https://github.com/
|
1213
|
-
* [rest-more](https://github.com/
|
1213
|
+
* [rest-core](https://github.com/godfat/rest-core)
|
1214
|
+
* [rest-more](https://github.com/godfat/rest-more)
|
1214
1215
|
|
1215
1216
|
## CONTRIBUTORS:
|
1216
1217
|
|
@@ -1218,9 +1219,9 @@ Or, I am happy to break legacy.
|
|
1218
1219
|
|
1219
1220
|
## LICENSE:
|
1220
1221
|
|
1221
|
-
Apache License 2.0
|
1222
|
+
Apache License 2.0 (Apache-2.0)
|
1222
1223
|
|
1223
|
-
Copyright (c) 2013-
|
1224
|
+
Copyright (c) 2013-2020, Lin Jen-Shin (godfat)
|
1224
1225
|
|
1225
1226
|
Licensed under the Apache License, Version 2.0 (the "License");
|
1226
1227
|
you may not use this file except in compliance with the License.
|
data/Rakefile
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
|
2
2
|
begin
|
3
|
-
require "#{
|
3
|
+
require "#{__dir__}/task/gemgem"
|
4
4
|
rescue LoadError
|
5
|
-
sh 'git submodule update --init'
|
5
|
+
sh 'git submodule update --init --recursive'
|
6
6
|
exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
|
7
7
|
end
|
8
8
|
|
9
|
-
Gemgem.init(
|
9
|
+
Gemgem.init(__dir__) do |s|
|
10
10
|
require 'muack/version'
|
11
11
|
s.name = 'muack'
|
12
12
|
s.version = Muack::VERSION
|
13
|
-
%w[].each{ |g| s.add_runtime_dependency(g) }
|
14
13
|
end
|
data/lib/muack/block.rb
CHANGED
@@ -1,17 +1,6 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
self.block, self.context = block, context
|
7
|
-
end
|
8
|
-
|
9
|
-
def call *args, &actual_block
|
10
|
-
if context # ruby: no way to pass actual_block to instance_exec
|
11
|
-
context.instance_exec(*args, &block)
|
12
|
-
else
|
13
|
-
block.call(*args, &actual_block)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
2
|
+
if RUBY_VERSION < '2.7'
|
3
|
+
require 'muack/block_26'
|
4
|
+
else
|
5
|
+
require 'muack/block_27'
|
17
6
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module Muack
|
3
|
+
class Block < Struct.new(:block, :context)
|
4
|
+
def initialize block, context=nil
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(*args, &block)
|
9
|
+
if context
|
10
|
+
context.instance_exec(*args, &block)
|
11
|
+
else
|
12
|
+
block.call(*args, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/muack/coat.rb
CHANGED
data/lib/muack/definition.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
|
2
2
|
module Muack
|
3
|
-
Definition =
|
4
|
-
|
5
|
-
|
3
|
+
Definition = Struct.new(:msg, :args, :returns,
|
4
|
+
:peek_args, :peek_return,
|
5
|
+
:original_method, :visibility)
|
6
|
+
ActualCall = Struct.new(:msg, :args, :block)
|
6
7
|
WithAnyArgs = Object.new
|
7
8
|
end
|
data/lib/muack/failure.rb
CHANGED
@@ -12,11 +12,12 @@ module Muack
|
|
12
12
|
|
13
13
|
class Unexpected < Failure
|
14
14
|
attr_reader :was
|
15
|
-
def initialize obj, expected_defis,
|
16
|
-
|
17
|
-
|
15
|
+
def initialize obj, expected_defis, actual_call
|
16
|
+
args = actual_call.args.map(&:inspect)
|
17
|
+
@was = "#{obj.inspect}.#{actual_call.msg}(#{args.join(', ')})"
|
18
|
+
|
18
19
|
if expected_defis.empty?
|
19
|
-
super("\nUnexpected call: #{
|
20
|
+
super("\nUnexpected call: #{was}")
|
20
21
|
else
|
21
22
|
build_expected(obj, expected_defis)
|
22
23
|
super("\nExpected: #{expected}\n but was: #{was}")
|
data/lib/muack/mock.rb
CHANGED
@@ -6,8 +6,6 @@ require 'muack/block'
|
|
6
6
|
require 'muack/error'
|
7
7
|
|
8
8
|
module Muack
|
9
|
-
EmptyBlock = proc{}
|
10
|
-
|
11
9
|
class Mock < BasicObject
|
12
10
|
attr_reader :object
|
13
11
|
def initialize object
|
@@ -24,8 +22,8 @@ module Muack
|
|
24
22
|
end
|
25
23
|
|
26
24
|
# Public API: Define mocked method
|
27
|
-
def method_missing msg, *args, &
|
28
|
-
defi = Definition.new(msg, args,
|
25
|
+
def method_missing msg, *args, &returns
|
26
|
+
defi = Definition.new(msg, args, returns)
|
29
27
|
if injected = __mock_injected[defi.msg]
|
30
28
|
defi.original_method = injected.original_method
|
31
29
|
else
|
@@ -51,42 +49,42 @@ module Muack
|
|
51
49
|
end
|
52
50
|
|
53
51
|
# used for mocked object to dispatch mocked method
|
54
|
-
def __mock_dispatch
|
55
|
-
if defi = __mock_defis[msg].shift
|
52
|
+
def __mock_dispatch actual_call
|
53
|
+
if defi = __mock_defis[actual_call.msg].shift
|
56
54
|
__mock_disps_push(defi)
|
57
|
-
if __mock_check_args(defi
|
55
|
+
if __mock_check_args(defi, actual_call)
|
58
56
|
defi
|
59
57
|
else
|
60
58
|
Mock.__send__(:raise, # Wrong argument
|
61
|
-
Unexpected.new(object, [defi],
|
59
|
+
Unexpected.new(object, [defi], actual_call))
|
62
60
|
end
|
63
61
|
else
|
64
|
-
__mock_failed(
|
62
|
+
__mock_failed(actual_call)
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|
68
66
|
# used for mocked object to dispatch mocked method
|
69
|
-
def __mock_dispatch_call context, disp,
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
__mock_block_call(context, disp.returns,
|
79
|
-
args, actual_block, true)
|
80
|
-
elsif disp.original_method # proxies for singleton methods
|
81
|
-
context.__send__(disp.original_method, *args, &actual_block)
|
82
|
-
else # proxies for instance methods
|
83
|
-
# need the original context for calling `super`
|
84
|
-
# ruby: can't pass a block to yield, so we name it _yield
|
85
|
-
_yield.call(args, &actual_block)
|
86
|
-
end
|
67
|
+
def __mock_dispatch_call context, disp, actual_call, &proxy_super
|
68
|
+
# resolving arguments
|
69
|
+
call =
|
70
|
+
if disp.peek_args
|
71
|
+
args = __mock_block_call(context, disp.peek_args, actual_call)
|
72
|
+
ActualCall.new(actual_call.msg, args, actual_call.block)
|
73
|
+
else
|
74
|
+
actual_call
|
75
|
+
end
|
87
76
|
|
77
|
+
# retrieve actual return
|
78
|
+
ret =
|
79
|
+
if disp.returns
|
80
|
+
__mock_block_call(context, disp.returns, call)
|
81
|
+
else
|
82
|
+
__mock_proxy_call(context, disp, call, proxy_super)
|
83
|
+
end
|
84
|
+
|
85
|
+
# resolving return
|
88
86
|
if disp.peek_return
|
89
|
-
__mock_block_call(context, disp.peek_return, ret,
|
87
|
+
__mock_block_call(context, disp.peek_return, ret, true)
|
90
88
|
else
|
91
89
|
ret
|
92
90
|
end
|
@@ -95,9 +93,9 @@ module Muack
|
|
95
93
|
# used for Muack::Session#verify
|
96
94
|
def __mock_verify
|
97
95
|
__mock_defis.values.all?(&:empty?) || begin
|
98
|
-
msg, defis_with_same_msg = __mock_defis.find{ |_, v| v.
|
96
|
+
msg, defis_with_same_msg = __mock_defis.find{ |_, v| v.any? }
|
99
97
|
args, defis = defis_with_same_msg.group_by(&:args).first
|
100
|
-
dsize = __mock_disps[msg].
|
98
|
+
dsize = __mock_disps[msg].count{ |d| d.args == args }
|
101
99
|
Mock.__send__(:raise, # Too little times
|
102
100
|
Expected.new(object, defis.first, defis.size + dsize, dsize))
|
103
101
|
end
|
@@ -114,36 +112,46 @@ module Muack
|
|
114
112
|
private
|
115
113
|
def __mock_inject_method defi
|
116
114
|
__mock_injected[defi.msg] = defi
|
117
|
-
|
118
|
-
|
119
|
-
|
115
|
+
# a) ancestors.first is the first module in the method chain.
|
116
|
+
# it's just the singleton_class when nothing was prepended,
|
117
|
+
# otherwise the last prepended module.
|
118
|
+
# b) would be the class in AnyInstanceOf.
|
119
|
+
target = object.singleton_class.ancestors.first
|
120
|
+
Mock.store_original_method(target, defi)
|
121
|
+
__mock_inject_mock_method(target, defi)
|
120
122
|
end
|
121
123
|
|
122
124
|
def __mock_reset_method defi
|
123
|
-
object.singleton_class.module_eval do
|
125
|
+
object.singleton_class.ancestors.first.module_eval do
|
124
126
|
remove_method(defi.msg)
|
125
127
|
# restore original method
|
126
|
-
if
|
128
|
+
if public_instance_methods(false).include?(defi.original_method) ||
|
129
|
+
protected_instance_methods(false).include?(defi.original_method) ||
|
127
130
|
private_instance_methods(false).include?(defi.original_method)
|
128
131
|
alias_method(defi.msg, defi.original_method)
|
132
|
+
__send__(defi.visibility, defi.msg)
|
129
133
|
remove_method(defi.original_method)
|
130
134
|
end
|
131
135
|
end
|
132
136
|
end
|
133
137
|
|
134
138
|
def self.store_original_method klass, defi
|
135
|
-
|
136
|
-
:public
|
139
|
+
visibility = if klass.public_instance_methods(false).include?(defi.msg)
|
140
|
+
:public
|
141
|
+
elsif klass.protected_instance_methods(false).include?(defi.msg)
|
142
|
+
:protected
|
137
143
|
elsif klass.private_instance_methods(false).include?(defi.msg)
|
138
144
|
:private
|
139
145
|
end
|
140
146
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
+
if visibility # store original method
|
148
|
+
original_method = find_new_name(klass, defi.msg)
|
149
|
+
klass.__send__(:alias_method, original_method, defi.msg)
|
150
|
+
defi.original_method = original_method
|
151
|
+
defi.visibility = visibility
|
152
|
+
else
|
153
|
+
defi.visibility = :public
|
154
|
+
end
|
147
155
|
end
|
148
156
|
|
149
157
|
def self.find_new_name klass, message, level=0
|
@@ -159,50 +167,100 @@ module Muack
|
|
159
167
|
end
|
160
168
|
end
|
161
169
|
|
162
|
-
def __mock_inject_mock_method target, defi
|
170
|
+
def __mock_inject_mock_method target, defi
|
163
171
|
mock = self # remember the context
|
164
172
|
target.__send__(:define_method, defi.msg){ |*actual_args, &actual_block|
|
165
|
-
|
166
|
-
mock.
|
167
|
-
|
168
|
-
super
|
173
|
+
actual_call = ActualCall.new(defi.msg, actual_args, actual_block)
|
174
|
+
disp = mock.__mock_dispatch(actual_call)
|
175
|
+
mock.__mock_dispatch_call(self, disp, actual_call) do |call, has_kargs|
|
176
|
+
# need the original context for calling `super`
|
177
|
+
if has_kargs && kargs = call.args.last
|
178
|
+
super(*call.args[0...-1], **kargs, &call.block)
|
179
|
+
else
|
180
|
+
super(*call.args, &call.block)
|
181
|
+
end
|
169
182
|
end
|
170
183
|
}
|
171
|
-
target.__send__(
|
184
|
+
target.__send__(defi.visibility, defi.msg)
|
172
185
|
end
|
173
186
|
|
174
187
|
# used for __mock_dispatch
|
175
|
-
def __mock_failed
|
176
|
-
if expected = __mock_find_checked_difi(disps,
|
188
|
+
def __mock_failed actual_call, disps=__mock_disps[actual_call.msg]
|
189
|
+
if expected = __mock_find_checked_difi(disps, actual_call)
|
177
190
|
Mock.__send__(:raise, # Too many times
|
178
191
|
Expected.new(object, expected, disps.size, disps.size+1))
|
179
192
|
else
|
180
193
|
Mock.__send__(:raise, # Wrong argument
|
181
|
-
Unexpected.new(object, disps,
|
194
|
+
Unexpected.new(object, disps, actual_call))
|
182
195
|
end
|
183
196
|
end
|
184
197
|
|
185
198
|
# used for __mock_dispatch_call
|
186
|
-
def __mock_block_call context, block,
|
187
|
-
return unless block
|
199
|
+
def __mock_block_call context, block, actual_call, peek_return=false
|
188
200
|
# for AnyInstanceOf, we don't have the actual context at the time
|
189
201
|
# we're defining it, so we update it here
|
190
|
-
|
191
|
-
|
192
|
-
block.
|
193
|
-
|
194
|
-
|
202
|
+
if block.kind_of?(Block)
|
203
|
+
block.context = context
|
204
|
+
instance_exec_block = block.block
|
205
|
+
end
|
206
|
+
|
207
|
+
if peek_return # actual_call is the actual return in this case
|
208
|
+
block.call(actual_call, &instance_exec_block)
|
209
|
+
else
|
210
|
+
actual_block = actual_call.block || instance_exec_block
|
211
|
+
if __mock_block_with_kargs?(instance_exec_block || block) &&
|
212
|
+
kargs = actual_call.args.last
|
213
|
+
block.call(*actual_call.args[0...-1], **kargs, &actual_block)
|
214
|
+
else
|
215
|
+
block.call(*actual_call.args, &actual_block)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# used for __mock_dispatch_call
|
221
|
+
def __mock_proxy_call context, disp, call, proxy_super
|
222
|
+
if disp.original_method # proxies for singleton methods with __send__
|
223
|
+
if __mock_method_with_kargs?(context, disp.original_method) &&
|
224
|
+
kargs = call.args.last
|
225
|
+
context.__send__(
|
226
|
+
disp.original_method, *call.args[0...-1], **kargs, &call.block)
|
227
|
+
else
|
228
|
+
context.__send__(disp.original_method, *call.args, &call.block)
|
229
|
+
end
|
230
|
+
else # proxies for instance methods with super
|
231
|
+
proxy_super.call(call, __mock_super_with_kargs?(context, call.msg))
|
195
232
|
end
|
196
233
|
end
|
197
234
|
|
198
|
-
def __mock_find_checked_difi defis,
|
199
|
-
defis.public_send(meth){ |d| __mock_check_args(d
|
235
|
+
def __mock_find_checked_difi defis, actual_call, meth=:find
|
236
|
+
defis.public_send(meth){ |d| __mock_check_args(d, actual_call) }
|
237
|
+
end
|
238
|
+
|
239
|
+
def __mock_method_with_kargs? object, method_name
|
240
|
+
__mock_block_with_kargs?(
|
241
|
+
::Kernel.instance_method(:method).bind(object).call(method_name))
|
200
242
|
end
|
201
243
|
|
202
|
-
def
|
203
|
-
|
204
|
-
|
205
|
-
|
244
|
+
def __mock_super_with_kargs? object, method_name
|
245
|
+
super_method =
|
246
|
+
::Kernel.instance_method(:method).bind(object).call(method_name).
|
247
|
+
super_method
|
248
|
+
|
249
|
+
super_method && __mock_block_with_kargs?(super_method)
|
250
|
+
end
|
251
|
+
|
252
|
+
def __mock_block_with_kargs? block
|
253
|
+
# there's no Symbol#start_with? in older Ruby
|
254
|
+
block.parameters.dig(-1, 0).to_s.start_with?('key')
|
255
|
+
end
|
256
|
+
|
257
|
+
def __mock_check_args defi, actual_call
|
258
|
+
return true if defi.args.size == 1 && defi.args.first == WithAnyArgs
|
259
|
+
|
260
|
+
expected_args = defi.args
|
261
|
+
actual_args = actual_call.args
|
262
|
+
|
263
|
+
if expected_args.none?{ |arg| arg.kind_of?(Satisfying) }
|
206
264
|
expected_args == actual_args
|
207
265
|
|
208
266
|
elsif expected_args.size == actual_args.size
|