minitest 5.12.0 → 5.22.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -32,8 +32,6 @@ module Minitest
32
32
  @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ &&
33
33
  system("diff.exe", __FILE__, __FILE__)) then
34
34
  "diff.exe -u"
35
- elsif Minitest::Test.maglev? then
36
- "diff -u"
37
35
  elsif system("gdiff", __FILE__, __FILE__)
38
36
  "gdiff -u" # solaris and kin suck
39
37
  elsif system("diff", __FILE__, __FILE__)
@@ -55,25 +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
- e1, e2 = expect.include?("\n"), expect.include?("\\n")
65
- b1, b2 = butwas.include?("\n"), butwas.include?("\\n")
66
-
67
- need_to_diff =
68
- (e1 ^ e2 ||
69
- b1 ^ b2 ||
70
- expect.size > 30 ||
71
- butwas.size > 30 ||
72
- expect == butwas) &&
73
- Minitest::Assertions.diff
62
+ expect, butwas = things_to_diff(exp, act)
74
63
 
75
64
  return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
76
- need_to_diff
65
+ expect
77
66
 
78
67
  Tempfile.open("expect") do |a|
79
68
  a.puts expect
@@ -102,6 +91,34 @@ module Minitest
102
91
  result
103
92
  end
104
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
+
105
122
  ##
106
123
  # This returns a human-readable version of +obj+. By default
107
124
  # #inspect is called. You can override this to use #pretty_inspect
@@ -136,8 +153,8 @@ module Minitest
136
153
  str = mu_pp obj
137
154
 
138
155
  # both '\n' & '\\n' (_after_ mu_pp (aka inspect))
139
- single = str.match?(/(?<!\\|^)\\n/)
140
- double = str.match?(/(?<=\\|^)\\n/)
156
+ single = !!str.match(/(?<!\\|^)\\n/)
157
+ double = !!str.match(/(?<=\\|^)\\n/)
141
158
 
142
159
  process =
143
160
  if single ^ double then
@@ -181,6 +198,11 @@ module Minitest
181
198
  assert obj.empty?, msg
182
199
  end
183
200
 
201
+ def _where # :nodoc:
202
+ where = Minitest.filter_backtrace(caller).first
203
+ where = where.split(/:in /, 2).first # clean up noise
204
+ end
205
+
184
206
  E = "" # :nodoc:
185
207
 
186
208
  ##
@@ -204,10 +226,7 @@ module Minitest
204
226
  if Minitest::VERSION =~ /^6/ then
205
227
  refute_nil exp, "Use assert_nil if expecting nil."
206
228
  else
207
- where = Minitest.filter_backtrace(caller).first
208
- where = where.split(/:in /, 2).first # clean up noise
209
-
210
- warn "DEPRECATED: Use assert_nil if expecting nil from #{where}. This will fail in Minitest 6."
229
+ warn "DEPRECATED: Use assert_nil if expecting nil from #{_where}. This will fail in Minitest 6."
211
230
  end
212
231
  end
213
232
 
@@ -276,6 +295,8 @@ module Minitest
276
295
  assert_respond_to matcher, :"=~"
277
296
  matcher = Regexp.new Regexp.escape matcher if String === matcher
278
297
  assert matcher =~ obj, msg
298
+
299
+ Regexp.last_match
279
300
  end
280
301
 
281
302
  ##
@@ -324,6 +345,44 @@ module Minitest
324
345
  x = send out_msg, stdout, out, "In stdout" if out_msg
325
346
 
326
347
  (!stdout || x) && (!stderr || y)
