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
data/Rakefile ADDED
@@ -0,0 +1,77 @@
1
+ require "rake"
2
+ require 'rake/gempackagetask'
3
+ require 'rake/contrib/rubyforgepublisher'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ desc "Runs the Rspec suite"
9
+ task(:default) do
10
+ run_suite
11
+ end
12
+
13
+ desc "Runs the Rspec suite"
14
+ task(:spec) do
15
+ run_suite
16
+ end
17
+
18
+ desc "Copies the trunk to a tag with the name of the current release"
19
+ task(:tag_release) do
20
+ tag_release
21
+ end
22
+
23
+ def run_suite
24
+ dir = File.dirname(__FILE__)
25
+ system("ruby #{dir}/spec/spec_suite.rb") || raise("Spec Suite failed")
26
+ end
27
+
28
+ PKG_NAME = "rr"
29
+ PKG_VERSION = "0.7.1"
30
+ PKG_FILES = FileList[
31
+ '[A-Z]*',
32
+ '*.rb',
33
+ 'lib/**/*.rb',
34
+ 'spec/**/*.rb'
35
+ ]
36
+
37
+ spec = Gem::Specification.new do |s|
38
+ s.name = PKG_NAME
39
+ s.version = "#{PKG_VERSION}.0.#{Time.now.to_i}"
40
+ s.summary = "RR (Double Ruby) is a double framework that features a rich " <<
41
+ "selection of double techniques and a terse syntax. " <<
42
+ "http://xunitpatterns.com/Test%20Double.html"
43
+ s.test_files = "spec/spec_suite.rb"
44
+ s.description = s.summary
45
+
46
+ s.files = PKG_FILES.to_a
47
+ s.require_path = 'lib'
48
+
49
+ s.has_rdoc = true
50
+ s.extra_rdoc_files = [ "README.rdoc", "CHANGES" ]
51
+ s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers"]
52
+
53
+ s.test_files = Dir.glob('spec/*_spec.rb')
54
+ s.require_path = 'lib'
55
+ s.author = "Brian Takita"
56
+ s.email = "brian@pivotallabs.com"
57
+ s.homepage = "http://pivotallabs.com"
58
+ s.rubyforge_project = "pivotalrb"
59
+ end
60
+
61
+ desc "Generate a gemspec file"
62
+ task :gemspec do
63
+ File.open("#{spec.name}.gemspec", 'w') do |f|
64
+ f.write spec.to_yaml
65
+ end
66
+ end
67
+
68
+ Rake::GemPackageTask.new(spec) do |pkg|
69
+ pkg.need_zip = true
70
+ pkg.need_tar = true
71
+ end
72
+
73
+ def tag_release
74
+ dashed_version = PKG_VERSION.gsub('.', '-')
75
+ svn_user = "#{ENV["SVN_USER"]}@" || ""
76
+ `svn cp svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/rr/trunk svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/rr/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
77
+ end
data/lib/rr.rb ADDED
@@ -0,0 +1,84 @@
1
+ dir = File.dirname(__FILE__)
2
+ require 'rubygems'
3
+
4
+ require "#{dir}/rr/errors/rr_error"
5
+ require "#{dir}/rr/errors/subject_does_not_implement_method_error"
6
+ require "#{dir}/rr/errors/subject_has_different_arity_error"
7
+ require "#{dir}/rr/errors/double_definition_error"
8
+ require "#{dir}/rr/errors/double_not_found_error"
9
+ require "#{dir}/rr/errors/double_order_error"
10
+ require "#{dir}/rr/errors/argument_equality_error"
11
+ require "#{dir}/rr/errors/times_called_error"
12
+ require "#{dir}/rr/errors/spy_verification_errors/spy_verification_error"
13
+ require "#{dir}/rr/errors/spy_verification_errors/double_injection_not_found_error"
14
+ require "#{dir}/rr/errors/spy_verification_errors/invocation_count_error"
15
+
16
+ require "#{dir}/rr/space"
17
+ require "#{dir}/rr/double_injection"
18
+ require "#{dir}/rr/hash_with_object_id_key"
19
+ require "#{dir}/rr/recorded_calls"
20
+
21
+ require "#{dir}/rr/double_definitions/double_definition_creator_proxy"
22
+ require "#{dir}/rr/double_definitions/double_definition_creator"
23
+ require "#{dir}/rr/double_definitions/child_double_definition_creator"
24
+ require "#{dir}/rr/adapters/rr_methods"
25
+
26
+ require "#{dir}/rr/double"
27
+ require "#{dir}/rr/double_definitions/double_definition"
28
+ require "#{dir}/rr/double_definitions/strategies/strategy"
29
+ require "#{dir}/rr/double_definitions/strategies/verification/verification_strategy"
30
+ require "#{dir}/rr/double_definitions/strategies/verification/mock"
31
+ require "#{dir}/rr/double_definitions/strategies/verification/stub"
32
+ require "#{dir}/rr/double_definitions/strategies/verification/dont_allow"
33
+ require "#{dir}/rr/double_definitions/strategies/implementation/implementation_strategy"
34
+ require "#{dir}/rr/double_definitions/strategies/implementation/reimplementation"
35
+ require "#{dir}/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation"
36
+ require "#{dir}/rr/double_definitions/strategies/implementation/proxy"
37
+ require "#{dir}/rr/double_definitions/strategies/scope/scope_strategy"
38
+ require "#{dir}/rr/double_definitions/strategies/scope/instance"
39
+ require "#{dir}/rr/double_definitions/strategies/scope/instance_of_class"
40
+ require "#{dir}/rr/double_matches"
41
+
42
+ require "#{dir}/rr/expectations/argument_equality_expectation"
43
+ require "#{dir}/rr/expectations/any_argument_expectation"
44
+ require "#{dir}/rr/expectations/times_called_expectation"
45
+
46
+ require "#{dir}/rr/wildcard_matchers/anything"
47
+ require "#{dir}/rr/wildcard_matchers/is_a"
48
+ require "#{dir}/rr/wildcard_matchers/numeric"
49
+ require "#{dir}/rr/wildcard_matchers/boolean"
50
+ require "#{dir}/rr/wildcard_matchers/duck_type"
51
+ require "#{dir}/rr/wildcard_matchers/regexp"
52
+ require "#{dir}/rr/wildcard_matchers/range"
53
+ require "#{dir}/rr/wildcard_matchers/satisfy"
54
+ require "#{dir}/rr/wildcard_matchers/hash_including"
55
+
56
+ require "#{dir}/rr/times_called_matchers/terminal"
57
+ require "#{dir}/rr/times_called_matchers/non_terminal"
58
+ require "#{dir}/rr/times_called_matchers/times_called_matcher"
59
+ require "#{dir}/rr/times_called_matchers/any_times_matcher"
60
+ require "#{dir}/rr/times_called_matchers/integer_matcher"
61
+ require "#{dir}/rr/times_called_matchers/range_matcher"
62
+ require "#{dir}/rr/times_called_matchers/proc_matcher"
63
+ require "#{dir}/rr/times_called_matchers/at_least_matcher"
64
+ require "#{dir}/rr/times_called_matchers/at_most_matcher"
65
+
66
+ require "#{dir}/rr/spy_verification_proxy"
67
+ require "#{dir}/rr/spy_verification"
68
+
69
+ require "#{dir}/rr/adapters/rspec"
70
+ require "#{dir}/rr/adapters/test_unit"
71
+
72
+ module RR
73
+ class << self
74
+ include Adapters::RRMethods
75
+ (RR::Space.instance_methods - Object.instance_methods).each do |method_name|
76
+ returns_method = <<-METHOD
77
+ def #{method_name}(*args, &block)
78
+ RR::Space.instance.__send__(:#{method_name}, *args, &block)
79
+ end
80
+ METHOD
81
+ class_eval(returns_method, __FILE__, __LINE__ - 4)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,122 @@
1
+ module RR
2
+ module Adapters
3
+ module RRMethods
4
+ class << self
5
+ def register_strategy_class(strategy_class, method_name)
6
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
7
+ def #{method_name}(subject=DoubleDefinitions::DoubleDefinitionCreator::NO_SUBJECT, method_name=nil, &definition_eval_block)
8
+ creator = DoubleDefinitions::DoubleDefinitionCreator.new
9
+ creator.#{method_name}(subject, method_name, &definition_eval_block)
10
+ end
11
+ CLASS
12
+
13
+ class_eval((<<-CLASS), __FILE__, __LINE__ + 1)
14
+ def #{method_name}!(method_name=nil, &definition_eval_block)
15
+ creator = DoubleDefinitions::DoubleDefinitionCreator.new
16
+ creator.#{method_name}!(method_name, &definition_eval_block)
17
+ end
18
+ CLASS
19
+ end
20
+ end
21
+
22
+ # Verifies all the DoubleInjection objects have met their
23
+ # TimesCalledExpectations.
24
+ def verify
25
+ RR::Space.instance.verify_doubles
26
+ end
27
+
28
+ # Resets the registered Doubles and ordered Doubles
29
+ def reset
30
+ RR::Space.instance.reset
31
+ end
32
+
33
+ # Returns a AnyTimesMatcher. This is meant to be passed in as an argument
34
+ # to Double#times.
35
+ #
36
+ # mock(object).method_name(anything).times(any_times) {return_value}
37
+ def any_times
38
+ TimesCalledMatchers::AnyTimesMatcher.new
39
+ end
40
+
41
+ # Sets up an Anything wildcard ArgumentEqualityExpectation
42
+ # that succeeds when passed any argument.
43
+ # mock(object).method_name(anything) {return_value}
44
+ # object.method_name("an arbitrary value") # passes
45
+ def anything
46
+ RR::WildcardMatchers::Anything.new
47
+ end
48
+
49
+ # Sets up an IsA wildcard ArgumentEqualityExpectation
50
+ # that succeeds when passed an argument of a certain type.
51
+ # mock(object).method_name(is_a(String)) {return_value}
52
+ # object.method_name("A String") # passes
53
+ def is_a(klass)
54
+ RR::WildcardMatchers::IsA.new(klass)
55
+ end
56
+
57
+ # Sets up an Numeric wildcard ArgumentEqualityExpectation
58
+ # that succeeds when passed an argument that is ::Numeric.
59
+ # mock(object).method_name(numeric) {return_value}
60
+ # object.method_name(99) # passes
61
+ def numeric
62
+ RR::WildcardMatchers::Numeric.new
63
+ end
64
+
65
+ # Sets up an Boolean wildcard ArgumentEqualityExpectation
66
+ # that succeeds when passed an argument that is a ::Boolean.
67
+ # mock(object).method_name(boolean) {return_value}
68
+ # object.method_name(false) # passes
69
+ def boolean
70
+ RR::WildcardMatchers::Boolean.new
71
+ end
72
+
73
+ # Sets up a DuckType wildcard ArgumentEqualityExpectation
74
+ # that succeeds when the passed argument implements the methods.
75
+ # arg = Object.new
76
+ # def arg.foo; end
77
+ # def arg.bar; end
78
+ # mock(object).method_name(duck_type(:foo, :bar)) {return_value}
79
+ # object.method_name(arg) # passes
80
+ def duck_type(*args)
81
+ RR::WildcardMatchers::DuckType.new(*args)
82
+ end
83
+
84
+ # Sets up a HashIncluding wildcard ArgumentEqualityExpectation
85
+ # that succeeds when the passed argument contains at least those keys
86
+ # and values of the expectation.
87
+ # mock(object).method_name(hash_including(:foo => 1)) {return_value}
88
+ # object.method_name({:foo => 1, :bar => 2) # passes
89
+ def hash_including(expected_hash)
90
+ RR::WildcardMatchers::HashIncluding.new(expected_hash)
91
+ end
92
+
93
+ # Sets up a Satisfy wildcard ArgumentEqualityExpectation
94
+ # that succeeds when the passed argument causes the expectation's
95
+ # proc to return true.
96
+ # mock(object).method_name(satisfy {|arg| arg == :foo}) {return_value}
97
+ # object.method_name(:foo) # passes
98
+ def satisfy(expectation_proc=nil, &block)
99
+ expectation_proc ||= block
100
+ RR::WildcardMatchers::Satisfy.new(expectation_proc)
101
+ end
102
+
103
+ def spy(subject)
104
+ methods_to_stub = subject.public_methods - ["methods", "==", "__send__", "__id__"]
105
+ methods_to_stub.each do |method|
106
+ stub.proxy(subject, method)
107
+ end
108
+ end
109
+
110
+ def received(subject)
111
+ RR::SpyVerificationProxy.new(subject)
112
+ end
113
+
114
+ instance_methods.each do |name|
115
+ alias_method "rr_#{name}", name
116
+ end
117
+ end
118
+ end
119
+ module Extensions
120
+ InstanceMethods = Adapters::RRMethods
121
+ end
122
+ end
@@ -0,0 +1,58 @@
1
+ module RR
2
+ module Adapters
3
+ module Rspec
4
+ def self.included(mod)
5
+ patterns = ::Spec::Runner::QuietBacktraceTweaker::IGNORE_PATTERNS
6
+ unless patterns.include?(RR::Errors::BACKTRACE_IDENTIFIER)
7
+ patterns.push(RR::Errors::BACKTRACE_IDENTIFIER)
8
+ end
9
+ end
10
+
11
+ include RRMethods
12
+ def setup_mocks_for_rspec
13
+ RR.reset
14
+ end
15
+ def verify_mocks_for_rspec
16
+ RR.verify
17
+ end
18
+ def teardown_mocks_for_rspec
19
+ RR.reset
20
+ end
21
+
22
+ def have_received(method = nil)
23
+ InvocationMatcher.new(method)
24
+ end
25
+
26
+ class InvocationMatcher < SpyVerificationProxy
27
+ attr_reader :failure_message
28
+
29
+ def initialize(method = nil)
30
+ method_missing(method) if method
31
+ end
32
+
33
+ def matches?(subject)
34
+ @verification.subject = subject
35
+ if error = RR::Space.instance.recorded_calls.match_error(@verification)
36
+ @failure_message = error.message
37
+ false
38
+ else
39
+ true
40
+ end
41
+ end
42
+
43
+ def nil?
44
+ false
45
+ end
46
+
47
+ def method_missing(method_name, *args, &block)
48
+ if @verification
49
+ @verification.send(method_name, *args)
50
+ else
51
+ @verification = super
52
+ end
53
+ self
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,29 @@
1
+ module RR
2
+ module Adapters
3
+ module TestUnit
4
+ include RRMethods
5
+ def self.included(mod)
6
+ RR.trim_backtrace = true
7
+ mod.class_eval do
8
+ alias_method :setup_without_rr, :setup
9
+ def setup_with_rr
10
+ setup_without_rr
11
+ RR.reset
12
+ end
13
+ alias_method :setup, :setup_with_rr
14
+
15
+ alias_method :teardown_without_rr, :teardown
16
+ def teardown_with_rr
17
+ RR.verify
18
+ teardown_without_rr
19
+ end
20
+ alias_method :teardown, :teardown_with_rr
21
+ end
22
+ end
23
+
24
+ def assert_received(subject, &block)
25
+ block.call(received(subject)).call
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/rr/double.rb ADDED
@@ -0,0 +1,212 @@
1
+ module RR
2
+ # RR::Double is the use case for a method call.
3
+ # It has the ArgumentEqualityExpectation, TimesCalledExpectation,
4
+ # and the implementation.
5
+ class Double
6
+ class << self
7
+ def formatted_name(method_name, args)
8
+ formatted_errors = args.collect {|arg| arg.inspect}.join(', ')
9
+ "#{method_name}(#{formatted_errors})"
10
+ end
11
+
12
+ def list_message_part(doubles)
13
+ doubles.collect do |double|
14
+ "- #{formatted_name(double.method_name, double.expected_arguments)}"
15
+ end.join("\n")
16
+ end
17
+ end
18
+
19
+ attr_reader :times_called, :double_injection, :definition, :times_called_expectation
20
+ include Space::Reader
21
+
22
+ def initialize(double_injection, definition)
23
+ @double_injection = double_injection
24
+ @definition = definition
25
+ @times_called = 0
26
+ @times_called_expectation = Expectations::TimesCalledExpectation.new(self)
27
+ definition.double = self
28
+ verify_method_signature if definition.verify_method_signature?
29
+ double_injection.register_double self
30
+ end
31
+
32
+ # Double#call calls the Double's implementation. The return
33
+ # value of the implementation is returned.
34
+ #
35
+ # A TimesCalledError is raised when the times called
36
+ # exceeds the expected TimesCalledExpectation.
37
+ def call(double_injection, *args, &block)
38
+ if verbose?
39
+ puts Double.formatted_name(double_injection.method_name, args)
40
+ end
41
+ times_called_expectation.attempt if definition.times_matcher
42
+ space.verify_ordered_double(self) if ordered?
43
+ yields!(block)
44
+ return_value = call_implementation(double_injection, *args, &block)
45
+ definition.after_call_proc ? extract_subject_from_return_value(definition.after_call_proc.call(return_value)) : return_value
46
+ end
47
+
48
+ # Double#exact_match? returns true when the passed in arguments
49
+ # exactly match the ArgumentEqualityExpectation arguments.
50
+ def exact_match?(*arguments)
51
+ definition.exact_match?(*arguments)
52
+ end
53
+
54
+ # Double#wildcard_match? returns true when the passed in arguments
55
+ # wildcard match the ArgumentEqualityExpectation arguments.
56
+ def wildcard_match?(*arguments)
57
+ definition.wildcard_match?(*arguments)
58
+ end
59
+
60
+ # Double#attempt? returns true when the
61
+ # TimesCalledExpectation is satisfied.
62
+ def attempt?
63
+ verify_times_matcher_is_set
64
+ times_called_expectation.attempt?
65
+ end
66
+
67
+ # Double#verify verifies the the TimesCalledExpectation
68
+ # is satisfied for this double. A TimesCalledError
69
+ # is raised if the TimesCalledExpectation is not met.
70
+ def verify
71
+ verify_times_matcher_is_set
72
+ times_called_expectation.verify!
73
+ true
74
+ end
75
+
76
+ def terminal?
77
+ verify_times_matcher_is_set
78
+ times_called_expectation.terminal?
79
+ end
80
+
81
+ # The method name that this Double is attatched to
82
+ def method_name
83
+ double_injection.method_name
84
+ end
85
+
86
+ # The Arguments that this Double expects
87
+ def expected_arguments
88
+ verify_argument_expectation_is_set
89
+ argument_expectation.expected_arguments
90
+ end
91
+
92
+ # The TimesCalledMatcher for the TimesCalledExpectation
93
+ def times_matcher
94
+ definition.times_matcher
95
+ end
96
+
97
+ def formatted_name
98
+ self.class.formatted_name(method_name, expected_arguments)
99
+ end
100
+
101
+ protected
102
+ def ordered?
103
+ definition.ordered?
104
+ end
105
+
106
+ def verbose?
107
+ definition.verbose?
108
+ end
109
+
110
+ def yields!(block)
111
+ if definition.yields_value
112
+ if block
113
+ block.call(*definition.yields_value)
114
+ else
115
+ raise ArgumentError, "A Block must be passed into the method call when using yields"
116
+ end
117
+ end
118
+ end
119
+
120
+ def call_implementation(double_injection, *args, &block)
121
+ return_value = do_call_implementation_and_get_return_value(double_injection, *args, &block)
122
+ extract_subject_from_return_value(return_value)
123
+ end
124
+
125
+ def verify_times_matcher_is_set
126
+ unless definition.times_matcher
127
+ raise RR::Errors::DoubleDefinitionError, "#definition.times_matcher is not set"
128
+ end
129
+ end
130
+
131
+ def verify_argument_expectation_is_set
132
+ unless definition.argument_expectation
133
+ raise RR::Errors::DoubleDefinitionError, "#definition.argument_expectation is not set"
134
+ end
135
+ end
136
+
137
+ def verify_method_signature
138
+ raise RR::Errors::SubjectDoesNotImplementMethodError unless definition.subject.respond_to?(double_injection.send(:original_method_name))
139
+ raise RR::Errors::SubjectHasDifferentArityError unless arity_matches?
140
+ end
141
+
142
+ def subject_arity
143
+ definition.subject.method(double_injection.send(:original_method_name)).arity
144
+ end
145
+
146
+ def subject_accepts_only_varargs?
147
+ subject_arity == -1
148
+ end
149
+
150
+ def subject_accepts_varargs?
151
+ subject_arity < 0
152
+ end
153
+
154
+ def arity_matches?
155
+ return true if subject_accepts_only_varargs?
156
+ if subject_accepts_varargs?
157
+ return ((subject_arity * -1) - 1) <= args.size
158
+ else
159
+ return subject_arity == args.size
160
+ end
161
+ end
162
+
163
+ def args
164
+ definition.argument_expectation.expected_arguments
165
+ end
166
+
167
+ def do_call_implementation_and_get_return_value(double_injection, *args, &block)
168
+ if definition.implementation_is_original_method?
169
+ if double_injection.object_has_original_method?
170
+ double_injection.call_original_method(*args, &block)
171
+ else
172
+ double_injection.subject.__send__(
173
+ :method_missing,
174
+ method_name,
175
+ *args,
176
+ &block
177
+ )
178
+ end
179
+ else
180
+ if implementation
181
+ if implementation.is_a?(Method)
182
+ implementation.call(*args, &block)
183
+ else
184
+ args << block if block
185
+ implementation.call(*args)
186
+ end
187
+ else
188
+ nil
189
+ end
190
+ end
191
+ end
192
+
193
+ def extract_subject_from_return_value(return_value)
194
+ case return_value
195
+ when DoubleDefinitions::DoubleDefinition
196
+ return_value.root_subject
197
+ when DoubleDefinitions::DoubleDefinitionCreatorProxy
198
+ return_value.__creator__.root_subject
199
+ else
200
+ return_value
201
+ end
202
+ end
203
+
204
+ def implementation
205
+ definition.implementation
206
+ end
207
+
208
+ def argument_expectation
209
+ definition.argument_expectation
210
+ end
211
+ end
212
+ end