minitest 5.11.3 → 5.17.0

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.
data/lib/minitest/mock.rb CHANGED
@@ -28,11 +28,19 @@ module Minitest # :nodoc:
28
28
  end
29
29
 
30
30
  overridden_methods.map(&:to_sym).each do |method_id|
31
- define_method method_id do |*args, &b|
31
+ define_method method_id do |*args, **kwargs, &b|
32
32
  if @expected_calls.key? method_id then
33
- method_missing(method_id, *args, &b)
33
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
34
+ method_missing(method_id, *args, &b)
35
+ else
36
+ method_missing(method_id, *args, **kwargs, &b)
37
+ end
34
38
  else
35
- super(*args, &b)
39
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
40
+ super(*args, &b)
41
+ else
42
+ super(*args, **kwargs, &b)
43
+ end
36
44
  end
37
45
  end
38
46
  end
@@ -43,9 +51,11 @@ module Minitest # :nodoc:
43
51
  @actual_calls = Hash.new { |calls, name| calls[name] = [] }
44
52
  end
45
53
 
54
+ @@KW_WARNED = false # :nodoc:
55
+
46
56
  ##
47
- # Expect that method +name+ is called, optionally with +args+ or a
48
- # +blk+, and returns +retval+.
57
+ # Expect that method +name+ is called, optionally with +args+ (and
58
+ # +kwargs+ or a +blk+, and returns +retval+.
49
59
  #
50
60
  # @mock.expect(:meaning_of_life, 42)
51
61
  # @mock.meaning_of_life # => 42
@@ -78,15 +88,31 @@ module Minitest # :nodoc:
78
88
  # @mock.ordinal_increment # => raises MockExpectationError "No more expects available for :ordinal_increment"
79
89
  #
80
90
 
81
- def expect name, retval, args = [], &blk
91
+ def expect name, retval, args = [], **kwargs, &blk
82
92
  name = name.to_sym
83
93
 
84
94
  if block_given?
85
95
  raise ArgumentError, "args ignored when block given" unless args.empty?
96
+ raise ArgumentError, "kwargs ignored when block given" unless kwargs.empty?
86
97
  @expected_calls[name] << { :retval => retval, :block => blk }
87
98
  else
88
99
  raise ArgumentError, "args must be an array" unless Array === args
89
- @expected_calls[name] << { :retval => retval, :args => args }
100
+
101
+ if ENV["MT_KWARGS_HAC\K"] && (Hash === args.last ||
102
+ Hash == args.last) then
103
+ if kwargs.empty? then
104
+ kwargs = args.pop
105
+ else
106
+ unless @@KW_WARNED then
107
+ from = caller.first
108
+ warn "Using MT_KWARGS_HAC\K yet passing kwargs. From #{from}"
109
+ @@KW_WARNED = true
110
+ end
111
+ end
112
+ end
113
+
114
+ @expected_calls[name] <<
115
+ { :retval => retval, :args => args, :kwargs => kwargs }
90
116
  end
91
117
  self
92
118
  end
@@ -94,7 +120,13 @@ module Minitest # :nodoc:
94
120
  def __call name, data # :nodoc:
95
121
  case data
96
122
  when Hash then
97
- "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
123
+ args = data[:args].inspect[1..-2]
124
+ kwargs = data[:kwargs]
125
+ if kwargs && !kwargs.empty? then
126
+ args << ", " unless args.empty?
127
+ args << kwargs.inspect[1..-2]
128
+ end
129
+ "#{name}(#{args}) => #{data[:retval].inspect}"
98
130
  else
99
131
  data.map { |d| __call name, d }.join ", "
100
132
  end
@@ -115,10 +147,14 @@ module Minitest # :nodoc:
115
147
  true
116
148
  end
117
149
 
118
- def method_missing sym, *args, &block # :nodoc:
150
+ def method_missing sym, *args, **kwargs, &block # :nodoc:
119
151
  unless @expected_calls.key?(sym) then
120
152
  if @delegator && @delegator.respond_to?(sym)
121
- return @delegator.public_send(sym, *args, &block)
153
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
154
+ return @delegator.public_send(sym, *args, &block)
155
+ else
156
+ return @delegator.public_send(sym, *args, **kwargs, &block)
157
+ end
122
158
  else
123
159
  raise NoMethodError, "unmocked method %p, expected one of %p" %
124
160
  [sym, @expected_calls.keys.sort_by(&:to_s)]
@@ -129,26 +165,34 @@ module Minitest # :nodoc:
129
165
  expected_call = @expected_calls[sym][index]
130
166
 
131
167
  unless expected_call then
132
- raise MockExpectationError, "No more expects available for %p: %p" %
133
- [sym, args]
168
+ raise MockExpectationError, "No more expects available for %p: %p %p" %
169
+ [sym, args, kwargs]
134
170
  end
