Updated to latest rspec
[gitorious:georgyos-clone.git] / vendor / plugins / rspec / lib / spec / mocks / message_expectation.rb
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