minitest 5.11.3 → 5.25.4

Sign up to get free protection for your applications and to get access to all the features.
data/lib/minitest/mock.rb CHANGED
@@ -8,9 +8,9 @@ module Minitest # :nodoc:
8
8
  # All mock objects are an instance of Mock
9
9
 
10
10
  class Mock
11
- alias :__respond_to? :respond_to?
11
+ alias __respond_to? respond_to?
12
12
 
13
- overridden_methods = %w[
13
+ overridden_methods = %i[
14
14
  ===
15
15
  class
16
16
  inspect
@@ -23,18 +23,31 @@ module Minitest # :nodoc:
23
23
  to_s
24
24
  ]
25
25
 
26
+ overridden_methods << :singleton_method_added if defined?(::DEBUGGER__)
27
+
26
28
  instance_methods.each do |m|
27
- undef_method m unless overridden_methods.include?(m.to_s) || m =~ /^__/
29
+ undef_method m unless overridden_methods.include?(m) || m =~ /^__/
28
30
  end
29
31
 
30
32
  overridden_methods.map(&:to_sym).each do |method_id|
31
- define_method method_id do |*args, &b|
33
+ old_w, $-w = $-w, nil
34
+ define_method method_id do |*args, **kwargs, &b|
32
35
  if @expected_calls.key? method_id then
33
- method_missing(method_id, *args, &b)
36
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
37
+ method_missing(method_id, *args, &b)
38
+ else
39
+ method_missing(method_id, *args, **kwargs, &b)
40
+ end
34
41
  else
35
- super(*args, &b)
42
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
43
+ super(*args, &b)
44
+ else
45
+ super(*args, **kwargs, &b)
46
+ end
36
47
  end
37
48
  end
49
+ ensure
50
+ $-w = old_w
38
51
  end
39
52
 
40
53
  def initialize delegator = nil # :nodoc:
@@ -43,9 +56,11 @@ module Minitest # :nodoc:
43
56
  @actual_calls = Hash.new { |calls, name| calls[name] = [] }
44
57
  end
45
58
 
59
+ @@KW_WARNED = false # :nodoc:
60
+
46
61
  ##
47
- # Expect that method +name+ is called, optionally with +args+ or a
48
- # +blk+, and returns +retval+.
62
+ # Expect that method +name+ is called, optionally with +args+ (and
63
+ # +kwargs+ or a +blk+), and returns +retval+.
49
64
  #
50
65
  # @mock.expect(:meaning_of_life, 42)
51
66
  # @mock.meaning_of_life # => 42
@@ -78,15 +93,31 @@ module Minitest # :nodoc:
78
93
  # @mock.ordinal_increment # => raises MockExpectationError "No more expects available for :ordinal_increment"
79
94
  #
80
95
 
81
- def expect name, retval, args = [], &blk
96
+ def expect name, retval, args = [], **kwargs, &blk
82
97
  name = name.to_sym
83
98
 
84
- if block_given?
99
+ if blk then
85
100
  raise ArgumentError, "args ignored when block given" unless args.empty?
101
+ raise ArgumentError, "kwargs ignored when block given" unless kwargs.empty?
86
102
  @expected_calls[name] << { :retval => retval, :block => blk }
87
103
  else
88
104
  raise ArgumentError, "args must be an array" unless Array === args
89
- @expected_calls[name] << { :retval => retval, :args => args }
105
+
106
+ if ENV["MT_KWARGS_HAC\K"] && (Hash === args.last ||
107
+ Hash == args.last) then
108
+ if kwargs.empty? then
109
+ kwargs = args.pop
110
+ else
111
+ unless @@KW_WARNED then
112
+ from = caller(1..1).first
113
+ warn "Using MT_KWARGS_HAC\K yet passing kwargs. From #{from}"
114
+ @@KW_WARNED = true
115
+ end
116
+ end
117
+ end
118
+
119
+ @expected_calls[name] <<
120
+ { :retval => retval, :args => args, :kwargs => kwargs }
90
121
  end
91
122
  self
92
123
  end
@@ -94,7 +125,13 @@ module Minitest # :nodoc:
94
125
  def __call name, data # :nodoc:
95
126
  case data
96
127
  when Hash then
97
- "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
128
+ args = data[:args].inspect[1..-2]
129
+ kwargs = data[:kwargs]
130
+ if kwargs && !kwargs.empty? then
131
+ args << ", " unless args.empty?
132
+ args << kwargs.inspect[1..-2]
133
+ end
134
+ "#{name}(#{args}) => #{data[:retval].inspect}"
98
135
  else
99
136
  data.map { |d| __call name, d }.join ", "
100
137
  end
@@ -107,18 +144,22 @@ module Minitest # :nodoc:
107
144
 
108
145
  def verify
109
146
  @expected_calls.each do |name, expected|
110
- actual = @actual_calls.fetch(name, nil)
111
- raise MockExpectationError, "expected #{__call name, expected[0]}" unless actual
112
- raise MockExpectationError, "expected #{__call name, expected[actual.size]}, got [#{__call name, actual}]" if
147
+ actual = @actual_calls.fetch name, nil # defaults to []
148
+ raise MockExpectationError, "Expected #{__call name, expected[0]}" unless actual
149
+ raise MockExpectationError, "Expected #{__call name, expected[actual.size]}, got [#{__call name, actual}]" if
113
150
  actual.size < expected.size
114
151
  end
115
152
  true
116
153
  end
117
154
 
118
- def method_missing sym, *args, &block # :nodoc:
119
- unless @expected_calls.key?(sym) then
155
+ def method_missing sym, *args, **kwargs, &block # :nodoc:
156
+ unless @expected_calls.key? sym then
120
157
  if @delegator && @delegator.respond_to?(sym)
121
- return @delegator.public_send(sym, *args, &block)
158
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
159
+ return @delegator.public_send(sym, *args, &block)
160
+ else
161
+ return @delegator.public_send(sym, *args, **kwargs, &block)
162
+ end
122
163
  else
123
164
  raise NoMethodError, "unmocked method %p, expected one of %p" %
124
165
  [sym, @expected_calls.keys.sort_by(&:to_s)]
@@ -129,41 +170,69 @@ module Minitest # :nodoc:
129
170
  expected_call = @expected_calls[sym][index]
130
171
 
131
172
  unless expected_call then
132
- raise MockExpectationError, "No more expects available for %p: %p" %
133
- [sym, args]
173
+ raise MockExpectationError, "No more expects available for %p: %p %p" %
174
+ [sym, args, kwargs]
134
175
  end
135
176
 
136
- expected_args, retval, val_block =
137
- expected_call.values_at(:args, :retval, :block)
177
+ expected_args, expected_kwargs, retval, val_block =
178
+ expected_call.values_at :args, :kwargs, :retval, :block
179
+
180
+ expected_kwargs = kwargs.to_h { |ak, av| [ak, Object] } if
181
+ Hash == expected_kwargs
138
182
 
139
183
  if val_block then
140
184
  # keep "verify" happy
141
185
  @actual_calls[sym] << expected_call
142
186
 
143
- raise MockExpectationError, "mocked method %p failed block w/ %p" %
144
- [sym, args] unless val_block.call(*args, &block)
187
+ raise MockExpectationError, "mocked method %p failed block w/ %p %p" %
188
+ [sym, args, kwargs] unless val_block.call(*args, **kwargs, &block)
145
189
 
146
190
  return retval
147
191
  end
148
192
 
149
193
  if expected_args.size != args.size then
150
- raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
151
- [sym, expected_args.size, args.size]
194
+ raise ArgumentError, "mocked method %p expects %d arguments, got %p" %
195
+ [sym, expected_args.size, args]
196
+ end
197
+
198
+ if expected_kwargs.size != kwargs.size then
199
+ raise ArgumentError, "mocked method %p expects %d keyword arguments, got %p" %
200
+ [sym, expected_kwargs.size, kwargs]
152
201
  end
153
202
 
154
- zipped_args = expected_args.zip(args)
203
+ zipped_args = expected_args.zip args
155
204
  fully_matched = zipped_args.all? { |mod, a|
156
205
  mod === a or mod == a
157
206
  }
158
207
 
159
208
  unless fully_matched then
160
- raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
161
- [sym, args]
209
+ fmt = "mocked method %p called with unexpected arguments %p"
210
+ raise MockExpectationError, fmt % [sym, args]
211
+ end
212
+
213
+ unless expected_kwargs.keys.sort == kwargs.keys.sort then
214
+ fmt = "mocked method %p called with unexpected keywords %p vs %p"
215
+ raise MockExpectationError, fmt % [sym, expected_kwargs.keys, kwargs.keys]
216
+ end
217
+
218
+ zipped_kwargs = expected_kwargs.to_h { |ek, ev|
219
+ av = kwargs[ek]
220
+ [ek, [ev, av]]
221
+ }
222
+
223
+ fully_matched = zipped_kwargs.all? { |ek, (ev, av)|
224
+ ev === av or ev == av
225
+ }
226
+
227
+ unless fully_matched then
228
+ fmt = "mocked method %p called with unexpected keyword arguments %p vs %p"
229
+ raise MockExpectationError, fmt % [sym, expected_kwargs, kwargs]
162
230
  end
163
231
 
164
232
  @actual_calls[sym] << {
165
233
  :retval => retval,
166
- :args => zipped_args.map! { |mod, a| mod === a ? mod : a },
234
+ :args => zipped_args.map { |e, a| e === a ? e : a },
235
+ :kwargs => zipped_kwargs.to_h { |k, (e, a)| [k, e === a ? e : a] },
167
236
  }
168
237
 
169
238
  retval
@@ -172,20 +241,35 @@ module Minitest # :nodoc:
172
241
  def respond_to? sym, include_private = false # :nodoc:
173
242
  return true if @expected_calls.key? sym.to_sym
174
243
  return true if @delegator && @delegator.respond_to?(sym, include_private)
175
- __respond_to?(sym, include_private)
244
+ __respond_to? sym, include_private
176
245
  end
177
246
  end
178
247
  end
179
248
 
180
249
  module Minitest::Assertions
181
250
  ##
182
- # Assert that the mock verifies correctly.
251
+ # Assert that the mock verifies correctly and fail if not.
183
252
 
184
- def assert_mock mock
253
+ def assert_mock mock, msg = nil
185
254
  assert mock.verify
255
+ rescue MockExpectationError => e
256
+ msg = message(msg) { e.message }
257
+ flunk msg
186
258
  end
187
259
  end
188
260
 
261
+ module Minitest::Expectations
262
+ ##
263
+ # See Minitest::Assertions#assert_mock.
264
+ #
265
+ # _(collection).must_verify
266
+ #
267
+ # :method: must_verify
268
+
269
+ infect_an_assertion :assert_mock, :must_verify, :unary if
270
+ defined?(infect_an_assertion)
271
+ end
272
+
189
273
  ##
190
274
  # Object extensions for Minitest::Mock.
191
275
 
@@ -207,31 +291,54 @@ class Object
207
291
  # assert obj_under_test.stale?
208
292
  # end
209
293
  # end
210
- #
294
+ #--
295
+ # NOTE: keyword args in callables are NOT checked for correctness
296
+ # against the existing method. Too many edge cases to be worth it.
211
297
 
212
- def stub name, val_or_callable, *block_args
298
+ def stub name, val_or_callable, *block_args, **block_kwargs, &block
213
299
  new_name = "__minitest_stub__#{name}"
214
300
 
215
301
  metaclass = class << self; self; end
216
302
 
217
303
  if respond_to? name and not methods.map(&:to_s).include? name.to_s then
218
- metaclass.send :define_method, name do |*args|
219
- super(*args)
304
+ metaclass.send :define_method, name do |*args, **kwargs|
305
+ super(*args, **kwargs)
220
306
  end
221
307
  end
222
308
 
223
309
  metaclass.send :alias_method, new_name, name
224
310
 
225
- metaclass.send :define_method, name do |*args, &blk|
226
- if val_or_callable.respond_to? :call then
227
- val_or_callable.call(*args, &blk)
228
- else
229
- blk.call(*block_args) if blk
230
- val_or_callable
311
+ if ENV["MT_KWARGS_HAC\K"] then
312
+ metaclass.send :define_method, name do |*args, &blk|
313
+ if val_or_callable.respond_to? :call then
314
+ val_or_callable.call(*args, &blk)
315
+ else
316
+ blk.call(*block_args, **block_kwargs) if blk
317
+ val_or_callable
318
+ end
319
+ end
320
+ else
321
+ metaclass.send :define_method, name do |*args, **kwargs, &blk|
322
+ if val_or_callable.respond_to? :call then
323
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
324
+ val_or_callable.call(*args, &blk)
325
+ else
326
+ val_or_callable.call(*args, **kwargs, &blk)
327
+ end
328
+ else
329
+ if blk then
330
+ if block_kwargs.empty? then # FIX: drop this after 2.7 dead
331
+ blk.call(*block_args)
332
+ else
333
+ blk.call(*block_args, **block_kwargs)
334
+ end
335
+ end
336
+ val_or_callable
337
+ end
231
338
  end
232
339
  end
233
340
 
234
- yield self
341
+ block[self]
235
342
  ensure
236
343
  metaclass.send :undef_method, name
237
344
  metaclass.send :alias_method, name, new_name
@@ -1,5 +1,5 @@
1
1
  module Minitest
2
- module Parallel #:nodoc:
2
+ module Parallel # :nodoc:
3
3
 
4
4
  ##
5
5
  # The engine used to run multiple tests in parallel.
@@ -16,7 +16,7 @@ module Minitest
16
16
 
17
17
  def initialize size
18
18
  @size = size
19
- @queue = Queue.new
19
+ @queue = Thread::Queue.new
20
20
  @pool = nil
21
21
  end
22
22
 
@@ -24,10 +24,10 @@ module Minitest
24
24
  # Start the executor
25
25
 
26
26
  def start
27
- @pool = size.times.map {
28
- Thread.new(@queue) do |queue|
27
+ @pool = Array.new(size) {
28
+ Thread.new @queue do |queue|
29
29
  Thread.current.abort_on_exception = true
30
- while (job = queue.pop)
30
+ while job = queue.pop do
31
31
  klass, method, reporter = job
32
32
  reporter.synchronize { reporter.prerecord klass, method }
33
33
  result = Minitest.run_one_method klass, method
@@ -8,13 +8,13 @@ module Minitest
8
8
  end
9
9
 
10
10
  def self.plugin_pride_init options # :nodoc:
11
- if PrideIO.pride? then
12
- klass = ENV["TERM"] =~ /^xterm|-256color$/ ? PrideLOL : PrideIO
13
- io = klass.new options[:io]
11
+ return unless PrideIO.pride?
14
12
 
15
- self.reporter.reporters.grep(Minitest::Reporter).each do |rep|
16
- rep.io = io if rep.io.tty?
17
- end
13
+ klass = ENV["TERM"] =~ /^xterm|-(?:256color|direct)$/ ? PrideLOL : PrideIO
14
+ io = klass.new options[:io]
15
+
16
+ self.reporter.reporters.grep(Minitest::Reporter).each do |rep|
17
+ rep.io = io if rep.io.tty?
18
18
  end
19
19
  end
20
20
 
@@ -48,7 +48,7 @@ module Minitest
48
48
  def initialize io # :nodoc:
49
49
  @io = io
50
50
  # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm
51
- # also reference http://en.wikipedia.org/wiki/ANSI_escape_code
51
+ # also reference https://en.wikipedia.org/wiki/ANSI_escape_code
52
52
  @colors ||= (31..36).to_a
53
53
  @size = @colors.size
54
54
  @index = 0
@@ -59,12 +59,10 @@ module Minitest
59
59
 
60
60
  def print o
61
61
  case o
62
- when "." then
62
+ when ".", "S" then
63
63
  io.print pride o
64
64
  when "E", "F" then
65
65
  io.print "#{ESC}41m#{ESC}37m#{o}#{NND}"
66
- when "S" then
67
- io.print pride o
68
66
  else
69
67
  io.print o
70
68
  end
@@ -72,11 +70,9 @@ module Minitest
72
70
 
73
71
  def puts *o # :nodoc:
74
72
  o.map! { |s|
75
- s.to_s.sub(/Finished/) {
73
+ s.to_s.sub("Finished") {
76
74
  @index = 0
77
- "Fabulous run".split(//).map { |c|
78
- pride(c)
79
- }.join
75
+ "Fabulous run".chars.map { |c| pride(c) }.join
80
76
  }
81
77
  }
82
78
 
@@ -113,19 +109,16 @@ module Minitest
113
109
  #
114
110
  # plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3)
115
111
 
116
- # 6 has wide pretty gradients. 3 == lolcat, about half the width
117
- @colors = (0...(6 * 7)).map { |n|
118
- n *= 1.0 / 6
112
+ @colors = Array.new(6 * 7) { |n|
113
+ n *= 1.0 / 3
119
114
  r = (3 * Math.sin(n ) + 3).to_i
120
- g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
121
- b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
122
-
123
- # Then we take rgb and encode them in a single number using base 6.
124
- # For some mysterious reason, we add 16... to clear the bottom 4 bits?
125
- # Yes... they're ugly.
115
+ g = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
116
+ b = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
126
117
 
118
+ # Then we take rgb and encode them in a single number using
119
+ # base 6, shifted by 16 for the base 16 ansi colors.
127
120
  36 * r + 6 * g + b + 16
128
- }
121
+ }.rotate(4) # puts "red" first
129
122
 
130
123
  super
131
124
  end
data/lib/minitest/spec.rb CHANGED
@@ -4,16 +4,26 @@ class Module # :nodoc:
4
4
  def infect_an_assertion meth, new_name, dont_flip = false # :nodoc:
5
5
  block = dont_flip == :block
6
6
  dont_flip = false if block
7
+ target_obj = block ? "_{obj.method}" : "_(obj)"
8
+
9
+ # https://eregon.me/blog/2021/02/13/correct-delegation-in-ruby-2-27-3.html
10
+ # Drop this when we can drop ruby 2.6 (aka after rails 6.1 EOL, ~2024-06)
11
+ kw_extra = "ruby2_keywords %p" % [new_name] if respond_to? :ruby2_keywords, true
7
12
 
8
13
  # warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
9
14
  self.class_eval <<-EOM, __FILE__, __LINE__ + 1
10
15
  def #{new_name} *args
16
+ where = Minitest.filter_backtrace(caller).first
17
+ where = where.split(/:in /, 2).first # clean up noise
18
+ Kernel.warn "DEPRECATED: global use of #{new_name} from #\{where}. Use #{target_obj}.#{new_name} instead. This will fail in Minitest 6."
11
19
  Minitest::Expectation.new(self, Minitest::Spec.current).#{new_name}(*args)
12
20
  end
21
+ #{kw_extra}
13
22
  EOM
14
23
 
15
24
  Minitest::Expectation.class_eval <<-EOM, __FILE__, __LINE__ + 1
16
25
  def #{new_name} *args
26
+ raise "Calling ##{new_name} outside of test." unless ctx
17
27
  case
18
28
  when #{!!dont_flip} then
19
29
  ctx.#{meth}(target, *args)
@@ -23,6 +33,7 @@ class Module # :nodoc:
23
33
  ctx.#{meth}(args.first, target, *args[1..-1])
24
34
  end
25
35
  end
36
+ #{kw_extra}
26
37
  EOM
27
38
  end
28
39
  end
@@ -61,7 +72,7 @@ module Kernel
61
72
  #
62
73
  # For some suggestions on how to improve your specs, try:
63
74
  #
64
- # http://betterspecs.org
75
+ # https://betterspecs.org
65
76
  #
66
77
  # but do note that several items there are debatable or specific to
67
78
  # rspec.
@@ -70,14 +81,15 @@ module Kernel
70
81
 
71
82
  def describe desc, *additional_desc, &block # :doc:
72
83
  stack = Minitest::Spec.describe_stack
73
- name = [stack.last, desc, *additional_desc].compact.join("::")
74
- sclas = stack.last || if Class === self && kind_of?(Minitest::Spec::DSL) then
75
- self
76
- else
77
- Minitest::Spec.spec_type desc, *additional_desc
78
- end
84
+ is_spec_class = Class === self && kind_of?(Minitest::Spec::DSL)
85
+ name = [stack.last, desc, *additional_desc]
86
+ name.prepend self if stack.empty? && is_spec_class
87
+ sclas =
88
+ stack.last \
89
+ || (is_spec_class && self) \
90
+ || Minitest::Spec.spec_type(desc, *additional_desc)
79
91
 
80
- cls = sclas.create name, desc
92
+ cls = sclas.create name.compact.join("::"), desc
81
93
 
82
94
  stack.push cls
83
95
  cls.class_eval(&block)
@@ -238,7 +250,7 @@ class Minitest::Spec < Minitest::Test
238
250
  pre, post = "let '#{name}' cannot ", ". Please use another name."
239
251
  methods = Minitest::Spec.instance_methods.map(&:to_s) - %w[subject]
240
252
  raise ArgumentError, "#{pre}begin with 'test'#{post}" if
241
- name =~ /\Atest/
253
+ name.start_with? "test"
242
254
  raise ArgumentError, "#{pre}override a method in Minitest::Spec#{post}" if
243
255
  methods.include? name
244
256
 
@@ -257,7 +269,7 @@ class Minitest::Spec < Minitest::Test
257
269
  end
258
270
 
259
271
  def create name, desc # :nodoc:
260
- cls = Class.new(self) do
272
+ cls = Class.new self do
261
273
  @name = name
262
274
  @desc = desc
263
275
 
@@ -278,28 +290,35 @@ class Minitest::Spec < Minitest::Test
278
290
  end
279
291
 
280
292
  attr_reader :desc # :nodoc:
281
- alias :specify :it
293
+ alias specify it
282
294
 
283
295
  ##
284
296
  # Rdoc... why are you so dumb?
285
297
 
286
298
  module InstanceMethods
287
299
  ##
288
- # Returns a value monad that has all of Expectations methods
289
- # available to it.
300
+ # Takes a value or a block and returns a value monad that has
301
+ # all of Expectations methods available to it.
290
302
  #
291
- # Also aliased to #value and #expect for your aesthetic pleasure:
303
+ # _(1 + 1).must_equal 2
292
304
  #
293
- # _(1 + 1).must_equal 2
294
- # value(1 + 1).must_equal 2
295
- # expect(1 + 1).must_equal 2
305
+ # And for blocks:
306
+ #
307
+ # _ { 1 + "1" }.must_raise TypeError
296
308
  #
297
309
  # This method of expectation-based testing is preferable to
298
310
  # straight-expectation methods (on Object) because it stores its
299
311
  # test context, bypassing our hacky use of thread-local variables.
300
312
  #
301
- # At some point, the methods on Object will be deprecated and then
302
- # removed.
313
+ # NOTE: At some point, the methods on Object will be deprecated
314
+ # and then removed.
315
+ #
316
+ # It is also aliased to #value and #expect for your aesthetic
317
+ # pleasure:
318
+ #
319
+ # _(1 + 1).must_equal 2
320
+ # value(1 + 1).must_equal 2
321
+ # expect(1 + 1).must_equal 2
303
322
 
304
323
  def _ value = nil, &block
305
324
  Minitest::Expectation.new block || value, self