lib/rr/recorded_calls.rb
module RR
class RecordedCalls
include RR::Space::Reader
def initialize(recorded_calls=[])
@recorded_calls = recorded_calls
@ordered_index = 0
end
attr_reader :recorded_calls
def [](index)
@recorded_calls[index]
end
def clear
self.ordered_index = 0
recorded_calls.clear
end
def add(subject, method_name, arguments, keyword_arguments, block)
recorded_calls << RecordedCall.new(subject,
method_name,
arguments,
keyword_arguments,
block)
end
def any?(&block)
recorded_calls.any?(&block)
end
def ==(other)
recorded_calls == other.recorded_calls
end
def match_error(spy_verification)
double_injection_exists_error(spy_verification) || begin
if spy_verification.ordered?
ordered_match_error(spy_verification)
else
unordered_match_error(spy_verification)
end
end
end
protected
attr_accessor :ordered_index
def double_injection_exists_error(spy_verification)
unless Injections::DoubleInjection.exists_by_subject?(spy_verification.subject, spy_verification.method_name)
RR::Errors.build_error(RR::Errors::SpyVerificationErrors::DoubleInjectionNotFoundError,
"A Double Injection for the subject and method call:\n" <<
"#{spy_verification.subject_inspect}\n" <<
"#{spy_verification.method_name}\ndoes not exist in:\n" <<
"\t#{recorded_calls.map {|call| call.inspect }.join("\n\t")}"
)
end
end
def ordered_match_error(spy_verification)
memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
if memoized_matching_recorded_calls.last
self.ordered_index = recorded_calls.index(memoized_matching_recorded_calls.last)
end
(0..memoized_matching_recorded_calls.size).to_a.any? do |i|
spy_verification.times_matcher.matches?(i)
end ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
end
def unordered_match_error(spy_verification)
memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
spy_verification.times_matcher.matches?(
memoized_matching_recorded_calls.size
) ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
end
def matching_recorded_calls(spy_verification)
recorded_calls[ordered_index..-1].
select(&match_double_injection(spy_verification)).
select(&match_argument_expectation(spy_verification))
end
def match_double_injection(spy_verification)
lambda do |recorded_call|
recorded_call.subject == spy_verification.subject &&
recorded_call.method_name == spy_verification.method_name
end
end
def match_argument_expectation(spy_verification)
lambda do |recorded_call|
expectation = spy_verification.argument_expectation
arguments = recorded_call.arguments
keyword_arguments = recorded_call.keyword_arguments
expectation.exact_match?(arguments, keyword_arguments) ||
expectation.wildcard_match?(arguments, keyword_arguments)
end
end
def invocation_count_error(spy_verification, matching_recorded_calls)
method_name = spy_verification.method_name
arguments = spy_verification.argument_expectation.expected_arguments
keyword_arguments = spy_verification.argument_expectation.expected_keyword_arguments
RR::Errors.build_error(RR::Errors::SpyVerificationErrors::InvocationCountError,
"On subject #{spy_verification.subject.inspect}\n" <<
"Expected #{Double.formatted_name(method_name, arguments, keyword_arguments)}\n" <<
"to be called #{spy_verification.times_matcher.expected_times_message},\n" <<
"but was called #{matching_recorded_calls.size} times.\n" <<
"All of the method calls related to Doubles are:\n" <<
"\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
)
end
end
end