minitest 5.10.3 → 5.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/History.rdoc +267 -4
- data/Manifest.txt +3 -0
- data/README.rdoc +123 -22
- data/Rakefile +5 -16
- data/lib/hoe/minitest.rb +0 -4
- data/lib/minitest/assertions.rb +197 -32
- data/lib/minitest/benchmark.rb +39 -8
- data/lib/minitest/expectations.rb +72 -35
- data/lib/minitest/mock.rb +118 -34
- data/lib/minitest/parallel.rb +1 -1
- data/lib/minitest/pride_plugin.rb +1 -1
- data/lib/minitest/spec.rb +27 -9
- data/lib/minitest/test.rb +38 -66
- data/lib/minitest/test_task.rb +305 -0
- data/lib/minitest/unit.rb +5 -8
- data/lib/minitest.rb +271 -52
- data/test/minitest/metametameta.rb +44 -9
- data/test/minitest/test_minitest_assertions.rb +1701 -0
- data/test/minitest/test_minitest_benchmark.rb +2 -2
- data/test/minitest/test_minitest_mock.rb +648 -14
- data/test/minitest/test_minitest_reporter.rb +46 -21
- data/test/minitest/test_minitest_spec.rb +317 -156
- data/test/minitest/test_minitest_test.rb +308 -1146
- data/test/minitest/test_minitest_test_task.rb +46 -0
- data.tar.gz.sig +1 -2
- metadata +36 -24
- metadata.gz.sig +0 -0
data/lib/hoe/minitest.rb
CHANGED
data/lib/minitest/assertions.rb
CHANGED
@@ -27,20 +27,18 @@ module Minitest
|
|
27
27
|
# figure out what diff to use.
|
28
28
|
|
29
29
|
def self.diff
|
30
|
+
return @diff if defined? @diff
|
31
|
+
|
30
32
|
@diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ &&
|
31
33
|
system("diff.exe", __FILE__, __FILE__)) then
|
32
34
|
"diff.exe -u"
|
33
|
-
elsif Minitest::Test.maglev? then
|
34
|
-
"diff -u"
|
35
35
|
elsif system("gdiff", __FILE__, __FILE__)
|
36
36
|
"gdiff -u" # solaris and kin suck
|
37
37
|
elsif system("diff", __FILE__, __FILE__)
|
38
38
|
"diff -u"
|
39
39
|
else
|
40
40
|
nil
|
41
|
-
end
|
42
|
-
|
43
|
-
@diff
|
41
|
+
end
|
44
42
|
end
|
45
43
|
|
46
44
|
##
|
@@ -55,22 +53,16 @@ module Minitest
|
|
55
53
|
# diff command or if it doesn't make sense to diff the output
|
56
54
|
# (single line, short output), then it simply returns a basic
|
57
55
|
# comparison between the two.
|
56
|
+
#
|
57
|
+
# See +things_to_diff+ for more info.
|
58
58
|
|
59
59
|
def diff exp, act
|
60
|
-
expect = mu_pp_for_diff exp
|
61
|
-
butwas = mu_pp_for_diff act
|
62
60
|
result = nil
|
63
61
|
|
64
|
-
|
65
|
-
(expect.include?("\n") ||
|
66
|
-
butwas.include?("\n") ||
|
67
|
-
expect.size > 30 ||
|
68
|
-
butwas.size > 30 ||
|
69
|
-
expect == butwas) &&
|
70
|
-
Minitest::Assertions.diff
|
62
|
+
expect, butwas = things_to_diff(exp, act)
|
71
63
|
|
72
64
|
return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
|
73
|
-
|
65
|
+
expect
|
74
66
|
|
75
67
|
Tempfile.open("expect") do |a|
|
76
68
|
a.puts expect
|
@@ -99,10 +91,40 @@ module Minitest
|
|
99
91
|
result
|
100
92
|
end
|
101
93
|
|
94
|
+
##
|
95
|
+
# Returns things to diff [expect, butwas], or [nil, nil] if nothing to diff.
|
96
|
+
#
|
97
|
+
# Criterion:
|
98
|
+
#
|
99
|
+
# 1. Strings include newlines or escaped newlines, but not both.
|
100
|
+
# 2. or: String lengths are > 30 characters.
|
101
|
+
# 3. or: Strings are equal to each other (but maybe different encodings?).
|
102
|
+
# 4. and: we found a diff executable.
|
103
|
+
|
104
|
+
def things_to_diff exp, act
|
105
|
+
expect = mu_pp_for_diff exp
|
106
|
+
butwas = mu_pp_for_diff act
|
107
|
+
|
108
|
+
e1, e2 = expect.include?("\n"), expect.include?("\\n")
|
109
|
+
b1, b2 = butwas.include?("\n"), butwas.include?("\\n")
|
110
|
+
|
111
|
+
need_to_diff =
|
112
|
+
(e1 ^ e2 ||
|
113
|
+
b1 ^ b2 ||
|
114
|
+
expect.size > 30 ||
|
115
|
+
butwas.size > 30 ||
|
116
|
+
expect == butwas) &&
|
117
|
+
Minitest::Assertions.diff
|
118
|
+
|
119
|
+
need_to_diff && [expect, butwas]
|
120
|
+
end
|
121
|
+
|
102
122
|
##
|
103
123
|
# This returns a human-readable version of +obj+. By default
|
104
|
-
# #inspect is called. You can override this to use #
|
124
|
+
# #inspect is called. You can override this to use #pretty_inspect
|
105
125
|
# if you want.
|
126
|
+
#
|
127
|
+
# See Minitest::Test.make_my_diffs_pretty!
|
106
128
|
|
107
129
|
def mu_pp obj
|
108
130
|
s = obj.inspect
|
@@ -110,8 +132,11 @@ module Minitest
|
|
110
132
|
if defined? Encoding then
|
111
133
|
s = s.encode Encoding.default_external
|
112
134
|
|
113
|
-
if String === obj && obj.encoding != Encoding.default_external
|
114
|
-
|
135
|
+
if String === obj && (obj.encoding != Encoding.default_external ||
|
136
|
+
!obj.valid_encoding?) then
|
137
|
+
enc = "# encoding: #{obj.encoding}"
|
138
|
+
val = "# valid: #{obj.valid_encoding?}"
|
139
|
+
s = "#{enc}\n#{val}\n#{s}"
|
115
140
|
end
|
116
141
|
end
|
117
142
|
|
@@ -119,13 +144,32 @@ module Minitest
|
|
119
144
|
end
|
120
145
|
|
121
146
|
##
|
122
|
-
# This returns a diff-able human-readable version of +obj+.
|
123
|
-
# differs from the regular mu_pp because it expands escaped
|
124
|
-
# newlines and makes hex-values
|
147
|
+
# This returns a diff-able more human-readable version of +obj+.
|
148
|
+
# This differs from the regular mu_pp because it expands escaped
|
149
|
+
# newlines and makes hex-values (like object_ids) generic. This
|
125
150
|
# uses mu_pp to do the first pass and then cleans it up.
|
126
151
|
|
127
152
|
def mu_pp_for_diff obj
|
128
|
-
mu_pp
|
153
|
+
str = mu_pp obj
|
154
|
+
|
155
|
+
# both '\n' & '\\n' (_after_ mu_pp (aka inspect))
|
156
|
+
single = !!str.match(/(?<!\\|^)\\n/)
|
157
|
+
double = !!str.match(/(?<=\\|^)\\n/)
|
158
|
+
|
159
|
+
process =
|
160
|
+
if single ^ double then
|
161
|
+
if single then
|
162
|
+
lambda { |s| s == "\\n" ? "\n" : s } # unescape
|
163
|
+
else
|
164
|
+
lambda { |s| s == "\\\\n" ? "\\n\n" : s } # unescape a bit, add nls
|
165
|
+
end
|
166
|
+
else
|
167
|
+
:itself # leave it alone
|
168
|
+
end
|
169
|
+
|
170
|
+
str.
|
171
|
+
gsub(/\\?\\n/, &process).
|
172
|
+
gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX") # anonymize hex values
|
129
173
|
end
|
130
174
|
|
131
175
|
##
|
@@ -173,7 +217,7 @@ module Minitest
|
|
173
217
|
msg = message(msg, E) { diff exp, act }
|
174
218
|
result = assert exp == act, msg
|
175
219
|
|
176
|
-
if
|
220
|
+
if nil == exp then
|
177
221
|
if Minitest::VERSION =~ /^6/ then
|
178
222
|
refute_nil exp, "Use assert_nil if expecting nil."
|
179
223
|
else
|
@@ -205,8 +249,8 @@ module Minitest
|
|
205
249
|
# For comparing Floats. Fails unless +exp+ and +act+ have a relative
|
206
250
|
# error less than +epsilon+.
|
207
251
|
|
208
|
-
def assert_in_epsilon
|
209
|
-
assert_in_delta
|
252
|
+
def assert_in_epsilon exp, act, epsilon = 0.001, msg = nil
|
253
|
+
assert_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg
|
210
254
|
end
|
211
255
|
|
212
256
|
##
|
@@ -249,6 +293,8 @@ module Minitest
|
|
249
293
|
assert_respond_to matcher, :"=~"
|
250
294
|
matcher = Regexp.new Regexp.escape matcher if String === matcher
|
251
295
|
assert matcher =~ obj, msg
|
296
|
+
|
297
|
+
Regexp.last_match
|
252
298
|
end
|
253
299
|
|
254
300
|
##
|
@@ -283,6 +329,9 @@ module Minitest
|
|
283
329
|
# See also: #assert_silent
|
284
330
|
|
285
331
|
def assert_output stdout = nil, stderr = nil
|
332
|
+
flunk "assert_output requires a block to capture output." unless
|
333
|
+
block_given?
|
334
|
+
|
286
335
|
out, err = capture_io do
|
287
336
|
yield
|
288
337
|
end
|
@@ -294,6 +343,44 @@ module Minitest
|
|
294
343
|
x = send out_msg, stdout, out, "In stdout" if out_msg
|
295
344
|
|
296
345
|
(!stdout || x) && (!stderr || y)
|
346
|
+
rescue Assertion
|
347
|
+
raise
|
348
|
+
rescue => e
|
349
|
+
raise UnexpectedError, e
|
350
|
+
end
|
351
|
+
|
352
|
+
##
|
353
|
+
# Fails unless +path+ exists.
|
354
|
+
|
355
|
+
def assert_path_exists path, msg = nil
|
356
|
+
msg = message(msg) { "Expected path '#{path}' to exist" }
|
357
|
+
assert File.exist?(path), msg
|
358
|
+
end
|
359
|
+
|
360
|
+
##
|
361
|
+
# For testing with pattern matching (only supported with Ruby 3.0 and later)
|
362
|
+
#
|
363
|
+
# # pass
|
364
|
+
# assert_pattern { [1,2,3] => [Integer, Integer, Integer] }
|
365
|
+
#
|
366
|
+
# # fail "length mismatch (given 3, expected 1)"
|
367
|
+
# assert_pattern { [1,2,3] => [Integer] }
|
368
|
+
#
|
369
|
+
# The bare <tt>=></tt> pattern will raise a NoMatchingPatternError on failure, which would
|
370
|
+
# normally be counted as a test error. This assertion rescues NoMatchingPatternError and
|
371
|
+
# generates a test failure. Any other exception will be raised as normal and generate a test
|
372
|
+
# error.
|
373
|
+
|
374
|
+
def assert_pattern
|
375
|
+
raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
|
376
|
+
flunk "assert_pattern requires a block to capture errors." unless block_given?
|
377
|
+
|
378
|
+
begin # TODO: remove after ruby 2.6 dropped
|
379
|
+
yield
|
380
|
+
pass
|
381
|
+
rescue NoMatchingPatternError => e
|
382
|
+
flunk e.message
|
383
|
+
end
|
297
384
|
end
|
298
385
|
|
299
386
|
##
|
@@ -316,9 +403,26 @@ module Minitest
|
|
316
403
|
#
|
317
404
|
# +exp+ takes an optional message on the end to help explain
|
318
405
|
# failures and defaults to StandardError if no exception class is
|
319
|
-
# passed.
|
406
|
+
# passed. Eg:
|
407
|
+
#
|
408
|
+
# assert_raises(CustomError) { method_with_custom_error }
|
409
|
+
#
|
410
|
+
# With custom error message:
|
411
|
+
#
|
412
|
+
# assert_raises(CustomError, 'This should have raised CustomError') { method_with_custom_error }
|
413
|
+
#
|
414
|
+
# Using the returned object:
|
415
|
+
#
|
416
|
+
# error = assert_raises(CustomError) do
|
417
|
+
# raise CustomError, 'This is really bad'
|
418
|
+
# end
|
419
|
+
#
|
420
|
+
# assert_equal 'This is really bad', error.message
|
320
421
|
|
321
422
|
def assert_raises *exp
|
423
|
+
flunk "assert_raises requires a block to capture errors." unless
|
424
|
+
block_given?
|
425
|
+
|
322
426
|
msg = "#{exp.pop}.\n" if String === exp.last
|
323
427
|
exp << StandardError if exp.empty?
|
324
428
|
|
@@ -327,7 +431,7 @@ module Minitest
|
|
327
431
|
rescue *exp => e
|
328
432
|
pass # count assertion
|
329
433
|
return e
|
330
|
-
rescue Minitest::Skip
|
434
|
+
rescue Minitest::Assertion # incl Skip & UnexpectedError
|
331
435
|
# don't count assertion
|
332
436
|
raise
|
333
437
|
rescue SignalException, SystemExit
|
@@ -397,7 +501,7 @@ module Minitest
|
|
397
501
|
def assert_throws sym, msg = nil
|
398
502
|
default = "Expected #{mu_pp(sym)} to have been thrown"
|
399
503
|
caught = true
|
400
|
-
catch(sym) do
|
504
|
+
value = catch(sym) do
|
401
505
|
begin
|
402
506
|
yield
|
403
507
|
rescue ThreadError => e # wtf?!? 1.8 + threads == suck
|
@@ -413,6 +517,11 @@ module Minitest
|
|
413
517
|
end
|
414
518
|
|
415
519
|
assert caught, message(msg) { default }
|
520
|
+
value
|
521
|
+
rescue Assertion
|
522
|
+
raise
|
523
|
+
rescue => e
|
524
|
+
raise UnexpectedError, e
|
416
525
|
end
|
417
526
|
|
418
527
|
##
|
@@ -481,10 +590,13 @@ module Minitest
|
|
481
590
|
|
482
591
|
return captured_stdout.read, captured_stderr.read
|
483
592
|
ensure
|
484
|
-
captured_stdout.unlink
|
485
|
-
captured_stderr.unlink
|
486
593
|
$stdout.reopen orig_stdout
|
487
594
|
$stderr.reopen orig_stderr
|
595
|
+
|
596
|
+
orig_stdout.close
|
597
|
+
orig_stderr.close
|
598
|
+
captured_stdout.close!
|
599
|
+
captured_stderr.close!
|
488
600
|
end
|
489
601
|
end
|
490
602
|
end
|
@@ -504,7 +616,16 @@ module Minitest
|
|
504
616
|
end
|
505
617
|
|
506
618
|
##
|
507
|
-
# Fails
|
619
|
+
# Fails after a given date (in the local time zone). This allows
|
620
|
+
# you to put time-bombs in your tests if you need to keep
|
621
|
+
# something around until a later date lest you forget about it.
|
622
|
+
|
623
|
+
def fail_after y,m,d,msg
|
624
|
+
flunk msg if Time.now > Time.local(y, m, d)
|
625
|
+
end
|
626
|
+
|
627
|
+
##
|
628
|
+
# Fails with +msg+.
|
508
629
|
|
509
630
|
def flunk msg = nil
|
510
631
|
msg ||= "Epic Fail!"
|
@@ -534,7 +655,7 @@ module Minitest
|
|
534
655
|
|
535
656
|
def refute test, msg = nil
|
536
657
|
msg ||= message { "Expected #{mu_pp(test)} to not be truthy" }
|
537
|
-
|
658
|
+
assert !test, msg
|
538
659
|
end
|
539
660
|
|
540
661
|
##
|
@@ -626,6 +747,30 @@ module Minitest
|
|
626
747
|
refute obj.nil?, msg
|
627
748
|
end
|
628
749
|
|
750
|
+
##
|
751
|
+
# For testing with pattern matching (only supported with Ruby 3.0 and later)
|
752
|
+
#
|
753
|
+
# # pass
|
754
|
+
# refute_pattern { [1,2,3] => [String] }
|
755
|
+
#
|
756
|
+
# # fail "NoMatchingPatternError expected, but nothing was raised."
|
757
|
+
# refute_pattern { [1,2,3] => [Integer, Integer, Integer] }
|
758
|
+
#
|
759
|
+
# This assertion expects a NoMatchingPatternError exception, and will fail if none is raised. Any
|
760
|
+
# other exceptions will be raised as normal and generate a test error.
|
761
|
+
|
762
|
+
def refute_pattern
|
763
|
+
raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
|
764
|
+
flunk "refute_pattern requires a block to capture errors." unless block_given?
|
765
|
+
|
766
|
+
begin
|
767
|
+
yield
|
768
|
+
flunk("NoMatchingPatternError expected, but nothing was raised.")
|
769
|
+
rescue NoMatchingPatternError
|
770
|
+
pass
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
629
774
|
##
|
630
775
|
# Fails if +o1+ is not +op+ +o2+. Eg:
|
631
776
|
#
|
@@ -638,6 +783,14 @@ module Minitest
|
|
638
783
|
refute o1.__send__(op, o2), msg
|
639
784
|
end
|
640
785
|
|
786
|
+
##
|
787
|
+
# Fails if +path+ exists.
|
788
|
+
|
789
|
+
def refute_path_exists path, msg = nil
|
790
|
+
msg = message(msg) { "Expected path '#{path}' to not exist" }
|
791
|
+
refute File.exist?(path), msg
|
792
|
+
end
|
793
|
+
|
641
794
|
##
|
642
795
|
# For testing with predicates.
|
643
796
|
#
|
@@ -683,6 +836,18 @@ module Minitest
|
|
683
836
|
raise Minitest::Skip, msg, bt
|
684
837
|
end
|
685
838
|
|
839
|
+
##
|
840
|
+
# Skips the current run until a given date (in the local time
|
841
|
+
# zone). This allows you to put some fixes on hold until a later
|
842
|
+
# date, but still holds you accountable and prevents you from
|
843
|
+
# forgetting it.
|
844
|
+
|
845
|
+
def skip_until y,m,d,msg
|
846
|
+
skip msg if Time.now < Time.local(y, m, d)
|
847
|
+
where = caller.first.rpartition(':in').reject(&:empty?).first
|
848
|
+
warn "Stale skip_until %p at %s" % [msg, where]
|
849
|
+
end
|
850
|
+
|
686
851
|
##
|
687
852
|
# Was this testcase skipped? Meant for #teardown.
|
688
853
|
|
data/lib/minitest/benchmark.rb
CHANGED
@@ -109,8 +109,8 @@ module Minitest
|
|
109
109
|
# is applied against the slope itself. As such, you probably want
|
110
110
|
# to tighten it from the default.
|
111
111
|
#
|
112
|
-
# See
|
113
|
-
# more details.
|
112
|
+
# See https://www.graphpad.com/guides/prism/8/curve-fitting/reg_intepretingnonlinr2.htm
|
113
|
+
# for more details.
|
114
114
|
#
|
115
115
|
# Fit is calculated by #fit_linear.
|
116
116
|
#
|
@@ -217,7 +217,7 @@ module Minitest
|
|
217
217
|
##
|
218
218
|
# Takes an array of x/y pairs and calculates the general R^2 value.
|
219
219
|
#
|
220
|
-
# See:
|
220
|
+
# See: https://en.wikipedia.org/wiki/Coefficient_of_determination
|
221
221
|
|
222
222
|
def fit_error xys
|
223
223
|
y_bar = sigma(xys) { |_, y| y } / xys.size.to_f
|
@@ -232,7 +232,7 @@ module Minitest
|
|
232
232
|
#
|
233
233
|
# Takes x and y values and returns [a, b, r^2].
|
234
234
|
#
|
235
|
-
# See:
|
235
|
+
# See: https://mathworld.wolfram.com/LeastSquaresFittingExponential.html
|
236
236
|
|
237
237
|
def fit_exponential xs, ys
|
238
238
|
n = xs.size
|
@@ -254,7 +254,7 @@ module Minitest
|
|
254
254
|
#
|
255
255
|
# Takes x and y values and returns [a, b, r^2].
|
256
256
|
#
|
257
|
-
# See:
|
257
|
+
# See: https://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
|
258
258
|
|
259
259
|
def fit_logarithmic xs, ys
|
260
260
|
n = xs.size
|
@@ -276,7 +276,7 @@ module Minitest
|
|
276
276
|
#
|
277
277
|
# Takes x and y values and returns [a, b, r^2].
|
278
278
|
#
|
279
|
-
# See:
|
279
|
+
# See: https://mathworld.wolfram.com/LeastSquaresFitting.html
|
280
280
|
|
281
281
|
def fit_linear xs, ys
|
282
282
|
n = xs.size
|
@@ -298,7 +298,7 @@ module Minitest
|
|
298
298
|
#
|
299
299
|
# Takes x and y values and returns [a, b, r^2].
|
300
300
|
#
|
301
|
-
# See:
|
301
|
+
# See: https://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
|
302
302
|
|
303
303
|
def fit_power xs, ys
|
304
304
|
n = xs.size
|
@@ -318,7 +318,7 @@ module Minitest
|
|
318
318
|
# Enumerates over +enum+ mapping +block+ if given, returning the
|
319
319
|
# sum of the result. Eg:
|
320
320
|
#
|
321
|
-
# sigma([1, 2, 3]) # => 1 + 2 + 3 =>
|
321
|
+
# sigma([1, 2, 3]) # => 1 + 2 + 3 => 6
|
322
322
|
# sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
|
323
323
|
|
324
324
|
def sigma enum, &block
|
@@ -418,6 +418,37 @@ module Minitest
|
|
418
418
|
assert_performance_exponential threshold, &work
|
419
419
|
end
|
420
420
|
end
|
421
|
+
|
422
|
+
|
423
|
+
##
|
424
|
+
# Create a benchmark that verifies that the performance is logarithmic.
|
425
|
+
#
|
426
|
+
# describe "my class Bench" do
|
427
|
+
# bench_performance_logarithmic "algorithm" do |n|
|
428
|
+
# @obj.algorithm(n)
|
429
|
+
# end
|
430
|
+
# end
|
431
|
+
|
432
|
+
def self.bench_performance_logarithmic name, threshold = 0.99, &work
|
433
|
+
bench name do
|
434
|
+
assert_performance_logarithmic threshold, &work
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
##
|
439
|
+
# Create a benchmark that verifies that the performance is power.
|
440
|
+
#
|
441
|
+
# describe "my class Bench" do
|
442
|
+
# bench_performance_power "algorithm" do |n|
|
443
|
+
# @obj.algorithm(n)
|
444
|
+
# end
|
445
|
+
# end
|
446
|
+
|
447
|
+
def self.bench_performance_power name, threshold = 0.99, &work
|
448
|
+
bench name do
|
449
|
+
assert_performance_power threshold, &work
|
450
|
+
end
|
451
|
+
end
|
421
452
|
end
|
422
453
|
|
423
454
|
Minitest::Spec.register_spec_type(/Bench(mark)?$/, Minitest::BenchSpec)
|