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.
Files changed (130) hide show
  1. data/CHANGES +196 -0
  2. data/README.rdoc +329 -0
  3. data/Rakefile +77 -0
  4. data/lib/rr.rb +84 -0
  5. data/lib/rr/adapters/rr_methods.rb +122 -0
  6. data/lib/rr/adapters/rspec.rb +58 -0
  7. data/lib/rr/adapters/test_unit.rb +29 -0
  8. data/lib/rr/double.rb +212 -0
  9. data/lib/rr/double_definitions/child_double_definition_creator.rb +27 -0
  10. data/lib/rr/double_definitions/double_definition.rb +346 -0
  11. data/lib/rr/double_definitions/double_definition_creator.rb +167 -0
  12. data/lib/rr/double_definitions/double_definition_creator_proxy.rb +37 -0
  13. data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +15 -0
  14. data/lib/rr/double_definitions/strategies/implementation/proxy.rb +62 -0
  15. data/lib/rr/double_definitions/strategies/implementation/reimplementation.rb +14 -0
  16. data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +17 -0
  17. data/lib/rr/double_definitions/strategies/scope/instance.rb +15 -0
  18. data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +46 -0
  19. data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +15 -0
  20. data/lib/rr/double_definitions/strategies/strategy.rb +70 -0
  21. data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +34 -0
  22. data/lib/rr/double_definitions/strategies/verification/mock.rb +44 -0
  23. data/lib/rr/double_definitions/strategies/verification/stub.rb +45 -0
  24. data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +15 -0
  25. data/lib/rr/double_injection.rb +143 -0
  26. data/lib/rr/double_matches.rb +51 -0
  27. data/lib/rr/errors/argument_equality_error.rb +6 -0
  28. data/lib/rr/errors/double_definition_error.rb +6 -0
  29. data/lib/rr/errors/double_not_found_error.rb +6 -0
  30. data/lib/rr/errors/double_order_error.rb +6 -0
  31. data/lib/rr/errors/rr_error.rb +20 -0
  32. data/lib/rr/errors/spy_verification_errors/double_injection_not_found_error.rb +8 -0
  33. data/lib/rr/errors/spy_verification_errors/invocation_count_error.rb +8 -0
  34. data/lib/rr/errors/spy_verification_errors/spy_verification_error.rb +8 -0
  35. data/lib/rr/errors/subject_does_not_implement_method_error.rb +6 -0
  36. data/lib/rr/errors/subject_has_different_arity_error.rb +6 -0
  37. data/lib/rr/errors/times_called_error.rb +6 -0
  38. data/lib/rr/expectations/any_argument_expectation.rb +21 -0
  39. data/lib/rr/expectations/argument_equality_expectation.rb +41 -0
  40. data/lib/rr/expectations/times_called_expectation.rb +57 -0
  41. data/lib/rr/hash_with_object_id_key.rb +41 -0
  42. data/lib/rr/recorded_calls.rb +103 -0
  43. data/lib/rr/space.rb +123 -0
  44. data/lib/rr/spy_verification.rb +48 -0
  45. data/lib/rr/spy_verification_proxy.rb +18 -0
  46. data/lib/rr/times_called_matchers/any_times_matcher.rb +18 -0
  47. data/lib/rr/times_called_matchers/at_least_matcher.rb +15 -0
  48. data/lib/rr/times_called_matchers/at_most_matcher.rb +23 -0
  49. data/lib/rr/times_called_matchers/integer_matcher.rb +19 -0
  50. data/lib/rr/times_called_matchers/non_terminal.rb +27 -0
  51. data/lib/rr/times_called_matchers/proc_matcher.rb +11 -0
  52. data/lib/rr/times_called_matchers/range_matcher.rb +21 -0
  53. data/lib/rr/times_called_matchers/terminal.rb +20 -0
  54. data/lib/rr/times_called_matchers/times_called_matcher.rb +44 -0
  55. data/lib/rr/wildcard_matchers/anything.rb +18 -0
  56. data/lib/rr/wildcard_matchers/boolean.rb +23 -0
  57. data/lib/rr/wildcard_matchers/duck_type.rb +32 -0
  58. data/lib/rr/wildcard_matchers/hash_including.rb +29 -0
  59. data/lib/rr/wildcard_matchers/is_a.rb +25 -0
  60. data/lib/rr/wildcard_matchers/numeric.rb +13 -0
  61. data/lib/rr/wildcard_matchers/range.rb +7 -0
  62. data/lib/rr/wildcard_matchers/regexp.rb +7 -0
  63. data/lib/rr/wildcard_matchers/satisfy.rb +26 -0
  64. data/spec/core_spec_suite.rb +19 -0
  65. data/spec/environment_fixture_setup.rb +6 -0
  66. data/spec/high_level_spec.rb +368 -0
  67. data/spec/rr/adapters/rr_methods_argument_matcher_spec.rb +67 -0
  68. data/spec/rr/adapters/rr_methods_creator_spec.rb +149 -0
  69. data/spec/rr/adapters/rr_methods_space_spec.rb +115 -0
  70. data/spec/rr/adapters/rr_methods_spec_helper.rb +11 -0
  71. data/spec/rr/adapters/rr_methods_times_matcher_spec.rb +17 -0
  72. data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +112 -0
  73. data/spec/rr/double_definitions/double_definition_creator_proxy_spec.rb +155 -0
  74. data/spec/rr/double_definitions/double_definition_creator_spec.rb +502 -0
  75. data/spec/rr/double_definitions/double_definition_spec.rb +1159 -0
  76. data/spec/rr/double_injection/double_injection_bind_spec.rb +111 -0
  77. data/spec/rr/double_injection/double_injection_dispatching_spec.rb +244 -0
  78. data/spec/rr/double_injection/double_injection_has_original_method_spec.rb +55 -0
  79. data/spec/rr/double_injection/double_injection_reset_spec.rb +90 -0
  80. data/spec/rr/double_injection/double_injection_spec.rb +77 -0
  81. data/spec/rr/double_injection/double_injection_verify_spec.rb +29 -0
  82. data/spec/rr/double_spec.rb +352 -0
  83. data/spec/rr/errors/rr_error_spec.rb +67 -0
  84. data/spec/rr/expectations/any_argument_expectation_spec.rb +47 -0
  85. data/spec/rr/expectations/anything_argument_equality_expectation_spec.rb +14 -0
  86. data/spec/rr/expectations/argument_equality_expectation_spec.rb +135 -0
  87. data/spec/rr/expectations/boolean_argument_equality_expectation_spec.rb +34 -0
  88. data/spec/rr/expectations/hash_including_argument_equality_expectation_spec.rb +82 -0
  89. data/spec/rr/expectations/hash_including_spec.rb +17 -0
  90. data/spec/rr/expectations/satisfy_argument_equality_expectation_spec.rb +59 -0
  91. data/spec/rr/expectations/satisfy_spec.rb +14 -0
  92. data/spec/rr/expectations/times_called_expectation/times_called_expectation_any_times_spec.rb +46 -0
  93. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_least_spec.rb +69 -0
  94. data/spec/rr/expectations/times_called_expectation/times_called_expectation_at_most_spec.rb +71 -0
  95. data/spec/rr/expectations/times_called_expectation/times_called_expectation_helper.rb +23 -0
  96. data/spec/rr/expectations/times_called_expectation/times_called_expectation_integer_spec.rb +104 -0
  97. data/spec/rr/expectations/times_called_expectation/times_called_expectation_proc_spec.rb +81 -0
  98. data/spec/rr/expectations/times_called_expectation/times_called_expectation_range_spec.rb +83 -0
  99. data/spec/rr/expectations/times_called_expectation/times_called_expectation_spec.rb +38 -0
  100. data/spec/rr/rspec/invocation_matcher_spec.rb +279 -0
  101. data/spec/rr/rspec/rspec_adapter_spec.rb +66 -0
  102. data/spec/rr/rspec/rspec_backtrace_tweaking_spec.rb +31 -0
  103. data/spec/rr/rspec/rspec_backtrace_tweaking_spec_fixture.rb +11 -0
  104. data/spec/rr/rspec/rspec_usage_spec.rb +86 -0
  105. data/spec/rr/space/hash_with_object_id_key_spec.rb +88 -0
  106. data/spec/rr/space/space_spec.rb +542 -0
  107. data/spec/rr/test_unit/test_helper.rb +7 -0
  108. data/spec/rr/test_unit/test_unit_backtrace_test.rb +35 -0
  109. data/spec/rr/test_unit/test_unit_integration_test.rb +57 -0
  110. data/spec/rr/times_called_matchers/any_times_matcher_spec.rb +47 -0
  111. data/spec/rr/times_called_matchers/at_least_matcher_spec.rb +55 -0
  112. data/spec/rr/times_called_matchers/at_most_matcher_spec.rb +70 -0
  113. data/spec/rr/times_called_matchers/integer_matcher_spec.rb +70 -0
  114. data/spec/rr/times_called_matchers/proc_matcher_spec.rb +55 -0
  115. data/spec/rr/times_called_matchers/range_matcher_spec.rb +76 -0
  116. data/spec/rr/times_called_matchers/times_called_matcher_spec.rb +118 -0
  117. data/spec/rr/wildcard_matchers/anything_spec.rb +24 -0
  118. data/spec/rr/wildcard_matchers/boolean_spec.rb +36 -0
  119. data/spec/rr/wildcard_matchers/duck_type_spec.rb +52 -0
  120. data/spec/rr/wildcard_matchers/is_a_spec.rb +32 -0
  121. data/spec/rr/wildcard_matchers/numeric_spec.rb +32 -0
  122. data/spec/rr/wildcard_matchers/range_spec.rb +35 -0
  123. data/spec/rr/wildcard_matchers/regexp_spec.rb +43 -0
  124. data/spec/rr_spec.rb +28 -0
  125. data/spec/rspec_spec_suite.rb +16 -0
  126. data/spec/spec_helper.rb +107 -0
  127. data/spec/spec_suite.rb +27 -0
  128. data/spec/spy_verification_spec.rb +129 -0
  129. data/spec/test_unit_spec_suite.rb +21 -0
  130. 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,11 @@
1
+ module RR
2
+ module TimesCalledMatchers
3
+ class ProcMatcher < TimesCalledMatcher #:nodoc:
4
+ include NonTerminal
5
+
6
+ def matches?(times_called)
7
+ @times.call(times_called)
8
+ end
9
+ end
10
+ end
11
+ 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