135
171
 
136
- expected_args, retval, val_block =
137
- expected_call.values_at(:args, :retval, :block)
172
+ expected_args, expected_kwargs, retval, val_block =
173
+ expected_call.values_at(:args, :kwargs, :retval, :block)
174
+
175
+ expected_kwargs = kwargs.map { |ak, av| [ak, Object] }.to_h if
176
+ Hash == expected_kwargs
138
177
 
139
178
  if val_block then
140
179
  # keep "verify" happy
141
180
  @actual_calls[sym] << expected_call
142
181
 
143
- raise MockExpectationError, "mocked method %p failed block w/ %p" %
144
- [sym, args] unless val_block.call(*args, &block)
182
+ raise MockExpectationError, "mocked method %p failed block w/ %p %p" %
183
+ [sym, args, kwargs] unless val_block.call(*args, **kwargs, &block)
145
184
 
146
185
  return retval
147
186
  end
148
187
 
149
188
  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]
189
+ raise ArgumentError, "mocked method %p expects %d arguments, got %p" %
190
+ [sym, expected_args.size, args]
191
+ end
192
+
193
+ if expected_kwargs.size != kwargs.size then
194
+ raise ArgumentError, "mocked method %p expects %d keyword arguments, got %p" %
195
+ [sym, expected_kwargs.size, kwargs]
152
196
  end
153
197
 
154
198
  zipped_args = expected_args.zip(args)
@@ -157,13 +201,33 @@ module Minitest # :nodoc:
157
201
  }
158
202
 
159
203
  unless fully_matched then
160
- raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
161
- [sym, args]
204
+ fmt = "mocked method %p called with unexpected arguments %p"
205
+ raise MockExpectationError, fmt % [sym, args]
206
+ end
207
+
208
+ unless expected_kwargs.keys.sort == kwargs.keys.sort then
209
+ fmt = "mocked method %p called with unexpected keywords %p vs %p"
210
+ raise MockExpectationError, fmt % [sym, expected_kwargs.keys, kwargs.keys]
211
+ end
212
+
213
+ zipped_kwargs = expected_kwargs.map { |ek, ev|
214
+ av = kwargs[ek]
215
+ [ek, [ev, av]]
216
+ }.to_h
217
+
218
+ fully_matched = zipped_kwargs.all? { |ek, (ev, av)|
219
+ ev === av or ev == av
220
+ }
221
+
222
+ unless fully_matched then
223
+ fmt = "mocked method %p called with unexpected keyword arguments %p vs %p"
224
+ raise MockExpectationError, fmt % [sym, expected_kwargs, kwargs]
162
225
  end
163
226
 
164
227
  @actual_calls[sym] << {
165
228
  :retval => retval,
166
- :args => zipped_args.map! { |mod, a| mod === a ? mod : a },
229
+ :args => zipped_args.map { |e, a| e === a ? e : a },
230
+ :kwargs => zipped_kwargs.map { |k, (e, a)| [k, e === a ? e : a] }.to_h,
167
231
  }
168
232
 
169
233
  retval
@@ -207,31 +271,54 @@ class Object
207
271
  # assert obj_under_test.stale?
208
272
  # end
209
273
  # end
210
- #
274
+ #--
275
+ # NOTE: keyword args in callables are NOT checked for correctness
276
+ # against the existing method. Too many edge cases to be worth it.
211
277
 
212
- def stub name, val_or_callable, *block_args
278
+ def stub name, val_or_callable, *block_args, **block_kwargs, &block
213
279
  new_name = "__minitest_stub__#{name}"
214
280
 
215
281
  metaclass = class << self; self; end
216
282
 
217
283
  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)
284
+ metaclass.send :define_method, name do |*args, **kwargs|
285
+ super(*args, **kwargs)
220
286
  end
221
287
  end
222
288
 
223
289
  metaclass.send :alias_method, new_name, name
224
290
 
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
291
+ if ENV["MT_KWARGS_HAC\K"] then
292
+ metaclass.send :define_method, name do |*args, &blk|
293
+ if val_or_callable.respond_to? :call then
294
+ val_or_callable.call(*args, &blk)
295
+ else
296
+ blk.call(*block_args, **block_kwargs) if blk
297
+ val_or_callable
298
+ end
299
+ end
300
+ else
301
+ metaclass.send :define_method, name do |*args, **kwargs, &blk|
302
+ if val_or_callable.respond_to? :call then
303
+ if kwargs.empty? then # FIX: drop this after 2.7 dead
304
+ val_or_callable.call(*args, &blk)
305
+ else
306
+ val_or_callable.call(*args, **kwargs, &blk)
307
+ end
308
+ else
309
+ if blk then
310
+ if block_kwargs.empty? then # FIX: drop this after 2.7 dead
311
+ blk.call(*block_args)
312
+ else
313
+ blk.call(*block_args, **block_kwargs)
314
+ end
315
+ end
316
+ val_or_callable
317
+ end
231
318
  end