348
+ rescue Assertion
349
+ raise
350
+ rescue => e
351
+ raise UnexpectedError, e
352
+ end
353
+
354
+ ##
355
+ # Fails unless +path+ exists.
356
+
357
+ def assert_path_exists path, msg = nil
358
+ msg = message(msg) { "Expected path '#{path}' to exist" }
359
+ assert File.exist?(path), msg
360
+ end
361
+
362
+ ##
363
+ # For testing with pattern matching (only supported with Ruby 3.0 and later)
364
+ #
365
+ # # pass
366
+ # assert_pattern { [1,2,3] => [Integer, Integer, Integer] }
367
+ #
368
+ # # fail "length mismatch (given 3, expected 1)"
369
+ # assert_pattern { [1,2,3] => [Integer] }
370
+ #
371
+ # The bare <tt>=></tt> pattern will raise a NoMatchingPatternError on failure, which would
372
+ # normally be counted as a test error. This assertion rescues NoMatchingPatternError and
373
+ # generates a test failure. Any other exception will be raised as normal and generate a test
374
+ # error.
375
+
376
+ def assert_pattern
377
+ raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
378
+ flunk "assert_pattern requires a block to capture errors." unless block_given?
379
+
380
+ begin # TODO: remove after ruby 2.6 dropped
381
+ yield
382
+ pass
383
+ rescue NoMatchingPatternError => e
384
+ flunk e.message
385
+ end
327
386
  end
328
387
 
329
388
  ##
@@ -346,7 +405,21 @@ module Minitest
346
405
  #
347
406
  # +exp+ takes an optional message on the end to help explain
348
407
  # failures and defaults to StandardError if no exception class is
349
- # passed.
408
+ # passed. Eg:
409
+ #
410
+ # assert_raises(CustomError) { method_with_custom_error }
411
+ #
412
+ # With custom error message:
413
+ #
414
+ # assert_raises(CustomError, 'This should have raised CustomError') { method_with_custom_error }
415
+ #
416
+ # Using the returned object:
417
+ #
418
+ # error = assert_raises(CustomError) do
419
+ # raise CustomError, 'This is really bad'
420
+ # end
421
+ #
422
+ # assert_equal 'This is really bad', error.message
350
423
 
351
424
  def assert_raises *exp
352
425
  flunk "assert_raises requires a block to capture errors." unless
@@ -360,7 +433,7 @@ module Minitest
360
433
  rescue *exp => e
361
434
  pass # count assertion
362
435
  return e
363
- rescue Minitest::Skip, Minitest::Assertion
436
+ rescue Minitest::Assertion # incl Skip & UnexpectedError
364
437
  # don't count assertion
365
438
  raise
366
439
  rescue SignalException, SystemExit
@@ -378,12 +451,13 @@ module Minitest
378
451
 
379
452
  ##
380
453
  # Fails unless +obj+ responds to +meth+.
454
+ # include_all defaults to false to match Object#respond_to?
381
455
 
382
- def assert_respond_to obj, meth, msg = nil
456
+ def assert_respond_to obj, meth, msg = nil, include_all: false
383
457
  msg = message(msg) {
384
458
  "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
385
459
  }
386
- assert obj.respond_to?(meth), msg
460
+ assert obj.respond_to?(meth, include_all), msg
387
461
  end
388
462
 
389
463
  ##
@@ -403,9 +477,7 @@ module Minitest
403
477
  # Fails unless the call returns a true value
404
478
 
405
479
  def assert_send send_ary, m = nil
406
- where = Minitest.filter_backtrace(caller).first
407
- where = where.split(/:in /, 2).first # clean up noise
408
- warn "DEPRECATED: assert_send. From #{where}"
480
+ warn "DEPRECATED: assert_send. From #{_where}"
409
481
 
410
482
  recv, msg, *args = send_ary
