Skip to content

Commit

Permalink
Merge 98426ca into 45c4524
Browse files Browse the repository at this point in the history
  • Loading branch information
JonRowe committed Oct 23, 2013
2 parents 45c4524 + 98426ca commit 4dd903f
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 12 deletions.
4 changes: 2 additions & 2 deletions lib/rspec/mocks/matchers/have_received.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Matchers
class HaveReceived
COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice)
ARGS_CONSTRAINTS = %w(with)
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w(ordered)

def initialize(method_name, &block)
@method_name = method_name
Expand Down Expand Up @@ -86,7 +86,7 @@ def generate_failure_message

def expected_messages_received?
mock_proxy.replay_received_message_on @expectation, &@block
@expectation.expected_messages_received?
@expectation.expected_messages_received? && @expectation.expected_ordering_received?
end

def mock_proxy
Expand Down
12 changes: 11 additions & 1 deletion lib/rspec/mocks/message_expectation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ def initialize(error_generator, expectation_ordering, expected_from, method_doub
@expected_received_count = expected_received_count
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
@order_group = expectation_ordering
@order_group.register(self)
@ordered = false
@at_least = @at_most = @exactly = nil
@args_to_yield = []
@failed_fast = nil
Expand Down Expand Up @@ -277,6 +279,10 @@ def expected_messages_received?
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
end

def expected_ordering_received?
!@ordered || @order_group.verify_invokation_order(self)
end

# @private
def ignoring_args?
@expected_received_count == :any
Expand Down Expand Up @@ -462,11 +468,15 @@ def twice(&block)
# api.should_receive(:finish).ordered
def ordered(&block)
self.inner_implementation_action = block
@order_group.register(self)
@ordered = true
self
end

# @private
def ordered?
@ordered
end

# @private
def negative_expectation_for?(message)
@message == message && negative?
Expand Down
52 changes: 45 additions & 7 deletions lib/rspec/mocks/order_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,76 @@ module Mocks
# @private
class OrderGroup
def initialize
@ordering = Array.new
@expectations = []
@invokation_order = []
@index = 0
end

# @private
def register(expectation)
@ordering << expectation
@expectations << expectation
end

def invoked(object, message)
@invokation_order << [object, message]
end

# @private
def ready_for?(expectation)
@ordering.first == expectation
first_ordered == expectation
end

# @private
def consume
@ordering.shift
expectation = first_ordered
@index = @expectations.find_index(expectation) + 1
expectation
end

# @private
def handle_order_constraint(expectation)
return unless @ordering.include?(expectation)
return unless expectation.ordered? && @expectations.include?(expectation)
return consume if ready_for?(expectation)
expectation.raise_out_of_order_error
end

def verify_invokation_order(expectation)
expectation.raise_out_of_order_error unless expectations_invoked_in_order?
true
end

def clear
@ordering.clear
@index = 0
@invokation_order.clear
@expectations.clear
end

def empty?
@ordering.empty?
@expectations.empty?
end

private

def first_ordered
(@expectations[@index..-1] || []).find { |expectation| expectation.ordered? }
end

def expectations_invoked_in_order?
invoked_expectations == expected_invokations
end

def invoked_expectations
@expectations.select { |e| e.ordered? && @invokation_order.include?([e.orig_object,e.message]) }
end

def expected_invokations
@invokation_order.map { |invokation| expectation_for(*invokation) }.compact
end

def expectation_for(object, message)
@expectations.find { |e| e.orig_object == object && e.message == message }
end

end
end
end
11 changes: 11 additions & 0 deletions lib/rspec/mocks/proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ module RSpec
module Mocks
# @private
class Proxy
class NullOrderGroup
def invoked(object, message)
end
end

# @private
def initialize(object, name=nil, options={})
Expand All @@ -11,9 +15,15 @@ def initialize(object, name=nil, options={})
@expectation_ordering = RSpec::Mocks::space.expectation_ordering
@messages_received = []
@options = options
@order_group = NullOrderGroup.new
@null_object = false
end

def with_order_group(group)
@order_group = group
self
end

# @private
attr_reader :object

Expand Down Expand Up @@ -139,6 +149,7 @@ def has_negative_expectation?(message)

# @private
def record_message_received(message, *args, &block)
@order_group.invoked(object, message)
@messages_received << [message, args, block]
end

Expand Down
4 changes: 2 additions & 2 deletions lib/rspec/mocks/space.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ def proxy_for(object)
proxies.fetch(id) do
proxies[id] = case object
when NilClass then ProxyForNil.new
when TestDouble then object.__build_mock_proxy
when TestDouble then object.__build_mock_proxy.with_order_group(expectation_ordering)
else
PartialMockProxy.new(object)
PartialMockProxy.new(object).with_order_group(expectation_ordering)
end
end
end
Expand Down
22 changes: 22 additions & 0 deletions spec/rspec/mocks/matchers/have_received_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,28 @@ module Mocks
end
end
end

context 'ordered' do
let(:dbl) { double one: 1, two: 2 }

it 'passes when the messages were received in order' do
dbl.one
dbl.two

expect(dbl).to have_received(:one).ordered
expect(dbl).to have_received(:two).ordered
end

it 'fails when the messages are received out of order' do
dbl.two
dbl.one

expect {
expect(dbl).to have_received(:one).ordered
expect(dbl).to have_received(:two).ordered
}.to raise_error(/received :two out of order/m)
end
end
end

describe "expect(...).not_to have_received" do
Expand Down

0 comments on commit 4dd903f

Please sign in to comment.