caricature 0.7.6 → 0.7.7
Sign up to get free protection for your applications and to get access to all the features.
- data/caricature.gemspec +3 -3
- data/lib/bin/Workarounds.dll.mdb +0 -0
- data/lib/caricature.rb +25 -25
- data/lib/caricature/clr.rb +6 -6
- data/lib/caricature/clr/aspnet_mvc.rb +54 -54
- data/lib/caricature/core_ext.rb +10 -10
- data/lib/caricature/core_ext/array.rb +9 -9
- data/lib/caricature/core_ext/class.rb +31 -14
- data/lib/caricature/core_ext/hash.rb +12 -12
- data/lib/caricature/core_ext/module.rb +14 -14
- data/lib/caricature/core_ext/object.rb +76 -18
- data/lib/caricature/core_ext/string.rb +16 -16
- data/lib/caricature/core_ext/system/string.rb +20 -20
- data/lib/caricature/core_ext/system/type.rb +26 -26
- data/lib/caricature/descriptor.rb +73 -73
- data/lib/caricature/expectation.rb +264 -263
- data/lib/caricature/isolation.rb +143 -143
- data/lib/caricature/isolator.rb +302 -302
- data/lib/caricature/messenger.rb +67 -67
- data/lib/caricature/method_call_recorder.rb +228 -228
- data/lib/caricature/verification.rb +60 -60
- data/lib/caricature/version.rb +1 -1
- data/spec/bacon/integration/clr_to_clr_spec.rb +4 -4
- data/spec/bacon/integration/clr_to_ruby_spec.rb +227 -227
- data/spec/bacon/integration/event_spec.rb +2 -2
- data/spec/bacon/integration/ruby_to_ruby_spec.rb +270 -270
- data/spec/bacon/integration/syntax_spec.rb +43 -0
- data/spec/bacon/unit/core_ext_spec.rb +87 -87
- data/spec/bacon/unit/expectation_spec.rb +300 -300
- data/spec/bacon/unit/interop_spec.rb +29 -29
- data/spec/bacon/unit/isolation_spec.rb +86 -86
- data/spec/bacon/unit/isolator_spec.rb +219 -219
- data/spec/bacon/unit/messaging_spec.rb +310 -310
- data/spec/bacon/unit/method_call_spec.rb +342 -342
- data/spec/bin/ClrModels.dll.mdb +0 -0
- data/spec/rspec/unit/event_spec.rb +1 -1
- metadata +31 -11
- data/spec/models.notused/ClrModels.cs +0 -241
- data/spec/models.notused/ruby_models.rb +0 -151
data/lib/caricature/messenger.rb
CHANGED
@@ -1,68 +1,68 @@
|
|
1
|
-
module Caricature
|
2
|
-
|
3
|
-
# A base class to encapsulate method invocation
|
4
|
-
class Messenger
|
5
|
-
|
6
|
-
# contains the recorder for recording method calls
|
7
|
-
attr_accessor :recorder
|
8
|
-
|
9
|
-
# the real instance of the isolated subject
|
10
|
-
# used to forward calls in partial mocks
|
11
|
-
attr_reader :instance
|
12
|
-
|
13
|
-
# the expecations that have been set for the isolation
|
14
|
-
attr_reader :expectations
|
15
|
-
|
16
|
-
# creates a new instance of this messaging strategy
|
17
|
-
def initialize(expectations, instance=nil)
|
18
|
-
@instance, @expectations = instance, expectations
|
19
|
-
end
|
20
|
-
|
21
|
-
# deliver the message to the receiving isolation
|
22
|
-
def deliver(method_name, return_type, *args, &b)
|
23
|
-
|
24
|
-
internal_deliver(:instance, method_name, return_type, *args, &b)
|
25
|
-
end
|
26
|
-
|
27
|
-
# deliver the message to class of the receiving isolation
|
28
|
-
def deliver_to_class(method_name, return_type, *args, &b)
|
29
|
-
internal_deliver(:class, method_name, return_type, *args, &b)
|
30
|
-
end
|
31
|
-
|
32
|
-
protected
|
33
|
-
|
34
|
-
# template method for looking up the expectation and/or returning a value
|
35
|
-
def internal_deliver(mode, method_name, return_type, *args, &b)
|
36
|
-
raise NotImplementedError.new("Override in an implementing class")
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
def record_call(method_name, mode, expectation, *args, &b)
|
41
|
-
recorder.record_call method_name, mode, expectation, *args, &b if recorder
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
# Encapsulates sending messages to Ruby isolations
|
47
|
-
class RubyMessenger < Messenger
|
48
|
-
|
49
|
-
protected
|
50
|
-
|
51
|
-
# implementation of the template method for looking up the expectation and/or returning a value
|
52
|
-
def internal_deliver(mode, method_name, return_type, *args, &b)
|
53
|
-
exp = expectations.find(method_name, mode, *args)
|
54
|
-
bl = record_call(method_name, mode, exp, *args, &b)
|
55
|
-
if exp
|
56
|
-
block = exp.block || b
|
57
|
-
res = instance.__send__(method_name, *args, &block) if exp.super_before?
|
58
|
-
res = exp.execute *args, &bl
|
59
|
-
res = instance.__send__(method_name, *args, &block) if !exp.super_before? and exp.call_super?
|
60
|
-
res
|
61
|
-
else
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
1
|
+
module Caricature
|
2
|
+
|
3
|
+
# A base class to encapsulate method invocation
|
4
|
+
class Messenger
|
5
|
+
|
6
|
+
# contains the recorder for recording method calls
|
7
|
+
attr_accessor :recorder
|
8
|
+
|
9
|
+
# the real instance of the isolated subject
|
10
|
+
# used to forward calls in partial mocks
|
11
|
+
attr_reader :instance
|
12
|
+
|
13
|
+
# the expecations that have been set for the isolation
|
14
|
+
attr_reader :expectations
|
15
|
+
|
16
|
+
# creates a new instance of this messaging strategy
|
17
|
+
def initialize(expectations, instance=nil)
|
18
|
+
@instance, @expectations = instance, expectations
|
19
|
+
end
|
20
|
+
|
21
|
+
# deliver the message to the receiving isolation
|
22
|
+
def deliver(method_name, return_type, *args, &b)
|
23
|
+
|
24
|
+
internal_deliver(:instance, method_name, return_type, *args, &b)
|
25
|
+
end
|
26
|
+
|
27
|
+
# deliver the message to class of the receiving isolation
|
28
|
+
def deliver_to_class(method_name, return_type, *args, &b)
|
29
|
+
internal_deliver(:class, method_name, return_type, *args, &b)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
# template method for looking up the expectation and/or returning a value
|
35
|
+
def internal_deliver(mode, method_name, return_type, *args, &b)
|
36
|
+
raise NotImplementedError.new("Override in an implementing class")
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def record_call(method_name, mode, expectation, *args, &b)
|
41
|
+
recorder.record_call method_name, mode, expectation, *args, &b if recorder
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# Encapsulates sending messages to Ruby isolations
|
47
|
+
class RubyMessenger < Messenger
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
# implementation of the template method for looking up the expectation and/or returning a value
|
52
|
+
def internal_deliver(mode, method_name, return_type, *args, &b)
|
53
|
+
exp = expectations.find(method_name, mode, *args)
|
54
|
+
bl = record_call(method_name, mode, exp, *args, &b)
|
55
|
+
if exp
|
56
|
+
block = exp.block || b
|
57
|
+
res = instance.__send__(method_name, *args, &block) if exp.super_before?
|
58
|
+
res = exp.execute *args, &bl
|
59
|
+
res = instance.__send__(method_name, *args, &block) if !exp.super_before? and exp.call_super?
|
60
|
+
res
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
68
|
end
|
@@ -1,229 +1,229 @@
|
|
1
|
-
module Caricature
|
2
|
-
|
3
|
-
class BlockCallRecording
|
4
|
-
attr_accessor :call_number
|
5
|
-
attr_reader :args
|
6
|
-
|
7
|
-
def initialize(*args)
|
8
|
-
@call_number = 1
|
9
|
-
@args = args
|
10
|
-
end
|
11
|
-
|
12
|
-
# compares one block variation to another.
|
13
|
-
# Also takes an array as an argument
|
14
|
-
def ==(other)
|
15
|
-
other = self.class.new(other) if other.respond_to?(:each)
|
16
|
-
return true if other.args.first.is_a?(Symbol) and other.args.first == :any
|
17
|
-
other.args == args
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
# A recording of an argument variation.
|
23
|
-
# Every time a method is called with different arguments
|
24
|
-
# the method call recorder will create an ArgumentVariation
|
25
|
-
# these variations are used later in the assertion
|
26
|
-
# to verify against specific argument values
|
27
|
-
class ArgumentRecording
|
28
|
-
|
29
|
-
# contains the arguments of the recorded parameters
|
30
|
-
attr_accessor :args
|
31
|
-
|
32
|
-
# contains the block for the recorded parameters
|
33
|
-
attr_accessor :block
|
34
|
-
|
35
|
-
# the number of the call that has the following parameters
|
36
|
-
attr_accessor :call_number
|
37
|
-
|
38
|
-
# the collection of block calls variations on this argument variation
|
39
|
-
attr_reader :blocks
|
40
|
-
|
41
|
-
# initializes a new instance of an argument recording.
|
42
|
-
# configures it with 1 call count and the args as an +Array+
|
43
|
-
def initialize(args=[], call_number=1, block=nil)
|
44
|
-
@args = args
|
45
|
-
@block = block
|
46
|
-
@blocks = []
|
47
|
-
@call_number = call_number
|
48
|
-
end
|
49
|
-
|
50
|
-
def add_block_variation(*args)
|
51
|
-
bv = find_block_variation(*args)
|
52
|
-
if bv.empty?
|
53
|
-
blocks << BlockCallRecording.new(*args)
|
54
|
-
else
|
55
|
-
bv.first.call_number += 1
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def find_block_variation(*args)
|
60
|
-
return @blocks if args.last.is_a?(Symbol) and args.last == :any
|
61
|
-
@blocks.select { |bv| bv.args == args }
|
62
|
-
end
|
63
|
-
|
64
|
-
# compares one argument variation to another.
|
65
|
-
# Also takes an array as an argument
|
66
|
-
def ==(other)
|
67
|
-
other = self.class.new(other) if other.respond_to?(:each)
|
68
|
-
return true if other.args.first.is_a?(Symbol) and other.args.first == :any
|
69
|
-
other.args == args
|
70
|
-
end
|
71
|
-
|
72
|
-
def to_s
|
73
|
-
"<ArgumentRecording @args=#{args}, @block= #{block}, @call_number=#{call_number}"
|
74
|
-
end
|
75
|
-
alias_method :inspect, :to_s
|
76
|
-
end
|
77
|
-
|
78
|
-
# A recording that represents a method call
|
79
|
-
# it contains argument variations that can be matched too
|
80
|
-
class MethodCallRecording
|
81
|
-
|
82
|
-
# gets or sets the method name
|
83
|
-
attr_accessor :method_name
|
84
|
-
|
85
|
-
# gets or sets the amount of times the method was called
|
86
|
-
attr_accessor :count
|
87
|
-
|
88
|
-
# gets or sets the arguments for this method call
|
89
|
-
attr_accessor :args
|
90
|
-
|
91
|
-
# gets or sets the block for this method call
|
92
|
-
attr_accessor :block
|
93
|
-
|
94
|
-
# Initializes a new instance of a method call recording
|
95
|
-
# every time a method gets called in an isolated object
|
96
|
-
# this gets stored in the method call recorder
|
97
|
-
# It expects a +method_name+ at the very least.
|
98
|
-
def initialize(method_name, count=0)
|
99
|
-
@method_name = method_name
|
100
|
-
@count = count
|
101
|
-
@variations = []
|
102
|
-
end
|
103
|
-
|
104
|
-
# add args
|
105
|
-
def args
|
106
|
-
@variations
|
107
|
-
end
|
108
|
-
|
109
|
-
# indicates if it has an argument variation
|
110
|
-
def has_argument_variations?
|
111
|
-
@variations.size > 1
|
112
|
-
end
|
113
|
-
|
114
|
-
# add an argument variation
|
115
|
-
def add_argument_variation(args, block)
|
116
|
-
variation = find_argument_variations args, nil
|
117
|
-
if variation.empty?
|
118
|
-
@variations << ArgumentRecording.new(args, @variations.size+1, block) if variation == []
|
119
|
-
else
|
120
|
-
variation.first.call_number += 1
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# finds an argument variation that matches the provided +args+
|
125
|
-
def find_argument_variations(args, block_args)
|
126
|
-
return @variations if args.first.is_a?(Symbol) and args.last == :any
|
127
|
-
return match_hash(args, block_args) if args.size == 1 and args.last.is_a?(Hash)
|
128
|
-
return @variations.select { |ar| ar.args == args } if block_args.nil?
|
129
|
-
return @variations.select { |ar| ar.args == args and ar.block } if block_args.last == :any
|
130
|
-
return @variations.select do |ar|
|
131
|
-
av = ar.args == args
|
132
|
-
# idx = 0
|
133
|
-
# ags = ar.args
|
134
|
-
# av = args.all? do |ag|
|
135
|
-
# rag = ags[idx]
|
136
|
-
# res = if ag.is_a?(Hash) and rag.is_a?(Hash)
|
137
|
-
# ag.all? { |k, v| ar.args[idx][k] == v }
|
138
|
-
# else
|
139
|
-
# ag == rag
|
140
|
-
# end
|
141
|
-
# idx += 1
|
142
|
-
# res
|
143
|
-
# end
|
144
|
-
av and not ar.find_block_variation(*block_args).empty?
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def match_hash(args, block_args)
|
149
|
-
@variations.select do |ar|
|
150
|
-
ags = ar.args.last
|
151
|
-
args.last.all? { |k, v| ags[k] == v }
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# The recorder that will collect method calls and provides an interface for finding those recordings
|
157
|
-
class MethodCallRecorder
|
158
|
-
|
159
|
-
# gets the collection of method calls. This is a hash with the method name as key
|
160
|
-
attr_reader :method_calls
|
161
|
-
|
162
|
-
|
163
|
-
# Initializes a new instance of a method call recorder
|
164
|
-
# every time a method gets called in an isolated object
|
165
|
-
# this gets stored in the method call recorder
|
166
|
-
def initialize
|
167
|
-
@method_calls = {:instance => {}, :class => {} }
|
168
|
-
end
|
169
|
-
|
170
|
-
# records a method call or increments the count of how many times this method was called.
|
171
|
-
def record_call(method_name, mode=:instance, expectation=nil, *args, &block)
|
172
|
-
mn_sym = method_name.to_s.underscore.to_sym
|
173
|
-
method_calls[mode][mn_sym] ||= MethodCallRecording.new mn_sym.to_s
|
174
|
-
mc = method_calls[mode][mn_sym]
|
175
|
-
mc.count += 1
|
176
|
-
agc = mc.add_argument_variation args, block
|
177
|
-
b = (expectation && expectation.block) ? (expectation.block||block) : block
|
178
|
-
expectation.block = lambda do |*ags|
|
179
|
-
res = nil
|
180
|
-
res = b.call *ags if b
|
181
|
-
agc.first.add_block_variation *ags
|
182
|
-
res
|
183
|
-
end if expectation
|
184
|
-
block.nil? ? nil : (lambda { |*ags|
|
185
|
-
res = nil
|
186
|
-
res = block.call *ags if block
|
187
|
-
agc.first.add_block_variation *ags
|
188
|
-
res
|
189
|
-
})
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
def error
|
194
|
-
@error
|
195
|
-
end
|
196
|
-
|
197
|
-
# returns whether the method was actually called with the specified constraints
|
198
|
-
def was_called?(method_name, block_args, mode=:instance, *args)
|
199
|
-
mc = method_calls[mode][method_name.to_s.underscore.to_sym]
|
200
|
-
if mc
|
201
|
-
vari = mc.find_argument_variations(args, block_args)
|
202
|
-
result = vari.any? { |agv| agv == args }
|
203
|
-
return result if result
|
204
|
-
if args.size == 1 and args.last.is_a?(Hash)
|
205
|
-
result = vari.any? do |agv|
|
206
|
-
agv.args.last.is_a?(Hash) and args.last.all? { |k, v| agv.args.last[k] == v }
|
207
|
-
end
|
208
|
-
end
|
209
|
-
@error = "Arguments don't match.\nYou expected:\n#{args.join(", ")}.\nI did find the following variations:\n#{mc.args.collect {|ar| ar.args.join(', ') }.join("\nand\n")}" unless result
|
210
|
-
result
|
211
|
-
else
|
212
|
-
@error = "Couldn't find a method with name #{method_name}"
|
213
|
-
return !!mc
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
# # indexer that gives you access to the recorded method by method name
|
218
|
-
# def [](method_name)
|
219
|
-
# method_calls[:instance][method_name.to_s.to_sym]
|
220
|
-
# end
|
221
|
-
|
222
|
-
# returns the number of different methods that has been recorderd
|
223
|
-
def size
|
224
|
-
@method_calls.size
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
|
1
|
+
module Caricature
|
2
|
+
|
3
|
+
class BlockCallRecording
|
4
|
+
attr_accessor :call_number
|
5
|
+
attr_reader :args
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
@call_number = 1
|
9
|
+
@args = args
|
10
|
+
end
|
11
|
+
|
12
|
+
# compares one block variation to another.
|
13
|
+
# Also takes an array as an argument
|
14
|
+
def ==(other)
|
15
|
+
other = self.class.new(other) if other.respond_to?(:each)
|
16
|
+
return true if other.args.first.is_a?(Symbol) and other.args.first == :any
|
17
|
+
other.args == args
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# A recording of an argument variation.
|
23
|
+
# Every time a method is called with different arguments
|
24
|
+
# the method call recorder will create an ArgumentVariation
|
25
|
+
# these variations are used later in the assertion
|
26
|
+
# to verify against specific argument values
|
27
|
+
class ArgumentRecording
|
28
|
+
|
29
|
+
# contains the arguments of the recorded parameters
|
30
|
+
attr_accessor :args
|
31
|
+
|
32
|
+
# contains the block for the recorded parameters
|
33
|
+
attr_accessor :block
|
34
|
+
|
35
|
+
# the number of the call that has the following parameters
|
36
|
+
attr_accessor :call_number
|
37
|
+
|
38
|
+
# the collection of block calls variations on this argument variation
|
39
|
+
attr_reader :blocks
|
40
|
+
|
41
|
+
# initializes a new instance of an argument recording.
|
42
|
+
# configures it with 1 call count and the args as an +Array+
|
43
|
+
def initialize(args=[], call_number=1, block=nil)
|
44
|
+
@args = args
|
45
|
+
@block = block
|
46
|
+
@blocks = []
|
47
|
+
@call_number = call_number
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_block_variation(*args)
|
51
|
+
bv = find_block_variation(*args)
|
52
|
+
if bv.empty?
|
53
|
+
blocks << BlockCallRecording.new(*args)
|
54
|
+
else
|
55
|
+
bv.first.call_number += 1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_block_variation(*args)
|
60
|
+
return @blocks if args.last.is_a?(Symbol) and args.last == :any
|
61
|
+
@blocks.select { |bv| bv.args == args }
|
62
|
+
end
|
63
|
+
|
64
|
+
# compares one argument variation to another.
|
65
|
+
# Also takes an array as an argument
|
66
|
+
def ==(other)
|
67
|
+
other = self.class.new(other) if other.respond_to?(:each)
|
68
|
+
return true if other.args.first.is_a?(Symbol) and other.args.first == :any
|
69
|
+
other.args == args
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"<ArgumentRecording @args=#{args}, @block= #{block}, @call_number=#{call_number}"
|
74
|
+
end
|
75
|
+
alias_method :inspect, :to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
# A recording that represents a method call
|
79
|
+
# it contains argument variations that can be matched too
|
80
|
+
class MethodCallRecording
|
81
|
+
|
82
|
+
# gets or sets the method name
|
83
|
+
attr_accessor :method_name
|
84
|
+
|
85
|
+
# gets or sets the amount of times the method was called
|
86
|
+
attr_accessor :count
|
87
|
+
|
88
|
+
# gets or sets the arguments for this method call
|
89
|
+
attr_accessor :args
|
90
|
+
|
91
|
+
# gets or sets the block for this method call
|
92
|
+
attr_accessor :block
|
93
|
+
|
94
|
+
# Initializes a new instance of a method call recording
|
95
|
+
# every time a method gets called in an isolated object
|
96
|
+
# this gets stored in the method call recorder
|
97
|
+
# It expects a +method_name+ at the very least.
|
98
|
+
def initialize(method_name, count=0)
|
99
|
+
@method_name = method_name
|
100
|
+
@count = count
|
101
|
+
@variations = []
|
102
|
+
end
|
103
|
+
|
104
|
+
# add args
|
105
|
+
def args
|
106
|
+
@variations
|
107
|
+
end
|
108
|
+
|
109
|
+
# indicates if it has an argument variation
|
110
|
+
def has_argument_variations?
|
111
|
+
@variations.size > 1
|
112
|
+
end
|
113
|
+
|
114
|
+
# add an argument variation
|
115
|
+
def add_argument_variation(args, block)
|
116
|
+
variation = find_argument_variations args, nil
|
117
|
+
if variation.empty?
|
118
|
+
@variations << ArgumentRecording.new(args, @variations.size+1, block) if variation == []
|
119
|
+
else
|
120
|
+
variation.first.call_number += 1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# finds an argument variation that matches the provided +args+
|
125
|
+
def find_argument_variations(args, block_args)
|
126
|
+
return @variations if args.first.is_a?(Symbol) and args.last == :any
|
127
|
+
return match_hash(args, block_args) if args.size == 1 and args.last.is_a?(Hash)
|
128
|
+
return @variations.select { |ar| ar.args == args } if block_args.nil?
|
129
|
+
return @variations.select { |ar| ar.args == args and ar.block } if block_args.last == :any
|
130
|
+
return @variations.select do |ar|
|
131
|
+
av = ar.args == args
|
132
|
+
# idx = 0
|
133
|
+
# ags = ar.args
|
134
|
+
# av = args.all? do |ag|
|
135
|
+
# rag = ags[idx]
|
136
|
+
# res = if ag.is_a?(Hash) and rag.is_a?(Hash)
|
137
|
+
# ag.all? { |k, v| ar.args[idx][k] == v }
|
138
|
+
# else
|
139
|
+
# ag == rag
|
140
|
+
# end
|
141
|
+
# idx += 1
|
142
|
+
# res
|
143
|
+
# end
|
144
|
+
av and not ar.find_block_variation(*block_args).empty?
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def match_hash(args, block_args)
|
149
|
+
@variations.select do |ar|
|
150
|
+
ags = ar.args.last
|
151
|
+
args.last.all? { |k, v| ags[k] == v }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# The recorder that will collect method calls and provides an interface for finding those recordings
|
157
|
+
class MethodCallRecorder
|
158
|
+
|
159
|
+
# gets the collection of method calls. This is a hash with the method name as key
|
160
|
+
attr_reader :method_calls
|
161
|
+
|
162
|
+
|
163
|
+
# Initializes a new instance of a method call recorder
|
164
|
+
# every time a method gets called in an isolated object
|
165
|
+
# this gets stored in the method call recorder
|
166
|
+
def initialize
|
167
|
+
@method_calls = {:instance => {}, :class => {} }
|
168
|
+
end
|
169
|
+
|
170
|
+
# records a method call or increments the count of how many times this method was called.
|
171
|
+
def record_call(method_name, mode=:instance, expectation=nil, *args, &block)
|
172
|
+
mn_sym = method_name.to_s.underscore.to_sym
|
173
|
+
method_calls[mode][mn_sym] ||= MethodCallRecording.new mn_sym.to_s
|
174
|
+
mc = method_calls[mode][mn_sym]
|
175
|
+
mc.count += 1
|
176
|
+
agc = mc.add_argument_variation args, block
|
177
|
+
b = (expectation && expectation.block) ? (expectation.block||block) : block
|
178
|
+
expectation.block = lambda do |*ags|
|
179
|
+
res = nil
|
180
|
+
res = b.call *ags if b
|
181
|
+
agc.first.add_block_variation *ags
|
182
|
+
res
|
183
|
+
end if expectation
|
184
|
+
block.nil? ? nil : (lambda { |*ags|
|
185
|
+
res = nil
|
186
|
+
res = block.call *ags if block
|
187
|
+
agc.first.add_block_variation *ags
|
188
|
+
res
|
189
|
+
})
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def error
|
194
|
+
@error
|
195
|
+
end
|
196
|
+
|
197
|
+
# returns whether the method was actually called with the specified constraints
|
198
|
+
def was_called?(method_name, block_args, mode=:instance, *args)
|
199
|
+
mc = method_calls[mode][method_name.to_s.underscore.to_sym]
|
200
|
+
if mc
|
201
|
+
vari = mc.find_argument_variations(args, block_args)
|
202
|
+
result = vari.any? { |agv| agv == args }
|
203
|
+
return result if result
|
204
|
+
if args.size == 1 and args.last.is_a?(Hash)
|
205
|
+
result = vari.any? do |agv|
|
206
|
+
agv.args.last.is_a?(Hash) and args.last.all? { |k, v| agv.args.last[k] == v }
|
207
|
+
end
|
208
|
+
end
|
209
|
+
@error = "Arguments don't match.\nYou expected:\n#{args.join(", ")}.\nI did find the following variations:\n#{mc.args.collect {|ar| ar.args.join(', ') }.join("\nand\n")}" unless result
|
210
|
+
result
|
211
|
+
else
|
212
|
+
@error = "Couldn't find a method with name #{method_name}"
|
213
|
+
return !!mc
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# # indexer that gives you access to the recorded method by method name
|
218
|
+
# def [](method_name)
|
219
|
+
# method_calls[:instance][method_name.to_s.to_sym]
|
220
|
+
# end
|
221
|
+
|
222
|
+
# returns the number of different methods that has been recorderd
|
223
|
+
def size
|
224
|
+
@method_calls.size
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
229
|
end
|