411
483
  m = message(m) {
@@ -430,7 +502,7 @@ module Minitest
430
502
  def assert_throws sym, msg = nil
431
503
  default = "Expected #{mu_pp(sym)} to have been thrown"
432
504
  caught = true
433
- catch(sym) do
505
+ value = catch(sym) do
434
506
  begin
435
507
  yield
436
508
  rescue ThreadError => e # wtf?!? 1.8 + threads == suck
@@ -446,6 +518,11 @@ module Minitest
446
518
  end
447
519
 
448
520
  assert caught, message(msg) { default }
521
+ value
522
+ rescue Assertion
523
+ raise
524
+ rescue => e
525
+ raise UnexpectedError, e
449
526
  end
450
527
 
451
528
  ##
@@ -514,10 +591,13 @@ module Minitest
514
591
 
515
592
  return captured_stdout.read, captured_stderr.read
516
593
  ensure
517
- captured_stdout.unlink
518
- captured_stderr.unlink
519
594
  $stdout.reopen orig_stdout
520
595
  $stderr.reopen orig_stderr
596
+
597
+ orig_stdout.close
598
+ orig_stderr.close
599
+ captured_stdout.close!
600
+ captured_stderr.close!
521
601
  end
522
602
  end
523
603
  end
@@ -537,7 +617,16 @@ module Minitest
537
617
  end
538
618
 
539
619
  ##
540
- # Fails with +msg+
620
+ # Fails after a given date (in the local time zone). This allows
621
+ # you to put time-bombs in your tests if you need to keep
622
+ # something around until a later date lest you forget about it.
623
+
624
+ def fail_after y,m,d,msg
625
+ flunk msg if Time.now > Time.local(y, m, d)
626
+ end
627
+
628
+ ##
629
+ # Fails with +msg+.
541
630
 
542
631
  def flunk msg = nil
543
632
  msg ||= "Epic Fail!"
@@ -567,7 +656,7 @@ module Minitest
567
656
 
568
657
  def refute test, msg = nil
569
658
  msg ||= message { "Expected #{mu_pp(test)} to not be truthy" }
570
- not assert !test, msg
659
+ assert !test, msg
571
660
  end
572
661
 
573
662
  ##
@@ -659,6 +748,30 @@ module Minitest
659
748
  refute obj.nil?, msg
660
749
  end
661
750
 
751
+ ##
752
+ # For testing with pattern matching (only supported with Ruby 3.0 and later)
753
+ #
754
+ # # pass
755
+ # refute_pattern { [1,2,3] => [String] }
756
+ #
757
+ # # fail "NoMatchingPatternError expected, but nothing was raised."
758
+ # refute_pattern { [1,2,3] => [Integer, Integer, Integer] }
759
+ #
760
+ # This assertion expects a NoMatchingPatternError exception, and will fail if none is raised. Any
761
+ # other exceptions will be raised as normal and generate a test error.
762
+
763
+ def refute_pattern
764
+ raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
765
+ flunk "refute_pattern requires a block to capture errors." unless block_given?
766
+
767
+ begin
768
+ yield
769
+ flunk("NoMatchingPatternError expected, but nothing was raised.")
770
+ rescue NoMatchingPatternError
771
+ pass
772
+ end
773
+ end
774
+
662
775
  ##
663
776
  # Fails if +o1+ is not +op+ +o2+. Eg:
664
777
  #
@@ -671,6 +784,14 @@ module Minitest
671
784
  refute o1.__send__(op, o2), msg
672
785
  end
673
786
 
787
+ ##
788
+ # Fails if +path+ exists.
789
+
790
+ def refute_path_exists path, msg = nil
791
+ msg = message(msg) { "Expected path '#{path}' to not exist" }
792
+ refute File.exist?(path), msg
793
+ end
794
+
674
795
  ##
675
796
  # For testing with predicates.
676
797
  #
@@ -687,11 +808,12 @@ module Minitest
687
808
 
688
809
  ##
689
810
  # Fails if +obj+ responds to the message +meth+.
811
+ # include_all defaults to false to match Object#respond_to?
690
812
 
691
- def refute_respond_to obj, meth, msg = nil
813
+ def refute_respond_to obj, meth, msg = nil, include_all: false
692
814
  msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
693
815
 
694
- refute obj.respond_to?(meth), msg
816
+ refute obj.respond_to?(meth, include_all), msg
695
817
  end
696
818
 
697
819
  ##
@@ -710,10 +832,22 @@ module Minitest
710
832
  # gets listed at the end of the run but doesn't cause a failure
711
833
  # exit code.
712
834
 
713
- def skip msg = nil, bt = caller
835
+ def skip msg = nil, _ignored = nil
714
836
  msg ||= "Skipped, no message given"
715
837
  @skip = true
716
- raise Minitest::Skip, msg, bt
838
+ raise Minitest::Skip, msg
839
+ end
840
+
841
+ ##
842
+ # Skips the current run until a given date (in the local time
843
+ # zone). This allows you to put some fixes on hold until a later
844
+ # date, but still holds you accountable and prevents you from
845
+ # forgetting it.
846
+
847
+ def skip_until y,m,d,msg
848
+ skip msg if Time.now < Time.local(y, m, d)
849
+ where = caller.first.rpartition(':in').reject(&:empty?).first
850
+ warn "Stale skip_until %p at %s" % [msg, where]
717
851
  end
718
852
 
719
853
  ##
@@ -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 http://www.graphpad.com/curvefit/goodness_of_fit.htm for
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: http://en.wikipedia.org/wiki/Coefficient_of_determination
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: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
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: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
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: http://mathworld.wolfram.com/LeastSquaresFitting.html
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: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
301
+ # See: https://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
302
302
 
303
303
  def fit_power xs, ys
304
304
  n = xs.size
@@ -0,0 +1,94 @@
1
+ module Minitest
2
+ ##
3
+ # Compresses backtraces.
4
+
5
+ module Compress
6
+
7
+ ##
8
+ # Takes a backtrace (array of strings) and compresses repeating
9
+ # cycles in it to make it more readable.
10
+
11
+ def compress orig
12
+ ary = orig
13
+
14
+ eswo = ->(ary, n, off) { # each_slice_with_offset
15
+ if off.zero? then
16
+ ary.each_slice n
17
+ else
18
+ # [ ...off... [...n...] [...n...] ... ]
19
+ front, back = ary.take(off), ary.drop(off)
20
+ [front].chain back.each_slice n
21
+ end
22
+ }
23
+
24
+ 3.times do # maybe don't use loop do here?
25
+ index = ary # [ a b c b c b c d ]
26
+ .size
27
+ .times # 0...size
28
+ .group_by { |i| ary[i] } # { a: [0] b: [1 3 5], c: [2 4 6], d: [7] }
29
+
30
+ order = index
31
+ .reject { |k, v| v.size == 1 } # { b: [1 3 5], c: [2 4 6] }
32
+ .sort_by { |k, ary| ### sort by max dist + min offset
33
+ d = ary.each_cons(2).sum { |a, b| b-a }
34
+ [-d, ary.first]
35
+ } # b: [1 3 5] c: [2 4 6]
36
+
37
+ ranges = order
38
+ .map { |k, ary| # [[1..2 3..4] [2..3 4..5]]
39
+ ary
40
+ .each_cons(2)
41
+ .map { |a, b| a..b-1 }
42
+ }
43
+
44
+ big_ranges = ranges
45
+ .flat_map { |a| # [1..2 3..4 2..3 4..5]
46
+ a.sort_by { |r| [-r.size, r.first] }.first 5
47
+ }
48
+ .first(100)
49
+
50
+ culprits = big_ranges
51
+ .map { |r|
52
+ eswo[ary, r.size, r.begin] # [o1 s1 s1 s2 s2]
53
+ .chunk_while { |a,b| a == b } # [[o1] [s1 s1] [s2 s2]]
54
+ .map { |a| [a.size, a.first] } # [[1 o1] [2 s1] [2 s2]]
55
+ }
56
+ .select { |chunks|
57
+ chunks.any? { |a| a.first > 1 } # compressed anything?
58
+ }
59
+
60
+ min = culprits
61
+ .min_by { |a| a.flatten.size } # most compressed
62
+
63
+ break unless min
64
+
65
+ ary = min.flat_map { |(n, lines)|
66
+ if n > 1 then
67
+ [[n, compress(lines)]] # [o1 [2 s1] [2 s2]]
68
+ else
69
+ lines
70
+ end
71
+ }
72
+ end
73
+
74
+ format = ->(lines) {
75
+ lines.flat_map { |line|
76
+ case line
77
+ when Array then
78
+ n, lines = line
79
+ lines = format[lines]
80
+ [
81
+ " +->> #{n} cycles of #{lines.size} lines:",
82
+ *lines.map { |s| " | #{s}" },
83
+ " +-<<",
84
+ ]
85
+ else
86
+ line
87
+ end
88
+ }
89
+ }
90
+
91
+ format[ary]
92
+ end
93
+ end
94
+ end