flexmock 2.3.0 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 471129d58809ac2dc9a53b22254f76e352f5b253
4
- data.tar.gz: 3ac4914a786029b8cb1c10750b28f7de602c984b
3
+ metadata.gz: 16f8df99b548903abdd25fef26a98df2906448bf
4
+ data.tar.gz: 7ce30d1e5ada1a4d679e53db575bd3d942b8e836
5
5
  SHA512:
6
- metadata.gz: dcab0eed825f701d642cce8286b9816b087b765beff23fe39205841e1289ecdfc80284a14d59cbbfb90a27d5050c50b25618fd5f27a6f9db4367e2b0b739360c
7
- data.tar.gz: 8e97cc8946bb2fac86b6b69cb01dec2c42a2cb3aa67d0307dc696e94228bccd66f82cabe1a8c6bdb92b096ba91b3a6889b898e40af714cbc7c7d071b0496a4aa
6
+ metadata.gz: 7ff6a1a9172b9df71a8d5d3c8717304cad88d7947ea212c45417f53106001b71587879bfcd9684301440fc766db0a48552f5fc80b81dbd55e4d65c81acc55a23
7
+ data.tar.gz: e93acaa91ace46df1fd14ead3c4790b857a654fb901d6a4d08193eae607abf65eeefa78e199b5e034855c72752d5b7bb824aa2d88455b229c740842dc2e8871a
data/lib/flexmock/core.rb CHANGED
@@ -45,7 +45,7 @@ class FlexMock
45
45
  include Ordering
46
46
 
47
47
  attr_reader :flexmock_name
48
- attr_accessor :flexmock_container
48
+ attr_reader :flexmock_container_stack
49
49
 
50
50
  class << self
51
51
  attr_accessor :partials_are_based
@@ -54,21 +54,50 @@ class FlexMock
54
54
  self.partials_are_based = false
55
55
  self.partials_verify_signatures = false
56
56
 
57
+ # Null object for {#parent_mock}
58
+ class NullParentMock
59
+ def flexmock_expectations_for(sym)
60
+ end
61
+ end
62
+
57
63
  # Create a FlexMock object with the given name. The name is used in
58
64
  # error messages. If no container is given, create a new, one-off
59
65
  # container for this mock.
60
- def initialize(name="unknown", container=nil)
66
+ def initialize(name="unknown", container=nil, parent: nil)
61
67
  @flexmock_name = name
62
68
  @flexmock_closed = false
69
+ @flexmock_container_stack = Array.new
63
70
  @expectations = Hash.new
64
- @ignore_missing = false
65
71
  @verified = false
66
72
  @calls = []
67
73
  @base_class = nil
74
+ if parent
75
+ @ignore_missing = parent.ignore_missing?
76
+ @parent_mock = parent
77
+ else
78
+ @ignore_missing = false
79
+ @parent_mock = NullParentMock.new
80
+ end
68
81
  container = UseContainer.new if container.nil?
69
82
  container.flexmock_remember(self)
70
83
  end
71
84
 
85
+ def ignore_missing?
86
+ @ignore_missing
87
+ end
88
+
89
+ def flexmock_container
90
+ flexmock_container_stack.last
91
+ end
92
+
93
+ def push_flexmock_container(container)
94
+ flexmock_container_stack.push(container)
95
+ end
96
+
97
+ def pop_flexmock_container
98
+ flexmock_container_stack.pop
99
+ end
100
+
72
101
  # Return the inspection string for a mock.
73
102
  def inspect
74
103
  "<FlexMock:#{flexmock_name}>"
@@ -117,8 +146,8 @@ class FlexMock
117
146
  flexmock_wrap do
118
147
  if flexmock_closed?
119
148
  FlexMock.undefined
120
- elsif handler = @expectations[sym]
121
- handler.call(enhanced_args, call_record)
149
+ elsif exp = flexmock_expectations_for(sym)
150
+ exp.call(enhanced_args, call_record)
122
151
  elsif @base_class && @base_class.flexmock_defined?(sym)
123
152
  FlexMock.undefined
124
153
  elsif @ignore_missing
@@ -139,13 +168,14 @@ class FlexMock
139
168
 
140
169
  # Find the mock expectation for method sym and arguments.
