rspec-mocks-diag 3.8.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.yardopts +6 -0
  4. data/Changelog.md +1108 -0
  5. data/LICENSE.md +25 -0
  6. data/README.md +460 -0
  7. data/lib/rspec/mocks.rb +130 -0
  8. data/lib/rspec/mocks/any_instance.rb +11 -0
  9. data/lib/rspec/mocks/any_instance/chain.rb +110 -0
  10. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  11. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
  12. data/lib/rspec/mocks/any_instance/expectation_chain.rb +50 -0
  13. data/lib/rspec/mocks/any_instance/message_chains.rb +83 -0
  14. data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
  15. data/lib/rspec/mocks/any_instance/recorder.rb +289 -0
  16. data/lib/rspec/mocks/any_instance/stub_chain.rb +51 -0
  17. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
  18. data/lib/rspec/mocks/argument_list_matcher.rb +100 -0
  19. data/lib/rspec/mocks/argument_matchers.rb +320 -0
  20. data/lib/rspec/mocks/configuration.rb +212 -0
  21. data/lib/rspec/mocks/error_generator.rb +378 -0
  22. data/lib/rspec/mocks/example_methods.rb +434 -0
  23. data/lib/rspec/mocks/instance_method_stasher.rb +146 -0
  24. data/lib/rspec/mocks/marshal_extension.rb +41 -0
  25. data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
  26. data/lib/rspec/mocks/matchers/have_received.rb +134 -0
  27. data/lib/rspec/mocks/matchers/receive.rb +132 -0
  28. data/lib/rspec/mocks/matchers/receive_message_chain.rb +82 -0
  29. data/lib/rspec/mocks/matchers/receive_messages.rb +77 -0
  30. data/lib/rspec/mocks/message_chain.rb +87 -0
  31. data/lib/rspec/mocks/message_expectation.rb +748 -0
  32. data/lib/rspec/mocks/method_double.rb +287 -0
  33. data/lib/rspec/mocks/method_reference.rb +202 -0
  34. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  35. data/lib/rspec/mocks/mutate_const.rb +339 -0
  36. data/lib/rspec/mocks/object_reference.rb +149 -0
  37. data/lib/rspec/mocks/order_group.rb +81 -0
  38. data/lib/rspec/mocks/proxy.rb +485 -0
  39. data/lib/rspec/mocks/space.rb +238 -0
  40. data/lib/rspec/mocks/standalone.rb +3 -0
  41. data/lib/rspec/mocks/syntax.rb +325 -0
  42. data/lib/rspec/mocks/targets.rb +124 -0
  43. data/lib/rspec/mocks/test_double.rb +171 -0
  44. data/lib/rspec/mocks/verifying_double.rb +129 -0
  45. data/lib/rspec/mocks/verifying_message_expectation.rb +54 -0
  46. data/lib/rspec/mocks/verifying_proxy.rb +220 -0
  47. data/lib/rspec/mocks/version.rb +9 -0
  48. metadata +186 -0
@@ -0,0 +1,146 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @private
4
+ class InstanceMethodStasher
5
+ def initialize(object, method)
6
+ @object = object
7
+ @method = method
8
+ @klass = (class << object; self; end)
9
+
10
+ @original_method = nil
11
+ @method_is_stashed = false
12
+ end
13
+
14
+ attr_reader :original_method
15
+
16
+ if RUBY_VERSION.to_f < 1.9
17
+ # @private
18
+ def method_is_stashed?
19
+ @method_is_stashed
20
+ end
21
+
22
+ # @private
23
+ def stash
24
+ return if !method_defined_directly_on_klass? || @method_is_stashed
25
+
26
+ @klass.__send__(:alias_method, stashed_method_name, @method)
27
+ @method_is_stashed = true
28
+ end
29
+
30
+ # @private
31
+ def stashed_method_name
32
+ "obfuscated_by_rspec_mocks__#{@method}"
33
+ end
34
+
35
+ # @private
36
+ def restore
37
+ return unless @method_is_stashed
38
+
39
+ if @klass.__send__(:method_defined?, @method)
40
+ @klass.__send__(:undef_method, @method)
41
+ end
42
+ @klass.__send__(:alias_method, @method, stashed_method_name)
43
+ @klass.__send__(:remove_method, stashed_method_name)
44
+ @method_is_stashed = false
45
+ end
46
+ else
47
+
48
+ # @private
49
+ def method_is_stashed?
50
+ !!@original_method
51
+ end
52
+
53
+ # @private
54
+ def stash
55
+ return unless method_defined_directly_on_klass?
56
+ @original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
57
+ @klass.__send__(:undef_method, @method)
58
+ end
59
+
60
+ # @private
61
+ def restore
62
+ return unless @original_method
63
+
64
+ if @klass.__send__(:method_defined?, @method)
65
+ @klass.__send__(:undef_method, @method)
66
+ end
67
+
68
+ handle_restoration_failures do
69
+ @klass.__send__(:define_method, @method, @original_method)
70
+ end
71
+
72
+ @original_method = nil
73
+ end
74
+ end
75
+
76
+ if RUBY_DESCRIPTION.include?('2.0.0p247') || RUBY_DESCRIPTION.include?('2.0.0p195')
77
+ # ruby 2.0.0-p247 and 2.0.0-p195 both have a bug that we can't work around :(.
78
+ # https://bugs.ruby-lang.org/issues/8686
79
+ def handle_restoration_failures
80
+ yield
81
+ rescue TypeError
82
+ RSpec.warn_with(
83
+ "RSpec failed to properly restore a partial double (#{@object.inspect}) " \
84
+ "to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \
85
+ "(https://bugs.ruby-lang.org/issues/8686). This object may remain " \
86
+ "screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.",
87
+ :call_site => nil, :use_spec_location_as_call_site => true
88
+ )
89
+ end
90
+ else
91
+ def handle_restoration_failures
92
+ # No known reasons for restoration to fail on other rubies.
93
+ yield
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ # @private
100
+ def method_defined_directly_on_klass?
101
+ method_defined_on_klass? && method_owned_by_klass?
102
+ end
103
+
104
+ # @private
105
+ def method_defined_on_klass?(klass=@klass)
106
+ MethodReference.method_defined_at_any_visibility?(klass, @method)
107
+ end
108
+
109
+ def method_owned_by_klass?
110
+ owner = @klass.instance_method(@method).owner
111
+
112
+ # On Ruby 2.0.0+ the owner of a method on a class which has been
113
+ # `prepend`ed may actually be an instance, e.g.
114
+ # `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
115
+ owner = owner.class unless Module === owner
116
+
117
+ # On some 1.9s (e.g. rubinius) aliased methods
118
+ # can report the wrong owner. Example:
119
+ # class MyClass
120
+ # class << self
121
+ # alias alternate_new new
122
+ # end
123
+ # end
124
+ #
125
+ # MyClass.owner(:alternate_new) returns `Class` when incorrect,
126
+ # but we need to consider the owner to be `MyClass` because
127
+ # it is not actually available on `Class` but is on `MyClass`.
128
+ # Hence, we verify that the owner actually has the method defined.
129
+ # If the given owner does not have the method defined, we assume
130
+ # that the method is actually owned by @klass.
131
+ #
132
+ # On 1.8, aliased methods can also report the wrong owner. Example:
133
+ # module M
134
+ # def a; end
135
+ # module_function :a
136
+ # alias b a
137
+ # module_function :b
138
+ # end
139
+ # The owner of M.b is the raw Module object, instead of the expected
140
+ # singleton class of the module
141
+ return true if RUBY_VERSION < '1.9' && owner == @object
142
+ owner == @klass || !(method_defined_on_klass?(owner))
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,41 @@
1
+ module RSpec
2
+ module Mocks
3
+ # Support for `patch_marshal_to_support_partial_doubles` configuration.
4
+ #
5
+ # @private
6
+ class MarshalExtension
7
+ def self.patch!
8
+ return if Marshal.respond_to?(:dump_with_rspec_mocks)
9
+
10
+ Marshal.instance_eval do
11
+ class << self
12
+ def dump_with_rspec_mocks(object, *rest)
13
+ if !::RSpec::Mocks.space.registered?(object) || NilClass === object
14
+ dump_without_rspec_mocks(object, *rest)
15
+ else
16
+ dump_without_rspec_mocks(object.dup, *rest)
17
+ end
18
+ end
19
+
20
+ alias_method :dump_without_rspec_mocks, :dump
21
+ undef_method :dump
22
+ alias_method :dump, :dump_with_rspec_mocks
23
+ end
24
+ end
25
+ end
26
+
27
+ def self.unpatch!
28
+ return unless Marshal.respond_to?(:dump_with_rspec_mocks)
29
+
30
+ Marshal.instance_eval do
31
+ class << self
32
+ undef_method :dump_with_rspec_mocks
33
+ undef_method :dump
34
+ alias_method :dump, :dump_without_rspec_mocks
35
+ undef_method :dump_without_rspec_mocks
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,20 @@
1
+ module RSpec
2
+ module Mocks
3
+ module Matchers
4
+ # @private
5
+ class ExpectationCustomization
6
+ attr_accessor :block
7
+
8
+ def initialize(method_name, args, block)
9
+ @method_name = method_name
10
+ @args = args
11
+ @block = block
12
+ end
13
+
14
+ def playback_onto(expectation)
15
+ expectation.__send__(@method_name, *@args, &@block)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,134 @@
1
+ module RSpec
2
+ module Mocks
3
+ module Matchers
4
+ # @private
5
+ class HaveReceived
6
+ include Matcher
7
+
8
+ COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice]
9
+ ARGS_CONSTRAINTS = %w[with]
10
+ CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered]
11
+
12
+ def initialize(method_name, &block)
13
+ @method_name = method_name
14
+ @block = block
15
+ @constraints = []
16
+ @subject = nil
17
+ end
18
+
19
+ def name
20
+ "have_received"
21
+ end
22
+
23
+ def matches?(subject, &block)
24
+ @block ||= block
25
+ @subject = subject
26
+ @expectation = expect
27
+ mock_proxy.ensure_implemented(@method_name)
28
+
29
+ expected_messages_received_in_order?
30
+ end
31
+
32
+ def does_not_match?(subject)
33
+ @subject = subject
34
+ ensure_count_unconstrained
35
+ @expectation = expect.never
36
+ mock_proxy.ensure_implemented(@method_name)
37
+ expected_messages_received_in_order?
38
+ end
39
+
40
+ def failure_message
41
+ capture_failure_message
42
+ end
43
+
44
+ def failure_message_when_negated
45
+ capture_failure_message
46
+ end
47
+
48
+ def description
49
+ (@expectation ||= expect).description_for("have received")
50
+ end
51
+
52
+ CONSTRAINTS.each do |expectation|
53
+ define_method expectation do |*args|
54
+ @constraints << [expectation, *args]
55
+ self
56
+ end
57
+ end
58
+
59
+ def setup_expectation(subject, &block)
60
+ notify_failure_message unless matches?(subject, &block)
61
+ end
62
+
63
+ def setup_negative_expectation(subject, &block)
64
+ notify_failure_message unless does_not_match?(subject, &block)
65
+ end
66
+
67
+ def setup_allowance(_subject, &_block)
68
+ disallow("allow", " as it would have no effect")
69
+ end
70
+
71
+ def setup_any_instance_allowance(_subject, &_block)
72
+ disallow("allow_any_instance_of")
73
+ end
74
+
75
+ def setup_any_instance_expectation(_subject, &_block)
76
+ disallow("expect_any_instance_of")
77
+ end
78
+
79
+ def setup_any_instance_negative_expectation(_subject, &_block)
80
+ disallow("expect_any_instance_of")
81
+ end
82
+
83
+ private
84
+
85
+ def disallow(type, reason="")
86
+ RSpec::Mocks.error_generator.raise_have_received_disallowed(type, reason)
87
+ end
88
+
89
+ def expect
90
+ expectation = mock_proxy.build_expectation(@method_name)
91
+ apply_constraints_to expectation
92
+ expectation
93
+ end
94
+
95
+ def apply_constraints_to(expectation)
96
+ @constraints.each do |constraint|
97
+ expectation.send(*constraint)
98
+ end
99
+ end
100
+
101
+ def ensure_count_unconstrained
102
+ return unless count_constraint
103
+ RSpec::Mocks.error_generator.raise_cant_constrain_count_for_negated_have_received_error(count_constraint)
104
+ end
105
+
106
+ def count_constraint
107
+ @constraints.map(&:first).find do |constraint|
108
+ COUNT_CONSTRAINTS.include?(constraint)
109
+ end
110
+ end
111
+
112
+ def capture_failure_message
113
+ RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do
114
+ notify_failure_message
115
+ end
116
+ end
117
+
118
+ def notify_failure_message
119
+ mock_proxy.check_for_unexpected_arguments(@expectation)
120
+ @expectation.generate_error
121
+ end
122
+
123
+ def expected_messages_received_in_order?
124
+ mock_proxy.replay_received_message_on @expectation, &@block
125
+ @expectation.expected_messages_received? && @expectation.ensure_expected_ordering_received!
126
+ end
127
+
128
+ def mock_proxy
129
+ RSpec::Mocks.space.proxy_for(@subject)
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,132 @@
1
+ RSpec::Support.require_rspec_mocks 'matchers/expectation_customization'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ module Matchers
6
+ # @private
7
+ class Receive
8
+ include Matcher
9
+
10
+ def initialize(message, block)
11
+ @message = message
12
+ @block = block
13
+ @recorded_customizations = []
14
+ end
15
+
16
+ def name
17
+ "receive"
18
+ end
19
+
20
+ def description
21
+ describable.description_for("receive")
22
+ end
23
+
24
+ def setup_expectation(subject, &block)
25
+ warn_if_any_instance("expect", subject)
26
+ @describable = setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
27
+ end
28
+ alias matches? setup_expectation
29
+
30
+ def setup_negative_expectation(subject, &block)
31
+ # ensure `never` goes first for cases like `never.and_return(5)`,
32
+ # where `and_return` is meant to raise an error
33
+ @recorded_customizations.unshift ExpectationCustomization.new(:never, [], nil)
34
+
35
+ warn_if_any_instance("expect", subject)
36
+
37
+ setup_expectation(subject, &block)
38
+ end
39
+ alias does_not_match? setup_negative_expectation
40
+
41
+ def setup_allowance(subject, &block)
42
+ warn_if_any_instance("allow", subject)
43
+ setup_mock_proxy_method_substitute(subject, :add_stub, block)
44
+ end
45
+
46
+ def setup_any_instance_expectation(subject, &block)
47
+ setup_any_instance_method_substitute(subject, :should_receive, block)
48
+ end
49
+
50
+ def setup_any_instance_negative_expectation(subject, &block)
51
+ setup_any_instance_method_substitute(subject, :should_not_receive, block)
52
+ end
53
+
54
+ def setup_any_instance_allowance(subject, &block)
55
+ setup_any_instance_method_substitute(subject, :stub, block)
56
+ end
57
+
58
+ MessageExpectation.public_instance_methods(false).each do |method|
59
+ next if method_defined?(method)
60
+
61
+ define_method(method) do |*args, &block|
62
+ @recorded_customizations << ExpectationCustomization.new(method, args, block)
63
+ self
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def describable
70
+ @describable ||= DefaultDescribable.new(@message)
71
+ end
72
+
73
+ def warn_if_any_instance(expression, subject)
74
+ return unless AnyInstance::Proxy === subject
75
+
76
+ RSpec.warning(
77
+ "`#{expression}(#{subject.klass}.any_instance).to` " \
78
+ "is probably not what you meant, it does not operate on " \
79
+ "any instance of `#{subject.klass}`. " \
80
+ "Use `#{expression}_any_instance_of(#{subject.klass}).to` instead."
81
+ )
82
+ end
83
+
84
+ def setup_mock_proxy_method_substitute(subject, method, block)
85
+ proxy = ::RSpec::Mocks.space.proxy_for(subject)
86
+ setup_method_substitute(proxy, method, block)
87
+ end
88
+
89
+ def setup_any_instance_method_substitute(subject, method, block)
90
+ proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject)
91
+ setup_method_substitute(proxy, method, block)
92
+ end
93
+
94
+ def setup_method_substitute(host, method, block, *args)
95
+ args << @message.to_sym
96
+ block = move_block_to_last_customization(block)
97
+
98
+ expectation = host.__send__(method, *args, &(@block || block))
99
+
100
+ @recorded_customizations.each do |customization|
101
+ customization.playback_onto(expectation)
102
+ end
103
+ expectation
104
+ end
105
+
106
+ def move_block_to_last_customization(block)
107
+ last = @recorded_customizations.last
108
+ return block unless last
109
+
110
+ last.block ||= block
111
+ nil
112
+ end
113
+
114
+ # MessageExpectation objects are able to describe themselves in detail.
115
+ # We use this as a fall back when a MessageExpectation is not available.
116
+ # @private
117
+ class DefaultDescribable
118
+ def initialize(message)
119
+ @message = message
120
+ end
121
+
122
+ # This is much simpler for the `any_instance` case than what the
123
+ # user may want, but I'm not up for putting a bunch of effort
124
+ # into full descriptions for `any_instance` expectations at this point :(.
125
+ def description_for(verb)
126
+ "#{verb} #{@message}"
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end