rspec-mocks 3.0.4 → 3.12.6
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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +512 -2
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +113 -30
- data/lib/rspec/mocks/any_instance/chain.rb +5 -3
- data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -5
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -8
- data/lib/rspec/mocks/any_instance/message_chains.rb +7 -8
- data/lib/rspec/mocks/any_instance/proxy.rb +14 -5
- data/lib/rspec/mocks/any_instance/recorder.rb +61 -31
- data/lib/rspec/mocks/any_instance/stub_chain.rb +15 -11
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -5
- data/lib/rspec/mocks/any_instance.rb +1 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +55 -10
- data/lib/rspec/mocks/argument_matchers.rb +88 -30
- data/lib/rspec/mocks/configuration.rb +61 -13
- data/lib/rspec/mocks/error_generator.rb +250 -107
- data/lib/rspec/mocks/example_methods.rb +151 -28
- data/lib/rspec/mocks/instance_method_stasher.rb +17 -6
- data/lib/rspec/mocks/matchers/have_received.rb +50 -20
- data/lib/rspec/mocks/matchers/receive.rb +39 -11
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +22 -7
- data/lib/rspec/mocks/matchers/receive_messages.rb +12 -7
- data/lib/rspec/mocks/message_chain.rb +3 -7
- data/lib/rspec/mocks/message_expectation.rb +466 -307
- data/lib/rspec/mocks/method_double.rb +88 -29
- data/lib/rspec/mocks/method_reference.rb +85 -25
- data/lib/rspec/mocks/minitest_integration.rb +68 -0
- data/lib/rspec/mocks/mutate_const.rb +50 -109
- data/lib/rspec/mocks/object_reference.rb +89 -32
- data/lib/rspec/mocks/order_group.rb +4 -5
- data/lib/rspec/mocks/proxy.rb +156 -60
- data/lib/rspec/mocks/space.rb +52 -35
- data/lib/rspec/mocks/standalone.rb +1 -1
- data/lib/rspec/mocks/syntax.rb +26 -30
- data/lib/rspec/mocks/targets.rb +55 -28
- data/lib/rspec/mocks/test_double.rb +43 -7
- data/lib/rspec/mocks/verifying_double.rb +27 -33
- data/lib/rspec/mocks/{verifying_message_expecation.rb → verifying_message_expectation.rb} +11 -16
- data/lib/rspec/mocks/verifying_proxy.rb +77 -26
- data/lib/rspec/mocks/version.rb +1 -1
- data/lib/rspec/mocks.rb +8 -1
- data.tar.gz.sig +0 -0
- metadata +80 -43
- metadata.gz.sig +0 -0
data/lib/rspec/mocks/proxy.rb
CHANGED
@@ -2,29 +2,45 @@ module RSpec
|
|
2
2
|
module Mocks
|
3
3
|
# @private
|
4
4
|
class Proxy
|
5
|
+
# @private
|
5
6
|
SpecificMessage = Struct.new(:object, :message, :args) do
|
6
7
|
def ==(expectation)
|
7
8
|
expectation.orig_object == object && expectation.matches?(message, *args)
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
12
|
+
unless defined?(Mutex)
|
13
|
+
Support.require_rspec_support 'mutex'
|
14
|
+
Mutex = Support::Mutex
|
15
|
+
end
|
16
|
+
|
11
17
|
# @private
|
12
|
-
def ensure_implemented(*
|
18
|
+
def ensure_implemented(*_args)
|
13
19
|
# noop for basic proxies, see VerifyingProxy for behaviour.
|
14
20
|
end
|
15
21
|
|
16
22
|
# @private
|
17
|
-
def initialize(object, order_group,
|
23
|
+
def initialize(object, order_group, options={})
|
24
|
+
ensure_can_be_proxied!(object)
|
25
|
+
|
18
26
|
@object = object
|
19
27
|
@order_group = order_group
|
20
|
-
@
|
21
|
-
@error_generator = ErrorGenerator.new(object, name)
|
28
|
+
@error_generator = ErrorGenerator.new(object)
|
22
29
|
@messages_received = []
|
30
|
+
@messages_received_mutex = Mutex.new
|
23
31
|
@options = options
|
24
32
|
@null_object = false
|
25
33
|
@method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
|
26
34
|
end
|
27
35
|
|
36
|
+
# @private
|
37
|
+
def ensure_can_be_proxied!(object)
|
38
|
+
return unless object.is_a?(Symbol)
|
39
|
+
|
40
|
+
msg = "Cannot proxy frozen objects. Symbols such as #{object} cannot be mocked or stubbed."
|
41
|
+
raise ArgumentError, msg
|
42
|
+
end
|
43
|
+
|
28
44
|
# @private
|
29
45
|
attr_reader :object
|
30
46
|
|
@@ -42,12 +58,14 @@ module RSpec
|
|
42
58
|
end
|
43
59
|
|
44
60
|
# @private
|
45
|
-
def original_method_handle_for(
|
61
|
+
def original_method_handle_for(_message)
|
46
62
|
nil
|
47
63
|
end
|
48
64
|
|
65
|
+
DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze
|
66
|
+
|
49
67
|
# @private
|
50
|
-
def add_message_expectation(method_name, opts=
|
68
|
+
def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block)
|
51
69
|
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
|
52
70
|
meth_double = method_double_for(method_name)
|
53
71
|
|
@@ -88,21 +106,30 @@ module RSpec
|
|
88
106
|
@error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
|
89
107
|
end
|
90
108
|
|
91
|
-
@
|
92
|
-
|
93
|
-
expectation.
|
94
|
-
|
109
|
+
@messages_received_mutex.synchronize do
|
110
|
+
@messages_received.each do |(actual_method_name, args, received_block)|
|
111
|
+
next unless expectation.matches?(actual_method_name, *args)
|
112
|
+
|
113
|
+
expectation.safe_invoke(nil)
|
114
|
+
block.call(*args, &received_block) if block
|
95
115
|
end
|
96
116
|
end
|
97
|
-
|
98
117
|
end
|
99
118
|
|
100
119
|
# @private
|
101
120
|
def check_for_unexpected_arguments(expectation)
|
102
|
-
@
|
103
|
-
if
|
104
|
-
|
121
|
+
@messages_received_mutex.synchronize do
|
122
|
+
return if @messages_received.empty?
|
123
|
+
|
124
|
+
return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
|
125
|
+
|
126
|
+
name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
|
127
|
+
expectation.matches_name_but_not_args(method_name, *args)
|
105
128
|
end
|
129
|
+
|
130
|
+
return if name_but_not_args.empty? && !others.empty?
|
131
|
+
|
132
|
+
expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
|
106
133
|
end
|
107
134
|
end
|
108
135
|
|
@@ -129,29 +156,43 @@ module RSpec
|
|
129
156
|
|
130
157
|
# @private
|
131
158
|
def verify
|
132
|
-
@method_doubles.each_value {|d| d.verify}
|
159
|
+
@method_doubles.each_value { |d| d.verify }
|
133
160
|
end
|
134
161
|
|
135
162
|
# @private
|
136
163
|
def reset
|
137
|
-
@
|
164
|
+
@messages_received_mutex.synchronize do
|
165
|
+
@messages_received.clear
|
166
|
+
end
|
138
167
|
end
|
139
168
|
|
140
169
|
# @private
|
141
170
|
def received_message?(method_name, *args, &block)
|
142
|
-
@
|
171
|
+
@messages_received_mutex.synchronize do
|
172
|
+
@messages_received.any? { |array| array == [method_name, args, block] }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# @private
|
177
|
+
def messages_arg_list
|
178
|
+
@messages_received_mutex.synchronize do
|
179
|
+
@messages_received.map { |_, args, _| args }
|
180
|
+
end
|
143
181
|
end
|
144
182
|
|
145
183
|
# @private
|
146
184
|
def has_negative_expectation?(message)
|
147
|
-
method_double_for(message).expectations.
|
185
|
+
method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) }
|
148
186
|
end
|
149
187
|
|
150
188
|
# @private
|
151
189
|
def record_message_received(message, *args, &block)
|
152
190
|
@order_group.invoked SpecificMessage.new(object, message, args)
|
153
|
-
@
|
191
|
+
@messages_received_mutex.synchronize do
|
192
|
+
@messages_received << [message, args, block]
|
193
|
+
end
|
154
194
|
end
|
195
|
+
ruby2_keywords :record_message_received if respond_to?(:ruby2_keywords, true)
|
155
196
|
|
156
197
|
# @private
|
157
198
|
def message_received(message, *args, &block)
|
@@ -162,57 +203,67 @@ module RSpec
|
|
162
203
|
|
163
204
|
if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
|
164
205
|
expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters?
|
165
|
-
if expectation = find_almost_matching_expectation(message, *args)
|
206
|
+
if (expectation = find_almost_matching_expectation(message, *args))
|
166
207
|
expectation.advise(*args) unless expectation.expected_messages_received?
|
167
208
|
end
|
168
209
|
stub.invoke(nil, *args, &block)
|
169
210
|
elsif expectation
|
211
|
+
expectation.unadvise(messages_arg_list)
|
170
212
|
expectation.invoke(stub, *args, &block)
|
171
|
-
elsif expectation = find_almost_matching_expectation(message, *args)
|
213
|
+
elsif (expectation = find_almost_matching_expectation(message, *args))
|
172
214
|
expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
|
173
|
-
|
174
|
-
|
215
|
+
|
216
|
+
if null_object? || !has_negative_expectation?(message)
|
217
|
+
expectation.raise_unexpected_message_args_error([args])
|
218
|
+
end
|
219
|
+
elsif (stub = find_almost_matching_stub(message, *args))
|
175
220
|
stub.advise(*args)
|
176
|
-
raise_missing_default_stub_error(stub,
|
221
|
+
raise_missing_default_stub_error(stub, [args])
|
177
222
|
elsif Class === @object
|
178
223
|
@object.superclass.__send__(message, *args, &block)
|
179
224
|
else
|
180
225
|
@object.__send__(:method_missing, message, *args, &block)
|
181
226
|
end
|
182
227
|
end
|
228
|
+
ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
|
183
229
|
|
184
230
|
# @private
|
185
|
-
def raise_unexpected_message_error(method_name,
|
186
|
-
@error_generator.raise_unexpected_message_error method_name,
|
231
|
+
def raise_unexpected_message_error(method_name, args)
|
232
|
+
@error_generator.raise_unexpected_message_error method_name, args
|
187
233
|
end
|
188
234
|
|
189
235
|
# @private
|
190
|
-
def
|
191
|
-
@error_generator.
|
236
|
+
def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
|
237
|
+
@error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls)
|
192
238
|
end
|
193
239
|
|
194
240
|
# @private
|
195
|
-
def
|
196
|
-
@error_generator.raise_missing_default_stub_error(expectation, *args)
|
197
|
-
end
|
198
|
-
|
199
|
-
# @private
|
200
|
-
def visibility_for(method_name)
|
241
|
+
def visibility_for(_method_name)
|
201
242
|
# This is the default (for test doubles). Subclasses override this.
|
202
243
|
:public
|
203
244
|
end
|
204
245
|
|
205
246
|
if Support::RubyFeatures.module_prepends_supported?
|
247
|
+
def self.prepended_modules_of(klass)
|
248
|
+
ancestors = klass.ancestors
|
249
|
+
|
250
|
+
# `|| 0` is necessary for Ruby 2.0, where the singleton class
|
251
|
+
# is only in the ancestor list when there are prepended modules.
|
252
|
+
singleton_index = ancestors.index(klass) || 0
|
253
|
+
|
254
|
+
ancestors[0, singleton_index]
|
255
|
+
end
|
256
|
+
|
206
257
|
def prepended_modules_of_singleton_class
|
207
|
-
@prepended_modules_of_singleton_class ||=
|
208
|
-
singleton_class = @object.singleton_class
|
209
|
-
singleton_class.ancestors.take_while do |mod|
|
210
|
-
!(Class === mod || @object.equal?(mod))
|
211
|
-
end
|
212
|
-
end
|
258
|
+
@prepended_modules_of_singleton_class ||= RSpec::Mocks::Proxy.prepended_modules_of(@object.singleton_class)
|
213
259
|
end
|
214
260
|
end
|
215
261
|
|
262
|
+
# @private
|
263
|
+
def method_double_if_exists_for_message(message)
|
264
|
+
method_double_for(message) if @method_doubles.key?(message.to_sym)
|
265
|
+
end
|
266
|
+
|
216
267
|
private
|
217
268
|
|
218
269
|
def method_double_for(message)
|
@@ -224,12 +275,14 @@ module RSpec
|
|
224
275
|
expectation.matches?(method_name, *args)
|
225
276
|
end
|
226
277
|
end
|
278
|
+
ruby2_keywords :find_matching_expectation if respond_to?(:ruby2_keywords, true)
|
227
279
|
|
228
280
|
def find_almost_matching_expectation(method_name, *args)
|
229
281
|
find_best_matching_expectation_for(method_name) do |expectation|
|
230
282
|
expectation.matches_name_but_not_args(method_name, *args)
|
231
283
|
end
|
232
284
|
end
|
285
|
+
ruby2_keywords :find_almost_matching_expectation if respond_to?(:ruby2_keywords, true)
|
233
286
|
|
234
287
|
def find_best_matching_expectation_for(method_name)
|
235
288
|
first_match = nil
|
@@ -244,12 +297,14 @@ module RSpec
|
|
244
297
|
end
|
245
298
|
|
246
299
|
def find_matching_method_stub(method_name, *args)
|
247
|
-
method_double_for(method_name).stubs.find {|stub| stub.matches?(method_name, *args)}
|
300
|
+
method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
|
248
301
|
end
|
302
|
+
ruby2_keywords :find_matching_method_stub if respond_to?(:ruby2_keywords, true)
|
249
303
|
|
250
304
|
def find_almost_matching_stub(method_name, *args)
|
251
|
-
method_double_for(method_name).stubs.find {|stub| stub.matches_name_but_not_args(method_name, *args)}
|
305
|
+
method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
|
252
306
|
end
|
307
|
+
ruby2_keywords :find_almost_matching_stub if respond_to?(:ruby2_keywords, true)
|
253
308
|
end
|
254
309
|
|
255
310
|
# @private
|
@@ -295,7 +350,7 @@ module RSpec
|
|
295
350
|
end
|
296
351
|
|
297
352
|
def reset
|
298
|
-
@method_doubles.each_value {|d| d.reset}
|
353
|
+
@method_doubles.each_value { |d| d.reset }
|
299
354
|
super
|
300
355
|
end
|
301
356
|
|
@@ -305,6 +360,7 @@ module RSpec
|
|
305
360
|
end
|
306
361
|
super
|
307
362
|
end
|
363
|
+
ruby2_keywords :message_received if respond_to?(:ruby2_keywords, true)
|
308
364
|
|
309
365
|
private
|
310
366
|
|
@@ -356,27 +412,43 @@ module RSpec
|
|
356
412
|
|
357
413
|
return super unless unbound_method
|
358
414
|
unbound_method.bind(object)
|
415
|
+
# :nocov:
|
416
|
+
rescue TypeError
|
417
|
+
if RUBY_VERSION == '1.8.7'
|
418
|
+
# In MRI 1.8.7, a singleton method on a class cannot be rebound to its subclass
|
419
|
+
if unbound_method && unbound_method.owner.ancestors.first != unbound_method.owner
|
420
|
+
# This is a singleton method; we can't do anything with it
|
421
|
+
# But we can work around this using a different implementation
|
422
|
+
double = method_double_from_ancestor_for(message)
|
423
|
+
return object.method(double.method_stasher.stashed_method_name)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
raise
|
427
|
+
# :nocov:
|
359
428
|
end
|
360
429
|
|
361
430
|
protected
|
362
431
|
|
363
432
|
def original_unbound_method_handle_from_ancestor_for(message)
|
364
|
-
|
433
|
+
double = method_double_from_ancestor_for(message)
|
434
|
+
double && double.original_method.unbind
|
435
|
+
end
|
436
|
+
|
437
|
+
def method_double_from_ancestor_for(message)
|
438
|
+
@method_doubles.fetch(message) do
|
365
439
|
# The fact that there is no method double for this message indicates
|
366
440
|
# that it has not been redefined by rspec-mocks. We need to continue
|
367
441
|
# looking up the ancestor chain.
|
368
442
|
return superclass_proxy &&
|
369
|
-
superclass_proxy.
|
443
|
+
superclass_proxy.method_double_from_ancestor_for(message)
|
370
444
|
end
|
371
|
-
|
372
|
-
method_double.original_method.unbind
|
373
445
|
end
|
374
446
|
|
375
447
|
def superclass_proxy
|
376
448
|
return @superclass_proxy if defined?(@superclass_proxy)
|
377
449
|
|
378
450
|
if (superclass = object.superclass)
|
379
|
-
@superclass_proxy = @source_space.
|
451
|
+
@superclass_proxy = @source_space.superclass_proxy_for(superclass)
|
380
452
|
else
|
381
453
|
@superclass_proxy = nil
|
382
454
|
end
|
@@ -391,33 +463,57 @@ module RSpec
|
|
391
463
|
# @private
|
392
464
|
class ProxyForNil < PartialDoubleProxy
|
393
465
|
def initialize(order_group)
|
394
|
-
|
466
|
+
set_expectation_behavior
|
395
467
|
super(nil, order_group)
|
396
468
|
end
|
397
469
|
|
470
|
+
attr_accessor :disallow_expectations
|
398
471
|
attr_accessor :warn_about_expectations
|
399
|
-
alias warn_about_expectations? warn_about_expectations
|
400
472
|
|
401
473
|
def add_message_expectation(method_name, opts={}, &block)
|
402
|
-
|
403
|
-
super
|
404
|
-
end
|
405
|
-
|
406
|
-
def add_negative_message_expectation(location, method_name, &implementation)
|
407
|
-
warn(method_name) if warn_about_expectations?
|
474
|
+
warn_or_raise!(method_name)
|
408
475
|
super
|
409
476
|
end
|
410
477
|
|
411
478
|
def add_stub(method_name, opts={}, &implementation)
|
412
|
-
|
479
|
+
warn_or_raise!(method_name)
|
413
480
|
super
|
414
481
|
end
|
415
482
|
|
416
483
|
private
|
417
484
|
|
418
|
-
def
|
419
|
-
|
420
|
-
|
485
|
+
def set_expectation_behavior
|
486
|
+
case RSpec::Mocks.configuration.allow_message_expectations_on_nil
|
487
|
+
when false
|
488
|
+
@warn_about_expectations = false
|
489
|
+
@disallow_expectations = true
|
490
|
+
when true
|
491
|
+
@warn_about_expectations = false
|
492
|
+
@disallow_expectations = false
|
493
|
+
else
|
494
|
+
@warn_about_expectations = true
|
495
|
+
@disallow_expectations = false
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
def warn_or_raise!(method_name)
|
500
|
+
# This method intentionally swallows the message when
|
501
|
+
# neither disallow_expectations nor warn_about_expectations
|
502
|
+
# are set to true.
|
503
|
+
if disallow_expectations
|
504
|
+
raise_error(method_name)
|
505
|
+
elsif warn_about_expectations
|
506
|
+
warn(method_name)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
def warn(method_name)
|
511
|
+
warning_msg = @error_generator.expectation_on_nil_message(method_name)
|
512
|
+
RSpec.warning(warning_msg)
|
513
|
+
end
|
514
|
+
|
515
|
+
def raise_error(method_name)
|
516
|
+
@error_generator.raise_expectation_on_nil_error(method_name)
|
421
517
|
end
|
422
518
|
end
|
423
519
|
end
|
data/lib/rspec/mocks/space.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
RSpec::Support.require_rspec_support 'reentrant_mutex'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Mocks
|
3
5
|
# @private
|
@@ -5,23 +7,23 @@ module RSpec
|
|
5
7
|
# the scope of an example. Called "root" because it serves
|
6
8
|
# as the root of the space stack.
|
7
9
|
class RootSpace
|
8
|
-
def proxy_for(*
|
10
|
+
def proxy_for(*_args)
|
9
11
|
raise_lifecycle_message
|
10
12
|
end
|
11
13
|
|
12
|
-
def any_instance_recorder_for(*
|
14
|
+
def any_instance_recorder_for(*_args)
|
13
15
|
raise_lifecycle_message
|
14
16
|
end
|
15
17
|
|
16
|
-
def any_instance_proxy_for(*
|
18
|
+
def any_instance_proxy_for(*_args)
|
17
19
|
raise_lifecycle_message
|
18
20
|
end
|
19
21
|
|
20
|
-
def register_constant_mutator(
|
22
|
+
def register_constant_mutator(_mutator)
|
21
23
|
raise_lifecycle_message
|
22
24
|
end
|
23
25
|
|
24
|
-
def any_instance_recorders_from_ancestry_of(
|
26
|
+
def any_instance_recorders_from_ancestry_of(_object)
|
25
27
|
raise_lifecycle_message
|
26
28
|
end
|
27
29
|
|
@@ -31,10 +33,14 @@ module RSpec
|
|
31
33
|
def verify_all
|
32
34
|
end
|
33
35
|
|
34
|
-
def registered?(
|
36
|
+
def registered?(_object)
|
35
37
|
false
|
36
38
|
end
|
37
39
|
|
40
|
+
def superclass_proxy_for(*_args)
|
41
|
+
raise_lifecycle_message
|
42
|
+
end
|
43
|
+
|
38
44
|
def new_scope
|
39
45
|
Space.new
|
40
46
|
end
|
@@ -43,7 +49,7 @@ module RSpec
|
|
43
49
|
|
44
50
|
def raise_lifecycle_message
|
45
51
|
raise OutsideOfExampleError,
|
46
|
-
|
52
|
+
"The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported."
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
@@ -71,9 +77,9 @@ module RSpec
|
|
71
77
|
|
72
78
|
def reset_all
|
73
79
|
proxies.each_value { |proxy| proxy.reset }
|
74
|
-
@constant_mutators.reverse.each { |mut| mut.idempotently_reset }
|
75
80
|
any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! }
|
76
81
|
any_instance_recorders.clear
|
82
|
+
@constant_mutators.reverse.each { |mut| mut.idempotently_reset }
|
77
83
|
end
|
78
84
|
|
79
85
|
def register_constant_mutator(mutator)
|
@@ -84,7 +90,7 @@ module RSpec
|
|
84
90
|
@constant_mutators.find { |m| m.full_constant_name == name }
|
85
91
|
end
|
86
92
|
|
87
|
-
def any_instance_recorder_for(klass, only_return_existing
|
93
|
+
def any_instance_recorder_for(klass, only_return_existing=false)
|
88
94
|
any_instance_mutex.synchronize do
|
89
95
|
id = klass.__id__
|
90
96
|
any_instance_recorders.fetch(id) do
|
@@ -109,10 +115,17 @@ module RSpec
|
|
109
115
|
end
|
110
116
|
end
|
111
117
|
|
118
|
+
def superclass_proxy_for(klass)
|
119
|
+
proxy_mutex.synchronize do
|
120
|
+
id = id_for(klass)
|
121
|
+
proxies.fetch(id) { superclass_proxy_not_found_for(id, klass) }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
112
125
|
alias ensure_registered proxy_for
|
113
126
|
|
114
127
|
def registered?(object)
|
115
|
-
proxies.
|
128
|
+
proxies.key?(id_for object)
|
116
129
|
end
|
117
130
|
|
118
131
|
def any_instance_recorders_from_ancestry_of(object)
|
@@ -131,36 +144,40 @@ module RSpec
|
|
131
144
|
|
132
145
|
private
|
133
146
|
|
134
|
-
# We don't want to depend on the stdlib ourselves, but if the user is
|
135
|
-
# using threads then a Mutex will be available to us. If not, we don't
|
136
|
-
# need to synchronize anyway.
|
137
147
|
def new_mutex
|
138
|
-
|
139
|
-
end
|
140
|
-
|
141
|
-
# @private
|
142
|
-
module FakeMutex
|
143
|
-
def self.synchronize
|
144
|
-
yield
|
145
|
-
end
|
148
|
+
Support::ReentrantMutex.new
|
146
149
|
end
|
147
150
|
|
148
151
|
def proxy_not_found_for(id, object)
|
149
152
|
proxies[id] = case object
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
153
|
+
when NilClass then ProxyForNil.new(@expectation_ordering)
|
154
|
+
when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering)
|
155
|
+
when Class
|
156
|
+
class_proxy_with_callback_verification_strategy(object, CallbackInvocationStrategy.new)
|
157
|
+
else
|
158
|
+
if RSpec::Mocks.configuration.verify_partial_doubles?
|
159
|
+
VerifyingPartialDoubleProxy.new(object, @expectation_ordering)
|
160
|
+
else
|
161
|
+
PartialDoubleProxy.new(object, @expectation_ordering)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def superclass_proxy_not_found_for(id, object)
|
167
|
+
raise "superclass_proxy_not_found_for called with something that is not a class" unless Class === object
|
168
|
+
proxies[id] = class_proxy_with_callback_verification_strategy(object, NoCallbackInvocationStrategy.new)
|
169
|
+
end
|
170
|
+
|
171
|
+
def class_proxy_with_callback_verification_strategy(object, strategy)
|
172
|
+
if RSpec::Mocks.configuration.verify_partial_doubles?
|
173
|
+
VerifyingPartialClassDoubleProxy.new(
|
174
|
+
self,
|
175
|
+
object,
|
176
|
+
@expectation_ordering,
|
177
|
+
strategy
|
178
|
+
)
|
179
|
+
else
|
180
|
+
PartialClassDoubleProxy.new(self, object, @expectation_ordering)
|
164
181
|
end
|
165
182
|
end
|
166
183
|
|