141
170
  def flexmock_find_expectation(method_name, *args) # :nodoc:
142
- exp = @expectations[method_name]
143
- exp ? exp.find_expectation(*args) : nil
171
+ if exp = flexmock_expectations_for(method_name)
172
+ exp.find_expectation(*args)
173
+ end
144
174
  end
145
175
 
146
176
  # Return the expectation director for a method name.
147
177
  def flexmock_expectations_for(method_name) # :nodoc:
148
- @expectations[method_name]
178
+ @expectations[method_name] || @parent_mock.flexmock_expectations_for(method_name)
149
179
  end
150
180
 
151
181
  def flexmock_base_class
@@ -183,9 +213,9 @@ class FlexMock
183
213
 
184
214
  # Override the built-in +method+ to include the mocked methods.
185
215
  def method(method_name)
186
- @expectations[method_name] || super
216
+ flexmock_expectations_for(method_name) || super
187
217
  rescue NameError => ex
188
- if @ignore_missing
218
+ if ignore_missing?
189
219
  proc { FlexMock.undefined }
190
220
  else
191
221
  raise ex
@@ -218,10 +248,11 @@ class FlexMock
218
248
  # Using +location+, define the expectations specified by +args+.
219
249
  def flexmock_define_expectation(location, *args)
220
250
  @last_expectation = EXP_BUILDER.parse_should_args(self, args) do |method_name|
221
- @expectations[method_name] ||= ExpectationDirector.new(method_name)
251
+ exp = flexmock_expectations_for(method_name) || ExpectationDirector.new(method_name)
252
+ @expectations[method_name] = exp
222
253
  result = Expectation.new(self, method_name, location)
223
- @expectations[method_name] << result
224
- override_existing_method(method_name) if flexmock_respond_to?(method_name)
254
+ exp << result
255
+ override_existing_method(method_name) if flexmock_respond_to?(method_name, true)
225
256
  result = ExplicitNeeded.new(result, method_name, @base_class) if
226
257
  @base_class && ! @base_class.flexmock_defined?(method_name)
227
258
  result
@@ -143,11 +143,6 @@ class FlexMock
143
143
  @count_validators.all? { |v| v.eligible?(@actual_count) }
144
144
  end
145
145
 
146
- # Is this expectation constrained by any call counts?
147
- def call_count_constrained?
148
- ! @count_validators.empty?
149
- end
150
-
151
146
  # Validate that the order
152
147
  def validate_order
153
148
  if @order_number
@@ -55,7 +55,7 @@ class FlexMock
55
55
  elsif names.size > 1
56
56
  create_demeter_chain(mock, names)
57
57
  else
58
- fail "Empty list of names"
58
+ fail ArgumentError, "Empty list of names"
59
59
  end
60
60
  end
61
61
 
@@ -52,6 +52,7 @@ class FlexMock
52
52
  def flexmock_close
53
53
  flexmock_created_mocks.each do |m|
54
54
  m.flexmock_teardown
55
+ m.pop_flexmock_container
55
56
  end
56
57
  @flexmock_created_mocks = []
57
58
  end
@@ -127,7 +128,7 @@ class FlexMock
127
128
  def flexmock_remember(mocking_object)
128
129
  @flexmock_created_mocks ||= []
129
130
  @flexmock_created_mocks << mocking_object
130
- mocking_object.flexmock_container = self
131
+ mocking_object.push_flexmock_container(self)
131
132
  mocking_object
132
133
  end
133
134
 
@@ -28,27 +28,64 @@ class FlexMock
28
28
 
29
29
  attr_reader :mock
30
30
 
31
- ProxyBox = Struct.new :proxy
31
+ # Boxing of the flexmock proxy
32
+ #
33
+ # It is managed as a stack in order to allow to setup containers recursively
34
+ # (as e.g. FlexMock.use( ... ) checks)
35
+ class ProxyBox
36
+ attr_reader :stack
37
+
38
+ Element = Struct.new :proxy, :container
39
+
40
+ def initialize
41
+ @stack = [Element.new]
42
+ end
43
+
44
+ # Tests whether the given container is the one on which the current proxy
45
+ # acts
46
+ def container
47
+ stack.last.container
48
+ end
49
+
50
+ def proxy
51
+ stack.last.proxy
52
+ end
53
+
54
+ def push(proxy, container)
55
+ stack.push(Element.new(proxy, container))
56
+ end
57
+
58
+ def pop
59
+ if !stack.empty?
60
+ stack.pop
61
+ end
62
+ end
63
+
64
+ def empty?
65
+ stack.size == 1
66
+ end
67
+ end
32
68
 