232
319
  end
233
320
 
234
- yield self
321
+ block[self]
235
322
  ensure
236
323
  metaclass.send :undef_method, name
237
324
  metaclass.send :alias_method, name, new_name
@@ -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
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.
@@ -285,21 +296,28 @@ class Minitest::Spec < Minitest::Test
285
296
 
286
297
  module InstanceMethods
287
298
  ##
288
- # Returns a value monad that has all of Expectations methods
289
- # available to it.
299
+ # Takes a value or a block and returns a value monad that has
300
+ # all of Expectations methods available to it.
290
301
  #
291
- # Also aliased to #value and #expect for your aesthetic pleasure:
302
+ # _(1 + 1).must_equal 2
292
303
  #
293
- # _(1 + 1).must_equal 2
294
- # value(1 + 1).must_equal 2
295
- # expect(1 + 1).must_equal 2
304
+ # And for blocks:
305
+ #
306
+ # _ { 1 + "1" }.must_raise TypeError
296
307
  #
297
308
  # This method of expectation-based testing is preferable to
298
309
  # straight-expectation methods (on Object) because it stores its
299
310
  # test context, bypassing our hacky use of thread-local variables.
300
311
  #
301
- # At some point, the methods on Object will be deprecated and then
302
- # removed.
312
+ # NOTE: At some point, the methods on Object will be deprecated
313
+ # and then removed.
314
+ #
315
+ # It is also aliased to #value and #expect for your aesthetic
316
+ # pleasure:
317
+ #
318
+ # _(1 + 1).must_equal 2
319
+ # value(1 + 1).must_equal 2
320
+ # expect(1 + 1).must_equal 2
303
321
 
304
322
  def _ value = nil, &block
305
323
  Minitest::Expectation.new block || value, self
data/lib/minitest/test.rb CHANGED
@@ -18,6 +18,10 @@ module Minitest
18
18
 
19
19
  PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, SystemExit] # :nodoc:
20
20
 
21
+ SETUP_METHODS = %w[ before_setup setup after_setup ] # :nodoc:
22
+
23
+ TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
24
+
21
25
  # :stopdoc:
22
26
  class << self; attr_accessor :io_lock; end
23
27
  self.io_lock = Mutex.new
@@ -67,8 +71,8 @@ module Minitest
67
71
 
68
72
  case self.test_order
69
73
  when :random, :parallel then
70
- max = methods.size
71
- methods.sort.sort_by { rand max }
74
+ srand Minitest.seed
75
+ methods.sort.shuffle
72
76
  when :alpha, :sorted then
73
77
  methods.sort
74
78
  else
@@ -84,8 +88,6 @@ module Minitest
84
88
  :random
85
89
  end
86
90
 
87
- TEARDOWN_METHODS = %w[ before_teardown teardown after_teardown ] # :nodoc:
88
-
89
91
  ##
90
92
  # Runs a single test with setup/teardown hooks.
91
93
 
@@ -93,7 +95,9 @@ module Minitest
93
95
  with_info_handler do
94
96
  time_it do
95
97
  capture_exceptions do
96
- before_setup; setup; after_setup
98
+ SETUP_METHODS.each do |hook|
99
+ self.send hook
100
+ end
97
101
 
98
102
  self.send self.name
99
103
  end
@@ -198,7 +202,39 @@ module Minitest
198
202
  rescue Assertion => e
199
203
  self.failures << e
200
204
  rescue Exception => e
201
- self.failures << UnexpectedError.new(e)
205
+ self.failures << UnexpectedError.new(sanitize_exception e)
206
+ end
207
+
208
+ def sanitize_exception e # :nodoc:
209
+ Marshal.dump e
210
+ e # good: use as-is
211
+ rescue
212
+ neuter_exception e
213
+ end
214
+
215
+ def neuter_exception e # :nodoc:
216
+ bt = e.backtrace
217
+ msg = e.message.dup
218
+
219
+ new_exception e.class, msg, bt # e.class can be a problem...
220
+ rescue
221
+ msg.prepend "Neutered Exception #{e.class}: "
222
+
223
+ new_exception RuntimeError, msg, bt, true # but if this raises, we die
224
+ end
225
+
226
+ def new_exception klass, msg, bt, kill = false # :nodoc:
227
+ ne = klass.new msg
228
+ ne.set_backtrace bt
229
+
230
+ if kill then
231
+ ne.instance_variables.each do |v|
232
+ ne.remove_instance_variable v
233
+ end
234
+ end
235
+
236
+ Marshal.dump ne # can raise TypeError
237
+ ne
202
238
  end
203
239
 
204
240
  def with_info_handler &block # :nodoc: