Blob of rspec/lib/spec/mocks/message_expectation.rb (raw blob data)

1 module Spec
2 module Mocks
3
4 class BaseExpectation
5 attr_reader :sym
6
7 def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={})
8 @error_generator = error_generator
9 @error_generator.opts = opts
10 @expected_from = expected_from
11 @sym = sym
12 @method_block = method_block
13 @return_block = nil
14 @actual_received_count = 0
15 @expected_received_count = expected_received_count
16 @args_expectation = ArgumentExpectation.new([AnyArgsConstraint.new])
17 @consecutive = false
18 @exception_to_raise = nil
19 @symbol_to_throw = nil
20 @order_group = expectation_ordering
21 @at_least = nil
22 @at_most = nil
23 @args_to_yield = []
24 end
25
26 def expected_args
27 @args_expectation.args
28 end
29
30 def and_return(*values, &return_block)
31 Kernel::raise AmbiguousReturnError unless @method_block.nil?
32 case values.size
33 when 0 then value = nil
34 when 1 then value = values[0]
35 else
36 value = values
37 @consecutive = true
38 @expected_received_count = values.size if !ignoring_args? &&
39 @expected_received_count < values.size
40 end
41 @return_block = block_given? ? return_block : lambda { value }
42 # Ruby 1.9 - see where this is used below
43 @ignore_args = !block_given?
44 end
45
46 # :call-seq:
47 # and_raise()
48 # and_raise(Exception) #any exception class
49 # and_raise(exception) #any exception object
50 #
51 # == Warning
52 #
53 # When you pass an exception class, the MessageExpectation will
54 # raise an instance of it, creating it with +new+. If the exception
55 # class initializer requires any parameters, you must pass in an
56 # instance and not the class.
57 def and_raise(exception=Exception)
58 @exception_to_raise = exception
59 end
60
61 def and_throw(symbol)
62 @symbol_to_throw = symbol
63 end
64
65 def and_yield(*args)
66 @args_to_yield << args
67 self
68 end
69
70 def matches(sym, args)
71 @sym == sym and @args_expectation.check_args(args)
72 end
73
74 def invoke(args, block)
75 if @expected_received_count == 0
76 @actual_received_count += 1
77 @error_generator.raise_expectation_error @sym, @expected_received_count, @actual_received_count, *args
78 end
79
80 @order_group.handle_order_constraint self
81
82 begin
83 Kernel::raise @exception_to_raise unless @exception_to_raise.nil?
84 Kernel::throw @symbol_to_throw unless @symbol_to_throw.nil?
85
86
87 if !@method_block.nil?
88 default_return_val = invoke_method_block(args)
89 elsif @args_to_yield.size > 0
90 default_return_val = invoke_with_yield(block)
91 else
92 default_return_val = nil
93 end
94
95 if @consecutive
96 return invoke_consecutive_return_block(args, block)
97 elsif @return_block
98 return invoke_return_block(args, block)
99 else
100 return default_return_val
101 end
102 ensure
103 @actual_received_count += 1
104 end
105 end
106
107 protected
108
109 def invoke_method_block(args)
110 begin
111 @method_block.call(*args)
112 rescue => detail
113 @error_generator.raise_block_failed_error @sym, detail.message
114 end
115 end
116
117 def invoke_with_yield(block)
118 if block.nil?
119 @error_generator.raise_missing_block_error @args_to_yield
120 end
121 value = nil
122 @args_to_yield.each do |args_to_yield_this_time|
123 if block.arity > -1 && args_to_yield_this_time.length != block.arity
124 @error_generator.raise_wrong_arity_error args_to_yield_this_time, block.arity
125 end
126 value = block.call(*args_to_yield_this_time)
127 end
128 value
129 end
130
131 def invoke_consecutive_return_block(args, block)
132 args << block unless block.nil?
133 value = @return_block.call(*args)
134
135 index = [@actual_received_count, value.size-1].min
136 value[index]
137 end
138
139 def invoke_return_block(args, block)
140 args << block unless block.nil?
141 # Ruby 1.9 - when we set @return_block to return values
142 # regardless of arguments, any arguments will result in
143 # a "wrong number of arguments" error
144 if @ignore_args
145 @return_block.call()
146 else
147 @return_block.call(*args)
148 end
149 end
150 end
151
152 class MessageExpectation < BaseExpectation
153
154 def matches_name_but_not_args(sym, args)
155 @sym == sym and not @args_expectation.check_args(args)
156 end
157
158 def verify_messages_received
159 return if expected_messages_received?
160
161 generate_error
162 rescue Spec::Mocks::MockExpectationError => error
163 error.backtrace.insert(0, @expected_from)
164 Kernel::raise error
165 end
166
167 def expected_messages_received?
168 ignoring_args? || matches_exact_count? ||
169 matches_at_least_count? || matches_at_most_count?
170 end
171
172 def ignoring_args?
173 @expected_received_count == :any
174 end
175
176 def matches_at_least_count?
177 @at_least && @actual_received_count >= @expected_received_count
178 end
179
180 def matches_at_most_count?
181 @at_most && @actual_received_count <= @expected_received_count
182 end
183
184 def matches_exact_count?
185 @expected_received_count == @actual_received_count
186 end
187
188 def similar_messages
189 @similar_messages ||= []
190 end
191
192 def advise(args, block)
193 similar_messages << args
194 end
195
196 def generate_error
197 if similar_messages.empty?
198 @error_generator.raise_expectation_error(@sym, @expected_received_count, @actual_received_count, *@args_expectation.args)
199 else
200 @error_generator.raise_unexpected_message_args_error(self, *@similar_messages.first)
201 end
202 end
203
204 def with(*args, &block)
205 @method_block = block if block
206 @args_expectation = ArgumentExpectation.new(args)
207 self
208 end
209
210 def exactly(n)
211 set_expected_received_count :exactly, n
212 self
213 end
214
215 def at_least(n)
216 set_expected_received_count :at_least, n
217 self
218 end
219
220 def at_most(n)
221 set_expected_received_count :at_most, n
222 self
223 end
224
225 def times(&block)
226 @method_block = block if block
227 self
228 end
229
230 def any_number_of_times(&block)
231 @method_block = block if block
232 @expected_received_count = :any
233 self
234 end
235
236 def never
237 @expected_received_count = 0
238 self
239 end
240
241 def once(&block)
242 @method_block = block if block
243 @expected_received_count = 1
244 self
245 end
246
247 def twice(&block)
248 @method_block = block if block
249 @expected_received_count = 2
250 self
251 end
252
253 def ordered(&block)
254 @method_block = block if block
255 @order_group.register(self)
256 @ordered = true
257 self
258 end
259
260 def negative_expectation_for?(sym)
261 return false
262 end
263
264 protected
265 def set_expected_received_count(relativity, n)
266 @at_least = (relativity == :at_least)
267 @at_most = (relativity == :at_most)
268 @expected_received_count = case n
269 when Numeric
270 n
271 when :once
272 1
273 when :twice
274 2
275 end
276 end
277
278 end
279
280 class NegativeMessageExpectation < MessageExpectation
281 def initialize(message, expectation_ordering, expected_from, sym, method_block)
282 super(message, expectation_ordering, expected_from, sym, method_block, 0)
283 end
284
285 def negative_expectation_for?(sym)
286 return @sym == sym
287 end
288 end
289
290 end
291 end