33
69
  # Make a partial mock proxy and install it on the target +obj+.
34
70
  def self.make_proxy_for(obj, container, name, safe_mode)
35
71
  name ||= "flexmock(#{obj.class.to_s})"
36
- if ! proxy_defined_on?(obj)
37
- mock = FlexMock.new(name, container)
38
- proxy = PartialMockProxy.new(obj, mock, safe_mode)
39
- if obj.instance_variable_defined?("@flexmock_proxy")
40
- obj.instance_variable_get("@flexmock_proxy").proxy = proxy
41
- else
42
- obj.instance_variable_set("@flexmock_proxy", ProxyBox.new(proxy))
43
- end
72
+ if !obj.instance_variable_defined?("@flexmock_proxy")
73
+ proxy_box = obj.instance_variable_set("@flexmock_proxy", ProxyBox.new)
74
+ else
75
+ proxy_box = obj.instance_variable_get("@flexmock_proxy")
44
76
  end
45
- obj.instance_variable_get("@flexmock_proxy").proxy
46
- end
47
77
 
48
- # Is there a mock proxy defined on the domain object?
49
- def self.proxy_defined_on?(obj)
50
- obj.instance_variable_defined?("@flexmock_proxy") &&
51
- obj.instance_variable_get("@flexmock_proxy").proxy
78
+ if proxy_box.container != container
79
+ if !proxy_box.empty?
80
+ parent_proxy, _ = proxy_box.proxy
81
+ parent_mock = parent_proxy.mock
82
+ end
83
+
84
+ mock = FlexMock.new(name, container, parent: parent_mock)
85
+ proxy = PartialMockProxy.new(obj, mock, safe_mode, parent: parent_proxy)
86
+ proxy_box.push(proxy, container)
87
+ end
88
+ proxy_box.proxy
52
89
  end
53
90
 
54
91
  # The following methods are added to partial mocks so that they
@@ -62,14 +99,21 @@ class FlexMock
62
99
  :invoke_original
63
100
  ]
64
101
 
102
+ attr_reader :method_definitions
103
+
65
104
  # Initialize a PartialMockProxy object.
66
- def initialize(obj, mock, safe_mode)
105
+ def initialize(obj, mock, safe_mode, parent: nil)
67
106
  @obj = obj
68
107
  @mock = mock
69
- @method_definitions = {}
70
- @methods_proxied = []
71
108
  @proxy_definition_module = nil
72
109
  @initialize_override = nil
110
+
111
+ if parent
112
+ @method_definitions = parent.method_definitions.dup
113
+ else
114
+ @method_definitions = {}
115
+ end
116
+
73
117
  unless safe_mode
74
118
  add_mock_method(:should_receive)
75
119
  MOCK_METHODS.each do |sym|
@@ -85,6 +129,14 @@ class FlexMock
85
129
  @mock
86
130
  end
87
131
 
132
+ def push_flexmock_container(container)
133
+ @mock.push_flexmock_container(container)
134
+ end
135
+
136
+ def pop_flexmock_container
137
+ @mock.pop_flexmock_container
138
+ end
139
+
88
140
  # :call-seq:
89
141
  # should_receive(:method_name)
90
142
  # should_receive(:method1, method2, ...)
@@ -119,9 +171,20 @@ class FlexMock
119
171
  flexmock_invoke_original(m, args)
120
172
  end
121
173
 
174
+ # Whether the given method's original definition has been stored
175
+ def has_original_method?(m)
176
+ @method_definitions.has_key?(m)
177
+ end
178
+
179
+ # Whether the given method is already being proxied
180
+ def has_proxied_method?(m)
181
+ @proxy_definition_module &&
182
+ @proxy_definition_module.method_defined?(m)
183
+ end
184
+
122
185
  def flexmock_define_expectation(location, *args)
123
186
  EXP_BUILDER.parse_should_args(self, args) do |method_name|
