jferris-rr 0.7.1.0.1239654108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +196 -0
- data/README.rdoc +329 -0
- data/Rakefile +77 -0
- data/lib/rr.rb +84 -0
- data/lib/rr/adapters/rr_methods.rb +122 -0
- data/lib/rr/adapters/rspec.rb +58 -0
- data/lib/rr/adapters/test_unit.rb +29 -0
- data/lib/rr/double.rb +212 -0
- data/lib/rr/double_definitions/child_double_definition_creator.rb +27 -0
- data/lib/rr/double_definitions/double_definition.rb +346 -0
- data/lib/rr/double_definitions/double_definition_creator.rb +167 -0
- data/lib/rr/double_definitions/double_definition_creator_proxy.rb +37 -0
- data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +15 -0
- data/lib/rr/double_definitions/strategies/implementation/proxy.rb +62 -0
- data/lib/rr/double_definitions/strategies/implementation/reimplementation.rb +14 -0
- data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
- data/lib/rr/double_definitions/strategies/scope/instance.rb +15 -0
- data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +46 -0
- data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +15 -0
- data/lib/rr/double_definitions/strategies/strategy.rb +70 -0
- data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +34 -0
- data/lib/rr/double_definitions/strategies/verification/mock.rb +44 -0
- data/lib/rr/double_definitions/strategies/verification/stub.rb +45 -0
- data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +15 -0
- data/lib/rr/double_injection.rb +143 -0
- data/lib/rr/double_matches.rb +51 -0
- data/lib/rr/errors/argument_equality_error.rb +6 -0
- data/lib/rr/errors/double_definition_error.rb +6 -0
- data/lib/rr/errors/double_not_found_error.rb +6 -0
- data/lib/rr/errors/double_order_error.rb +6 -0
- data/lib/rr/errors/rr_error.rb +20 -0
- data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
- data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
- data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
- data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
- data/lib/rr/errors/times_called_error.rb +6 -0
- data/lib/rr/expectations/any_argument_expectation.rb +21 -0
- data/lib/rr/expectations/argument_equality_expectation.rb +41 -0
- data/lib/rr/expectations/times_called_expectation.rb +57 -0
- data/lib/rr/hash_with_object_id_key.rb +41 -0
- data/lib/rr/recorded_calls.rb +103 -0
- data/lib/rr/space.rb +123 -0
- data/lib/rr/spy_verification.rb +48 -0
- data/lib/rr/spy_verification_proxy.rb +18 -0
- data/lib/rr/times_called_matchers/any_times_matcher.rb +18 -0
- data/lib/rr/times_called_matchers/at_least_matcher.rb +15 -0
- data/lib/rr/times_called_matchers/at_most_matcher.rb +23 -0
- data/lib/rr/times_called_matchers/integer_matcher.rb +19 -0
- data/lib/rr/times_called_matchers/non_terminal.rb +27 -0
- data/lib/rr/times_called_matchers/proc_matcher.rb +11 -0
- data/lib/rr/times_called_matchers/range_matcher.rb +21 -0
- data/lib/rr/times_called_matchers/terminal.rb +20 -0
- data/lib/rr/times_called_matchers/times_called_matcher.rb +44 -0
- data/lib/rr/wildcard_matchers/anything.rb +18 -0
- data/lib/rr/wildcard_matchers/boolean.rb +23 -0
- data/lib/rr/wildcard_matchers/duck_type.rb +32 -0
- data/lib/rr/wildcard_matchers/hash_including.rb +29 -0
- data/lib/rr/wildcard_matchers/is_a.rb +25 -0
- data/lib/rr/wildcard_matchers/numeric.rb +13 -0
- data/lib/rr/wildcard_matchers/range.rb +7 -0
- data/lib/rr/wildcard_matchers/regexp.rb +7 -0
- data/lib/rr/wildcard_matchers/satisfy.rb +26 -0
- data/spec/core_spec_suite.rb +19 -0
- data/spec/environment_fixture_setup.rb +6 -0
- data/spec/high_level_spec.rb +368 -0
- data/spec/rr/adapters/rr_methods_argument_matcher_spec.rb +67 -0
- data/spec/rr/adapters/rr_methods_creator_spec.rb +149 -0
- data/spec/rr/adapters/rr_methods_space_spec.rb +115 -0
- data/spec/rr/adapters/rr_methods_spec_helper.rb +11 -0
- data/spec/rr/adapters/rr_methods_times_matcher_spec.rb +17 -0
- data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +112 -0
- data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +155 -0
- data/spec/rr/double_definitions/double_definition_creator_spec.rb +502 -0
- data/spec/rr/double_definitions/double_definition_spec.rb +1159 -0
- data/spec/rr/double_injection/double_injection_bind_spec.rb +111 -0
- data/spec/rr/double_injection/double_injection_dispatching_spec.rb +244 -0
- data/spec/rr/double_injection/double_injection_has_original_method_spec.rb +55 -0
- data/spec/rr/double_injection/double_injection_reset_spec.rb +90 -0
- data/spec/rr/double_injection/double_injection_spec.rb +77 -0
- data/spec/rr/double_injection/double_injection_verify_spec.rb +29 -0
- data/spec/rr/double_spec.rb +352 -0
- data/spec/rr/errors/rr_error_spec.rb +67 -0
- data/spec/rr/expectations/any_argument_expectation_spec.rb +47 -0
- data/spec/rr/expectations/anything_argument_equality_expectation_spec.rb +14 -0
- data/spec/rr/expectations/argument_equality_expectation_spec.rb +135 -0
- data/spec/rr/expectations/boolean_argument_equality_expectation_spec.rb +34 -0
- data/spec/rr/expectations/hash_including_argument_equality_expectation_spec.rb +82 -0
- data/spec/rr/expectations/hash_including_spec.rb +17 -0
- data/spec/rr/expectations/satisfy_argument_equality_expectation_spec.rb +59 -0
- data/spec/rr/expectations/satisfy_spec.rb +14 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +46 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +69 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +71 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +23 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +104 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +81 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +83 -0
- data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +38 -0
- data/spec/rr/rspec/invocation_matcher_spec.rb +279 -0
- data/spec/rr/rspec/rspec_adapter_spec.rb +66 -0
- data/spec/rr/rspec/rspec_backtrace_tweaking_spec.rb +31 -0
- data/spec/rr/rspec/rspec_backtrace_tweaking_spec_fixture.rb +11 -0
- data/spec/rr/rspec/rspec_usage_spec.rb +86 -0
- data/spec/rr/space/hash_with_object_id_key_spec.rb +88 -0
- data/spec/rr/space/space_spec.rb +542 -0
- data/spec/rr/test_unit/test_helper.rb +7 -0
- data/spec/rr/test_unit/test_unit_backtrace_test.rb +35 -0
- data/spec/rr/test_unit/test_unit_integration_test.rb +57 -0
- data/spec/rr/times_called_matchers/any_times_matcher_spec.rb +47 -0
- data/spec/rr/times_called_matchers/at_least_matcher_spec.rb +55 -0
- data/spec/rr/times_called_matchers/at_most_matcher_spec.rb +70 -0
- data/spec/rr/times_called_matchers/integer_matcher_spec.rb +70 -0
- data/spec/rr/times_called_matchers/proc_matcher_spec.rb +55 -0
- data/spec/rr/times_called_matchers/range_matcher_spec.rb +76 -0
- data/spec/rr/times_called_matchers/times_called_matcher_spec.rb +118 -0
- data/spec/rr/wildcard_matchers/anything_spec.rb +24 -0
- data/spec/rr/wildcard_matchers/boolean_spec.rb +36 -0
- data/spec/rr/wildcard_matchers/duck_type_spec.rb +52 -0
- data/spec/rr/wildcard_matchers/is_a_spec.rb +32 -0
- data/spec/rr/wildcard_matchers/numeric_spec.rb +32 -0
- data/spec/rr/wildcard_matchers/range_spec.rb +35 -0
- data/spec/rr/wildcard_matchers/regexp_spec.rb +43 -0
- data/spec/rr_spec.rb +28 -0
- data/spec/rspec_spec_suite.rb +16 -0
- data/spec/spec_helper.rb +107 -0
- data/spec/spec_suite.rb +27 -0
- data/spec/spy_verification_spec.rb +129 -0
- data/spec/test_unit_spec_suite.rb +21 -0
- metadata +187 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
class RecordedCalls
|
|
3
|
+
include RR::Space::Reader
|
|
4
|
+
|
|
5
|
+
def initialize(recorded_calls=[])
|
|
6
|
+
@recorded_calls = recorded_calls
|
|
7
|
+
@ordered_index = 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :recorded_calls
|
|
11
|
+
|
|
12
|
+
def clear
|
|
13
|
+
self.ordered_index = 0
|
|
14
|
+
recorded_calls.clear
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def <<(recorded_call)
|
|
18
|
+
recorded_calls << recorded_call
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def any?(&block)
|
|
22
|
+
recorded_calls.any?(&block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def ==(other)
|
|
26
|
+
recorded_calls == other.recorded_calls
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def match_error(spy_verification)
|
|
30
|
+
double_injection_exists_error(spy_verification) || begin
|
|
31
|
+
if spy_verification.ordered?
|
|
32
|
+
ordered_match_error(spy_verification)
|
|
33
|
+
else
|
|
34
|
+
unordered_match_error(spy_verification)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
protected
|
|
40
|
+
attr_accessor :ordered_index
|
|
41
|
+
|
|
42
|
+
def double_injection_exists_error(spy_verification)
|
|
43
|
+
unless space.double_injection_exists?(spy_verification.subject, spy_verification.method_name)
|
|
44
|
+
RR::Errors::SpyVerificationErrors::DoubleInjectionNotFoundError.new(
|
|
45
|
+
"A Double Injection for the subject and method call:\n" <<
|
|
46
|
+
"#{spy_verification.subject.inspect}\n" <<
|
|
47
|
+
"#{spy_verification.method_name}\ndoes not exist in:\n" <<
|
|
48
|
+
"\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def ordered_match_error(spy_verification)
|
|
54
|
+
memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
|
|
55
|
+
|
|
56
|
+
if memoized_matching_recorded_calls.last
|
|
57
|
+
self.ordered_index = recorded_calls.index(memoized_matching_recorded_calls.last)
|
|
58
|
+
end
|
|
59
|
+
(0..memoized_matching_recorded_calls.size).to_a.any? do |i|
|
|
60
|
+
spy_verification.times_matcher.matches?(i)
|
|
61
|
+
end ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def unordered_match_error(spy_verification)
|
|
65
|
+
memoized_matching_recorded_calls = matching_recorded_calls(spy_verification)
|
|
66
|
+
|
|
67
|
+
spy_verification.times_matcher.matches?(
|
|
68
|
+
memoized_matching_recorded_calls.size
|
|
69
|
+
) ? nil : invocation_count_error(spy_verification, memoized_matching_recorded_calls)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def matching_recorded_calls(spy_verification)
|
|
73
|
+
recorded_calls[ordered_index..-1].
|
|
74
|
+
select(&match_double_injection(spy_verification)).
|
|
75
|
+
select(&match_argument_expectation(spy_verification))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def match_double_injection(spy_verification)
|
|
79
|
+
lambda do |recorded_call|
|
|
80
|
+
recorded_call[0] == spy_verification.subject &&
|
|
81
|
+
recorded_call[1] == spy_verification.method_name
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def match_argument_expectation(spy_verification)
|
|
86
|
+
lambda do |recorded_call|
|
|
87
|
+
spy_verification.argument_expectation.exact_match?(*recorded_call[2]) ||
|
|
88
|
+
spy_verification.argument_expectation.wildcard_match?(*recorded_call[2])
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def invocation_count_error(spy_verification, matching_recorded_calls)
|
|
93
|
+
RR::Errors::SpyVerificationErrors::InvocationCountError.new(
|
|
94
|
+
"On subject #{spy_verification.subject.inspect}\n" <<
|
|
95
|
+
"Expected #{Double.formatted_name(spy_verification.method_name, spy_verification.argument_expectation.expected_arguments)}\n" <<
|
|
96
|
+
"to be called #{spy_verification.times_matcher.expected_times_message},\n" <<
|
|
97
|
+
"but was called #{matching_recorded_calls.size} times.\n" <<
|
|
98
|
+
"All of the method calls related to Doubles are:\n" <<
|
|
99
|
+
"\t#{recorded_calls.map {|call| call.inspect}.join("\n\t")}"
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/rr/space.rb
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
# RR::Space.instance is the global state subject for the RR framework.
|
|
3
|
+
class Space
|
|
4
|
+
module Reader
|
|
5
|
+
def space
|
|
6
|
+
RR::Space.instance
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def instance
|
|
12
|
+
@instance ||= new
|
|
13
|
+
end
|
|
14
|
+
attr_writer :instance
|
|
15
|
+
|
|
16
|
+
protected
|
|
17
|
+
def method_missing(method_name, *args, &block)
|
|
18
|
+
instance.__send__(method_name, *args, &block)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :double_injections, :ordered_doubles, :recorded_calls
|
|
23
|
+
attr_accessor :trim_backtrace
|
|
24
|
+
def initialize
|
|
25
|
+
@double_injections = HashWithObjectIdKey.new
|
|
26
|
+
@ordered_doubles = []
|
|
27
|
+
@trim_backtrace = false
|
|
28
|
+
@recorded_calls = RR::RecordedCalls.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Reuses or creates, if none exists, a DoubleInjection for the passed
|
|
32
|
+
# in subject and method_name.
|
|
33
|
+
# When a DoubleInjection is created, it binds the dispatcher to the
|
|
34
|
+
# subject.
|
|
35
|
+
def double_injection(subject, method_name)
|
|
36
|
+
@double_injections[subject][method_name.to_sym] ||= begin
|
|
37
|
+
DoubleInjection.new(subject, method_name.to_sym).bind
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def double_injection_exists?(subject, method_name)
|
|
42
|
+
!!@double_injections[subject][method_name.to_sym]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Registers the ordered Double to be verified.
|
|
46
|
+
def register_ordered_double(double)
|
|
47
|
+
@ordered_doubles << double unless ordered_doubles.include?(double)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Verifies that the passed in ordered Double is being called
|
|
51
|
+
# in the correct position.
|
|
52
|
+
def verify_ordered_double(double)
|
|
53
|
+
unless double.terminal?
|
|
54
|
+
raise Errors::DoubleOrderError,
|
|
55
|
+
"Ordered Doubles cannot have a NonTerminal TimesCalledExpectation"
|
|
56
|
+
end
|
|
57
|
+
unless @ordered_doubles.first == double
|
|
58
|
+
message = Double.formatted_name(double.method_name, double.expected_arguments)
|
|
59
|
+
message << " called out of order in list\n"
|
|
60
|
+
message << Double.list_message_part(@ordered_doubles)
|
|
61
|
+
raise Errors::DoubleOrderError, message
|
|
62
|
+
end
|
|
63
|
+
@ordered_doubles.shift unless double.attempt?
|
|
64
|
+
double
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Verifies all the DoubleInjection objects have met their
|
|
68
|
+
# TimesCalledExpectations.
|
|
69
|
+
def verify_doubles(*objects)
|
|
70
|
+
objects = @double_injections.keys if objects.empty?
|
|
71
|
+
objects.each do |subject|
|
|
72
|
+
@double_injections[subject].keys.each do |method_name|
|
|
73
|
+
verify_double(subject, method_name)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
alias_method :verify, :verify_doubles
|
|
78
|
+
|
|
79
|
+
# Resets the registered Doubles and ordered Doubles
|
|
80
|
+
def reset
|
|
81
|
+
reset_ordered_doubles
|
|
82
|
+
reset_double_injections
|
|
83
|
+
reset_recorded_calls
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Verifies the DoubleInjection for the passed in subject and method_name.
|
|
87
|
+
def verify_double(subject, method_name)
|
|
88
|
+
@double_injections[subject][method_name].verify
|
|
89
|
+
ensure
|
|
90
|
+
reset_double subject, method_name
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Resets the DoubleInjection for the passed in subject and method_name.
|
|
94
|
+
def reset_double(subject, method_name)
|
|
95
|
+
double_injection = @double_injections[subject].delete(method_name)
|
|
96
|
+
@double_injections.delete(subject) if @double_injections[subject].empty?
|
|
97
|
+
double_injection.reset
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def record_call(subject, method_name, arguments, block)
|
|
101
|
+
@recorded_calls << [subject, method_name, arguments, block]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
protected
|
|
105
|
+
# Removes the ordered Doubles from the list
|
|
106
|
+
def reset_ordered_doubles
|
|
107
|
+
@ordered_doubles.clear
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Resets the registered Doubles for the next test run.
|
|
111
|
+
def reset_double_injections
|
|
112
|
+
@double_injections.each do |subject, method_double_map|
|
|
113
|
+
method_double_map.keys.each do |method_name|
|
|
114
|
+
reset_double(subject, method_name)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def reset_recorded_calls
|
|
120
|
+
@recorded_calls.clear
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
class SpyVerification
|
|
3
|
+
def initialize(subject, method_name, args)
|
|
4
|
+
@subject = subject
|
|
5
|
+
@method_name = method_name.to_sym
|
|
6
|
+
set_argument_expectation_for_args(args)
|
|
7
|
+
@ordered = false
|
|
8
|
+
once
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
attr_reader :argument_expectation, :method_name, :times_matcher
|
|
12
|
+
attr_accessor :subject
|
|
13
|
+
|
|
14
|
+
include RR::DoubleDefinitions::DoubleDefinition::TimesDefinitionConstructionMethods
|
|
15
|
+
include RR::DoubleDefinitions::DoubleDefinition::ArgumentDefinitionConstructionMethods
|
|
16
|
+
|
|
17
|
+
def ordered
|
|
18
|
+
@ordered = true
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ordered?
|
|
23
|
+
@ordered
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
(error = RR.recorded_calls.match_error(self)) && raise(error)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_proc
|
|
31
|
+
lambda do
|
|
32
|
+
call
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
attr_writer :times_matcher
|
|
38
|
+
|
|
39
|
+
def set_argument_expectation_for_args(args)
|
|
40
|
+
# with_no_args and with actually set @argument_expectation
|
|
41
|
+
args.empty? ? with_no_args : with(*args)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def install_method_callback(return_value_block)
|
|
45
|
+
# Do nothing. This is to support DefinitionConstructionMethods
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
class SpyVerificationProxy
|
|
3
|
+
instance_methods.each do |m|
|
|
4
|
+
unless m =~ /^_/ || m.to_s == 'object_id' || m.to_s == "instance_eval" || m.to_s == 'respond_to?'
|
|
5
|
+
alias_method "__blank_slated_#{m}", m
|
|
6
|
+
undef_method m
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(subject)
|
|
11
|
+
@subject = subject
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def method_missing(method_name, *args, &block)
|
|
15
|
+
SpyVerification.new(@subject, method_name, args)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
|
3
|
+
class AnyTimesMatcher < TimesCalledMatcher
|
|
4
|
+
include NonTerminal
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def matches?(times_called)
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def expected_times_message
|
|
14
|
+
"any number of times"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
|
3
|
+
class AtLeastMatcher < TimesCalledMatcher
|
|
4
|
+
include NonTerminal
|
|
5
|
+
|
|
6
|
+
def matches?(times_called)
|
|
7
|
+
times_called >= @times
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def expected_times_message
|
|
11
|
+
"at least #{@times.inspect} times"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
|
3
|
+
class AtMostMatcher < TimesCalledMatcher
|
|
4
|
+
include Terminal
|
|
5
|
+
|
|
6
|
+
def possible_match?(times_called)
|
|
7
|
+
times_called <= @times
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def matches?(times_called)
|
|
11
|
+
times_called <= @times
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def attempt?(times_called)
|
|
15
|
+
times_called < @times
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def expected_times_message
|
|
19
|
+
"at most #{@times.inspect} times"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers #:nodoc:
|
|
3
|
+
class IntegerMatcher < TimesCalledMatcher
|
|
4
|
+
include Terminal
|
|
5
|
+
|
|
6
|
+
def possible_match?(times_called)
|
|
7
|
+
times_called <= @times
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def matches?(times_called)
|
|
11
|
+
times_called == @times
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def attempt?(times_called)
|
|
15
|
+
times_called < @times
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers
|
|
3
|
+
# Including this module marks the TimesCalledMatcher as NonTerminal.
|
|
4
|
+
# Being NonTerminal means the Double will not "terminate" even when
|
|
5
|
+
# called infinite times.
|
|
6
|
+
#
|
|
7
|
+
# The Double that uses a NonTerminal TimesCalledMatcher will
|
|
8
|
+
# continue using the Double when passed the matching arguments.
|
|
9
|
+
# This is done by the attempt? always returning true.
|
|
10
|
+
#
|
|
11
|
+
# This is in opposition to Terminal TimesCalledMatchers, where
|
|
12
|
+
# attempt? will eventually return false.
|
|
13
|
+
module NonTerminal #:nodoc:
|
|
14
|
+
def terminal?
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def possible_match?(times_called)
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def attempt?(times_called)
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers
|
|
3
|
+
class RangeMatcher < TimesCalledMatcher #:nodoc:
|
|
4
|
+
include Terminal
|
|
5
|
+
|
|
6
|
+
def possible_match?(times_called)
|
|
7
|
+
return true if times_called < @times.begin
|
|
8
|
+
return true if @times.include?(times_called)
|
|
9
|
+
return false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def matches?(times_called)
|
|
13
|
+
@times.include?(times_called)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def attempt?(times_called)
|
|
17
|
+
possible_match?(times_called)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module RR
|
|
2
|
+
module TimesCalledMatchers
|
|
3
|
+
# Including this module marks the TimesCalledMatcher as Terminal.
|
|
4
|
+
# Being Terminal the Double will "terminate" when times called is
|
|
5
|
+
# finite.
|
|
6
|
+
#
|
|
7
|
+
# The Double that uses a Terminal TimesCalledMatcher will
|
|
8
|
+
# eventually be passed over to the next Double when passed
|
|
9
|
+
# the matching arguments enough times. This is done by the attempt?
|
|
10
|
+
# method returning false when executed a finite number of times.
|
|
11
|
+
#
|
|
12
|
+
# This is in opposition to NonTerminal TimesCalledMatchers, where
|
|
13
|
+
# attempt? will always return true.
|
|
14
|
+
module Terminal #:nodoc:
|
|
15
|
+
def terminal?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|