124
- if !@methods_proxied.include?(method_name)
187
+ if !has_proxied_method?(method_name) && !has_original_method?(method_name)
125
188
  hide_existing_method(method_name)
126
189
  end
127
190
  ex = @mock.flexmock_define_expectation(location, method_name)
@@ -278,7 +341,7 @@ class FlexMock
278
341
  end
279
342
  if @obj.instance_variable_defined?(:@flexmock_proxy) &&
280
343
  (box = @obj.instance_variable_get(:@flexmock_proxy))
281
- box.proxy = nil
344
+ box.pop
282
345
  end
283
346
  @obj = nil
284
347
  end
@@ -358,9 +421,8 @@ class FlexMock
358
421
  # Stow the existing method definition so that it can be recovered
359
422
  # later.
360
423
  def stow_existing_definition(method_name)
361
- if !@methods_proxied.include?(method_name)
424
+ if !@method_definitions.has_key?(method_name)
362
425
  @method_definitions[method_name] = target_class_eval { instance_method(method_name) }
363
- @methods_proxied << method_name
364
426
  end
365
427
  @method_definitions[method_name]
366
428
  rescue NameError
@@ -390,6 +452,5 @@ class FlexMock
390
452
  def detached?
391
453
  @obj.nil?
392
454
  end
393
-
394
455
  end
395
456
  end
@@ -213,18 +213,28 @@ class FlexMock
213
213
  # @param [Array] args
214
214
  # @raise ValidationFailed
215
215
  def validate(args)
216
+ args = args.dup
216
217
  kw_args = Hash.new
218
+
219
+ last_is_proc = false
220
+ begin
221
+ if args.last.kind_of?(Proc)
222
+ args.pop
223
+ last_is_proc = true
224
+ end
225
+ rescue NoMethodError
226
+ end
227
+
228
+ last_is_kw_hash = false
217
229
  if expects_keyword_arguments?
218
230
  last_is_kw_hash =
219
- begin
220
- args.last.kind_of?(Hash)
221
- rescue NoMethodError
222
- false
223
- end
231
+ begin
232
+ args.last.kind_of?(Hash)
233
+ rescue NoMethodError
234
+ end
224
235
 
225
236
  if last_is_kw_hash
226
- kw_args = args[-1]
227
- args = args[0..-2]
237
+ kw_args = args.pop
228
238
  elsif requires_keyword_arguments?
229
239
  raise ValidationFailed, "#{@exp} expects keyword arguments but none were provided"
230
240
  end
@@ -232,12 +242,34 @@ class FlexMock
232
242
 
233
243
  # There is currently no way to disambiguate "given a block" from "given a
234
244
  # proc as last argument" ... give some leeway in this case
245
+ positional_count = args.size
246
+
247
+ if required_arguments > positional_count
248
+ if requires_keyword_arguments?
249
+ raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}"
250
+ end
251
+
252
+ if (required_arguments - positional_count) == 1 && (last_is_kw_hash || last_is_proc)
253
+ if last_is_kw_hash
254
+ last_is_kw_hash = false
255
+ kw_args = Hash.new
256
+ else
257
+ last_is_proc = false
258
+ end
259
+ positional_count += 1
260
+ elsif (required_arguments - positional_count) == 2 && (last_is_kw_hash && last_is_proc)
261
+ last_is_kw_hash = false
262
+ kw_args = Hash.new
263
+ last_is_proc = false
264
+ positional_count += 2
265
+ else
266
+ raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}"
267
+ end
268
+ end
235
269
 
236
- if required_arguments > args.size
237
- raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{args.size}"
238
- elsif !splat? && (required_arguments + optional_arguments) < args.size
239
- if !args.last.kind_of?(Proc) || (required_arguments + optional_arguments) < args.size - 1
240
- raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{args.size}"
270
+ if !splat? && (required_arguments + optional_arguments) < positional_count
271
+ if !last_is_proc || (required_arguments + optional_arguments) < positional_count - 1
272
+ raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{positional_count}"
241
273
  end
242
274
  end
243
275
 
@@ -1,3 +1,3 @@
1
1
  class FlexMock
2
- VERSION = "2.3.0"
2
+ VERSION = "2.3.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flexmock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Weirich
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-10-11 00:00:00.000000000 Z
12
+ date: 2016-10-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest