minitest 5.16.3 → 5.25.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
-
3
1
  require "rbconfig"
4
2
  require "tempfile"
5
3
  require "stringio"
@@ -29,12 +27,12 @@ module Minitest
29
27
  def self.diff
30
28
  return @diff if defined? @diff
31
29
 
32
- @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ &&
33
- system("diff.exe", __FILE__, __FILE__)) then
30
+ @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ and
31
+ system "diff.exe", __FILE__, __FILE__) then
34
32
  "diff.exe -u"
35
- elsif system("gdiff", __FILE__, __FILE__)
33
+ elsif system "gdiff", __FILE__, __FILE__ then
36
34
  "gdiff -u" # solaris and kin suck
37
- elsif system("diff", __FILE__, __FILE__)
35
+ elsif system "diff", __FILE__, __FILE__ then
38
36
  "diff -u"
39
37
  else
40
38
  nil
@@ -59,16 +57,16 @@ module Minitest
59
57
  def diff exp, act
60
58
  result = nil
61
59
 
62
- expect, butwas = things_to_diff(exp, act)
60
+ expect, butwas = things_to_diff exp, act
63
61
 
64
62
  return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
65
63
  expect
66
64
 
67
- Tempfile.open("expect") do |a|
65
+ Tempfile.open "expect" do |a|
68
66
  a.puts expect
69
67
  a.flush
70
68
 
71
- Tempfile.open("butwas") do |b|
69
+ Tempfile.open "butwas" do |b|
72
70
  b.puts butwas
73
71
  b.flush
74
72
 
@@ -79,10 +77,10 @@ module Minitest
79
77
  if result.empty? then
80
78
  klass = exp.class
81
79
  result = [
82
- "No visible difference in the #{klass}#inspect output.\n",
83
- "You should look at the implementation of #== on ",
84
- "#{klass} or its members.\n",
85
- expect,
80
+ "No visible difference in the #{klass}#inspect output.\n",
81
+ "You should look at the implementation of #== on ",
82
+ "#{klass} or its members.\n",
83
+ expect,
86
84
  ].join
87
85
  end
88
86
  end
@@ -127,20 +125,15 @@ module Minitest
127
125
  # See Minitest::Test.make_my_diffs_pretty!
128
126
 
129
127
  def mu_pp obj
130
- s = obj.inspect
128
+ s = obj.inspect.encode Encoding.default_external
131
129
 
132
- if defined? Encoding then
133
- s = s.encode Encoding.default_external
130
+ return s unless String === obj &&
131
+ (obj.encoding != Encoding.default_external || !obj.valid_encoding?)
134
132
 
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}"
140
- end
141
- end
133
+ enc = "# encoding: #{obj.encoding}"
134
+ val = "# valid: #{obj.valid_encoding?}"
142
135
 
143
- s
136
+ [enc, val, s].join "\n"
144
137
  end
145
138
 
146
139
  ##
@@ -153,8 +146,8 @@ module Minitest
153
146
  str = mu_pp obj
154
147
 
155
148
  # both '\n' & '\\n' (_after_ mu_pp (aka inspect))
156
- single = !!str.match(/(?<!\\|^)\\n/)
157
- double = !!str.match(/(?<=\\|^)\\n/)
149
+ single = str.match?(/(?<!\\|^)\\n/)
150
+ double = str.match?(/(?<=\\|^)\\n/)
158
151
 
159
152
  process =
160
153
  if single ^ double then
@@ -167,9 +160,9 @@ module Minitest
167
160
  :itself # leave it alone
168
161
  end
169
162
 
170
- str.
171
- gsub(/\\?\\n/, &process).
172
- gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX") # anonymize hex values
163
+ str
164
+ .gsub(/\\?\\n/, &process)
165
+ .gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX") # anonymize hex values
173
166
  end
174
167
 
175
168
  ##
@@ -193,11 +186,16 @@ module Minitest
193
186
  # Fails unless +obj+ is empty.
194
187
 
195
188
  def assert_empty obj, msg = nil
196
- msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
189
+ msg = message(msg) { "Expected #{mu_pp obj} to be empty" }
197
190
  assert_respond_to obj, :empty?
198
191
  assert obj.empty?, msg
199
192
  end
200
193
 
194
+ def _where # :nodoc:
195
+ Minitest.filter_backtrace(caller).first
196
+ .split(":in ", 2).first # clean up noise
197
+ end
198
+
201
199
  E = "" # :nodoc:
202
200
 
203
201
  ##
@@ -218,13 +216,10 @@ module Minitest
218
216
  result = assert exp == act, msg
219
217
 
220
218
  if nil == exp then
221
- if Minitest::VERSION =~ /^6/ then
219
+ if Minitest::VERSION >= "6" then
222
220
  refute_nil exp, "Use assert_nil if expecting nil."
223
221
  else
224
- where = Minitest.filter_backtrace(caller).first
225
- where = where.split(/:in /, 2).first # clean up noise
226
-
227
- warn "DEPRECATED: Use assert_nil if expecting nil from #{where}. This will fail in Minitest 6."
222
+ warn "DEPRECATED: Use assert_nil if expecting nil from #{_where}. This will fail in Minitest 6."
228
223
  end
229
224
  end
230
225
 
@@ -258,7 +253,7 @@ module Minitest
258
253
 
259
254
  def assert_includes collection, obj, msg = nil
260
255
  msg = message(msg) {
261
- "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
256
+ "Expected #{mu_pp collection} to include #{mu_pp obj}"
262
257
  }
263
258
  assert_respond_to collection, :include?
264
259
  assert collection.include?(obj), msg
@@ -269,7 +264,7 @@ module Minitest
269
264
 
270
265
  def assert_instance_of cls, obj, msg = nil
271
266
  msg = message(msg) {
272
- "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
267
+ "Expected #{mu_pp obj} to be an instance of #{cls}, not #{obj.class}"
273
268
  }
274
269
 
275
270
  assert obj.instance_of?(cls), msg
@@ -280,7 +275,8 @@ module Minitest
280
275
 
281
276
  def assert_kind_of cls, obj, msg = nil
282
277
  msg = message(msg) {
283
- "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
278
+ "Expected #{mu_pp obj} to be a kind of #{cls}, not #{obj.class}"
279
+ }
284
280
 
285
281
  assert obj.kind_of?(cls), msg
286
282
  end
@@ -290,7 +286,7 @@ module Minitest
290
286
 
291
287
  def assert_match matcher, obj, msg = nil
292
288
  msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
293
- assert_respond_to matcher, :"=~"
289
+ assert_respond_to matcher, :=~
294
290
  matcher = Regexp.new Regexp.escape matcher if String === matcher
295
291
  assert matcher =~ obj, msg
296
292
 
@@ -301,7 +297,7 @@ module Minitest
301
297
  # Fails unless +obj+ is nil
302
298
 
303
299
  def assert_nil obj, msg = nil
304
- msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
300
+ msg = message(msg) { "Expected #{mu_pp obj} to be nil" }
305
301
  assert obj.nil?, msg
306
302
  end
307
303
 
@@ -312,7 +308,7 @@ module Minitest
312
308
 
313
309
  def assert_operator o1, op, o2 = UNDEFINED, msg = nil
314
310
  return assert_predicate o1, op, msg if UNDEFINED == o2
315
- msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
311
+ msg = message(msg) { "Expected #{mu_pp o1} to be #{op} #{mu_pp o2}" }
316
312
  assert o1.__send__(op, o2), msg
317
313
  end
318
314
 
@@ -357,6 +353,32 @@ module Minitest
357
353
  assert File.exist?(path), msg
358
354
  end
359
355
 
356
+ ##
357
+ # For testing with pattern matching (only supported with Ruby 3.0 and later)
358
+ #
359
+ # # pass
360
+ # assert_pattern { [1,2,3] => [Integer, Integer, Integer] }
361
+ #
362
+ # # fail "length mismatch (given 3, expected 1)"
363
+ # assert_pattern { [1,2,3] => [Integer] }
364
+ #
365
+ # The bare <tt>=></tt> pattern will raise a NoMatchingPatternError on failure, which would
366
+ # normally be counted as a test error. This assertion rescues NoMatchingPatternError and
367
+ # generates a test failure. Any other exception will be raised as normal and generate a test
368
+ # error.
369
+
370
+ def assert_pattern
371
+ raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
372
+ flunk "assert_pattern requires a block to capture errors." unless block_given?
373
+
374
+ begin # TODO: remove after ruby 2.6 dropped
375
+ yield
376
+ pass
377
+ rescue NoMatchingPatternError => e
378
+ flunk e.message
379
+ end
380
+ end
381
+
360
382
  ##
361
383
  # For testing with predicates. Eg:
362
384
  #
@@ -367,7 +389,7 @@ module Minitest
367
389
  # str.must_be :empty?
368
390
 
369
391
  def assert_predicate o1, op, msg = nil
370
- msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
392
+ msg = message(msg) { "Expected #{mu_pp o1} to be #{op}" }
371
393
  assert o1.__send__(op), msg
372
394
  end
373
395
 
@@ -412,23 +434,24 @@ module Minitest
412
434
  raise
413
435
  rescue Exception => e
414
436
  flunk proc {
415
- exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
437
+ exception_details(e, "#{msg}#{mu_pp exp} exception expected, not")
416
438
  }
417
439
  end
418
440
 
419
441
  exp = exp.first if exp.size == 1
420
442
 
421
- flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
443
+ flunk "#{msg}#{mu_pp exp} expected but nothing was raised."
422
444
  end
423
445
 
424
446
  ##
425
447
  # Fails unless +obj+ responds to +meth+.
448
+ # include_all defaults to false to match Object#respond_to?
426
449
 
427
- def assert_respond_to obj, meth, msg = nil
450
+ def assert_respond_to obj, meth, msg = nil, include_all: false
428
451
  msg = message(msg) {
429
- "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
452
+ "Expected #{mu_pp obj} (#{obj.class}) to respond to ##{meth}"
430
453
  }
431
- assert obj.respond_to?(meth), msg
454
+ assert obj.respond_to?(meth, include_all), msg
432
455
  end
433
456
 
434
457
  ##
@@ -448,13 +471,12 @@ module Minitest
448
471
  # Fails unless the call returns a true value
449
472
 
450
473
  def assert_send send_ary, m = nil
451
- where = Minitest.filter_backtrace(caller).first
452
- where = where.split(/:in /, 2).first # clean up noise
453
- warn "DEPRECATED: assert_send. From #{where}"
474
+ warn "DEPRECATED: assert_send. From #{_where}"
454
475
 
455
476
  recv, msg, *args = send_ary
456
477
  m = message(m) {
457
- "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
478
+ "Expected #{mu_pp recv}.#{msg}(*#{mu_pp args}) to return true"
479
+ }
458
480
  assert recv.__send__(msg, *args), m
459
481
  end
460
482
 
@@ -473,15 +495,15 @@ module Minitest
473
495
  # Fails unless the block throws +sym+
474
496
 
475
497
  def assert_throws sym, msg = nil
476
- default = "Expected #{mu_pp(sym)} to have been thrown"
498
+ default = "Expected #{mu_pp sym} to have been thrown"
477
499
  caught = true
478
- value = catch(sym) do
500
+ value = catch sym do
479
501
  begin
480
502
  yield
481
503
  rescue ThreadError => e # wtf?!? 1.8 + threads == suck
482
- default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
504
+ default += ", not :#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
483
505
  rescue ArgumentError => e # 1.9 exception
484
- raise e unless e.message.include?("uncaught throw")
506
+ raise e unless e.message.include? "uncaught throw"
485
507
  default += ", not #{e.message.split(/ /).last}"
486
508
  rescue NameError => e # 1.8 exception
487
509
  raise e unless e.name == sym
@@ -580,12 +602,12 @@ module Minitest
580
602
 
581
603
  def exception_details e, msg
582
604
  [
583
- "#{msg}",
584
- "Class: <#{e.class}>",
585
- "Message: <#{e.message.inspect}>",
586
- "---Backtrace---",
587
- "#{Minitest.filter_backtrace(e.backtrace).join("\n")}",
588
- "---------------",
605
+ msg,
606
+ "Class: <#{e.class}>",
607
+ "Message: <#{e.message.inspect}>",
608
+ "---Backtrace---",
609
+ Minitest.filter_backtrace(e.backtrace),
610
+ "---------------",
589
611
  ].join "\n"
590
612
  end
591
613
 
@@ -594,7 +616,7 @@ module Minitest
594
616
  # you to put time-bombs in your tests if you need to keep
595
617
  # something around until a later date lest you forget about it.
596
618
 
597
- def fail_after y,m,d,msg
619
+ def fail_after y, m, d, msg
598
620
  flunk msg if Time.now > Time.local(y, m, d)
599
621
  end
600
622
 
@@ -628,7 +650,7 @@ module Minitest
628
650
  # Fails if +test+ is truthy.
629
651
 
630
652
  def refute test, msg = nil
631
- msg ||= message { "Expected #{mu_pp(test)} to not be truthy" }
653
+ msg ||= message { "Expected #{mu_pp test} to not be truthy" }
632
654
  assert !test, msg
633
655
  end
634
656
 
@@ -636,7 +658,7 @@ module Minitest
636
658
  # Fails if +obj+ is empty.
637
659
 
638
660
  def refute_empty obj, msg = nil
639
- msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
661
+ msg = message(msg) { "Expected #{mu_pp obj} to not be empty" }
640
662
  assert_respond_to obj, :empty?
641
663
  refute obj.empty?, msg
642
664
  end
@@ -648,7 +670,7 @@ module Minitest
648
670
 
649
671
  def refute_equal exp, act, msg = nil
650
672
  msg = message(msg) {
651
- "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
673
+ "Expected #{mu_pp act} to not be equal to #{mu_pp exp}"
652
674
  }
653
675
  refute exp == act, msg
654
676
  end
@@ -679,7 +701,7 @@ module Minitest
679
701
 
680
702
  def refute_includes collection, obj, msg = nil
681
703
  msg = message(msg) {
682
- "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
704
+ "Expected #{mu_pp collection} to not include #{mu_pp obj}"
683
705
  }
684
706
  assert_respond_to collection, :include?
685
707
  refute collection.include?(obj), msg
@@ -690,7 +712,7 @@ module Minitest
690
712
 
691
713
  def refute_instance_of cls, obj, msg = nil
692
714
  msg = message(msg) {
693
- "Expected #{mu_pp(obj)} to not be an instance of #{cls}"
715
+ "Expected #{mu_pp obj} to not be an instance of #{cls}"
694
716
  }
695
717
  refute obj.instance_of?(cls), msg
696
718
  end
@@ -699,7 +721,7 @@ module Minitest
699
721
  # Fails if +obj+ is a kind of +cls+.
700
722
 
701
723
  def refute_kind_of cls, obj, msg = nil
702
- msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
724
+ msg = message(msg) { "Expected #{mu_pp obj} to not be a kind of #{cls}" }
703
725
  refute obj.kind_of?(cls), msg
704
726
  end
705
727
 
@@ -708,7 +730,7 @@ module Minitest
708
730
 
709
731
  def refute_match matcher, obj, msg = nil
710
732
  msg = message(msg) { "Expected #{mu_pp matcher} to not match #{mu_pp obj}" }
711
- assert_respond_to matcher, :"=~"
733
+ assert_respond_to matcher, :=~
712
734
  matcher = Regexp.new Regexp.escape matcher if String === matcher
713
735
  refute matcher =~ obj, msg
714
736
  end
@@ -717,10 +739,34 @@ module Minitest
717
739
  # Fails if +obj+ is nil.
718
740
 
719
741
  def refute_nil obj, msg = nil
720
- msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
742
+ msg = message(msg) { "Expected #{mu_pp obj} to not be nil" }
721
743
  refute obj.nil?, msg
722
744
  end
723
745
 
746
+ ##
747
+ # For testing with pattern matching (only supported with Ruby 3.0 and later)
748
+ #
749
+ # # pass
750
+ # refute_pattern { [1,2,3] => [String] }
751
+ #
752
+ # # fail "NoMatchingPatternError expected, but nothing was raised."
753
+ # refute_pattern { [1,2,3] => [Integer, Integer, Integer] }
754
+ #
755
+ # This assertion expects a NoMatchingPatternError exception, and will fail if none is raised. Any
756
+ # other exceptions will be raised as normal and generate a test error.
757
+
758
+ def refute_pattern
759
+ raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
760
+ flunk "refute_pattern requires a block to capture errors." unless block_given?
761
+
762
+ begin
763
+ yield
764
+ flunk "NoMatchingPatternError expected, but nothing was raised."
765
+ rescue NoMatchingPatternError
766
+ pass
767
+ end
768
+ end
769
+
724
770
  ##
725
771
  # Fails if +o1+ is not +op+ +o2+. Eg:
726
772
  #
@@ -729,7 +775,7 @@ module Minitest
729
775
 
730
776
  def refute_operator o1, op, o2 = UNDEFINED, msg = nil
731
777
  return refute_predicate o1, op, msg if UNDEFINED == o2
732
- msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}" }
778
+ msg = message(msg) { "Expected #{mu_pp o1} to not be #{op} #{mu_pp o2}" }
733
779
  refute o1.__send__(op, o2), msg
734
780
  end
735
781
 
@@ -751,17 +797,18 @@ module Minitest
751
797
  # str.wont_be :empty?
752
798
 
753
799
  def refute_predicate o1, op, msg = nil
754
- msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
800
+ msg = message(msg) { "Expected #{mu_pp o1} to not be #{op}" }
755
801
  refute o1.__send__(op), msg
756
802
  end
757
803
 
758
804
  ##
759
805
  # Fails if +obj+ responds to the message +meth+.
806
+ # include_all defaults to false to match Object#respond_to?
760
807
 
761
- def refute_respond_to obj, meth, msg = nil
762
- msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
808
+ def refute_respond_to obj, meth, msg = nil, include_all: false
809
+ msg = message(msg) { "Expected #{mu_pp obj} to not respond to #{meth}" }
763
810
 
764
- refute obj.respond_to?(meth), msg
811
+ refute obj.respond_to?(meth, include_all), msg
765
812
  end
766
813
 
767
814
  ##
@@ -780,10 +827,10 @@ module Minitest
780
827
  # gets listed at the end of the run but doesn't cause a failure
781
828
  # exit code.
782
829
 
783
- def skip msg = nil, bt = caller
830
+ def skip msg = nil, _ignored = nil
784
831
  msg ||= "Skipped, no message given"
785
832
  @skip = true
786
- raise Minitest::Skip, msg, bt
833
+ raise Minitest::Skip, msg
787
834
  end
788
835
 
789
836
  ##
@@ -792,9 +839,9 @@ module Minitest
792
839
  # date, but still holds you accountable and prevents you from
793
840
  # forgetting it.
794
841
 
795
- def skip_until y,m,d,msg
842
+ def skip_until y, m, d, msg
796
843
  skip msg if Time.now < Time.local(y, m, d)
797
- where = caller.first.rpartition(':in').reject(&:empty?).first
844
+ where = caller(1..1).first.rpartition(":in").reject(&:empty?).first
798
845
  warn "Stale skip_until %p at %s" % [msg, where]
799
846
  end
800
847
 
@@ -1,10 +1,3 @@
1
- begin
2
- require "rubygems"
3
- gem "minitest"
4
- rescue Gem::LoadError
5
- # do nothing
6
- end
7
-
8
1
  require "minitest"
9
2
  require "minitest/spec"
10
3
  require "minitest/mock"
@@ -47,8 +47,6 @@ module Minitest
47
47
 
48
48
  def self.bench_linear min, max, step = 10
49
49
  (min..max).step(step).to_a
50
- rescue LocalJumpError # 1.8.6
51
- r = []; (min..max).step(step) { |n| r << n }; r
52
50
  end
53
51
 
54
52
  ##
@@ -83,7 +81,7 @@ module Minitest
83
81
  def assert_performance validation, &work
84
82
  range = self.class.bench_range
85
83
 
86
- io.print "#{self.name}"
84
+ io.print self.name
87
85
 
88
86
  times = []
89
87
 
@@ -236,7 +234,7 @@ module Minitest
236
234
 
237
235
  def fit_exponential xs, ys
238
236
  n = xs.size
239
- xys = xs.zip(ys)
237
+ xys = xs.zip ys
240
238
  sxlny = sigma(xys) { |x, y| x * Math.log(y) }
241
239
  slny = sigma(xys) { |_, y| Math.log(y) }
242
240
  sx2 = sigma(xys) { |x, _| x * x }
@@ -258,7 +256,7 @@ module Minitest
258
256
 
259
257
  def fit_logarithmic xs, ys
260
258
  n = xs.size
261
- xys = xs.zip(ys)
259
+ xys = xs.zip ys
262
260
  slnx2 = sigma(xys) { |x, _| Math.log(x) ** 2 }
263
261
  slnx = sigma(xys) { |x, _| Math.log(x) }
264
262
  sylnx = sigma(xys) { |x, y| y * Math.log(x) }
@@ -280,7 +278,7 @@ module Minitest
280
278
 
281
279
  def fit_linear xs, ys
282
280
  n = xs.size
283
- xys = xs.zip(ys)
281
+ xys = xs.zip ys
284
282
  sx = sigma xs
285
283
  sy = sigma ys
286
284
  sx2 = sigma(xs) { |x| x ** 2 }
@@ -302,7 +300,7 @@ module Minitest
302
300
 
303
301
  def fit_power xs, ys
304
302
  n = xs.size
305
- xys = xs.zip(ys)
303
+ xys = xs.zip ys
306
304
  slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) }
307
305
  slnx = sigma(xs) { |x | Math.log(x) }
308
306
  slny = sigma(ys) { | y| Math.log(y) }
@@ -323,7 +321,7 @@ module Minitest
323
321
 
324
322
  def sigma enum, &block
325
323
  enum = enum.map(&block) if block
326
- enum.inject { |sum, n| sum + n }
324
+ enum.sum
327
325
  end
328
326
 
329
327
  ##
@@ -419,7 +417,6 @@ module Minitest
419
417
  end
420
418
  end
421
419
 
422
-
423
420
  ##
424
421
  # Create a benchmark that verifies that the performance is logarithmic.
425
422
  #
@@ -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 = ->(a, n, off) { # each_slice_with_offset
15
+ if off.zero? then
16
+ a.each_slice n
17
+ else
18
+ # [ ...off... [...n...] [...n...] ... ]
19
+ front, back = a.take(off), a.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, a1| ### sort by max dist + min offset
33
+ d = a1.each_cons(2).sum { |a2, b| b-a2 }
34
+ [-d, a1.first]
35
+ } # b: [1 3 5] c: [2 4 6]
36
+
37
+ ranges = order
38
+ .map { |k, a1| # [[1..2 3..4] [2..3 4..5]]
39
+ a1
40
+ .each_cons(2)
41
+ .map { |a2, b| a2..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
@@ -0,0 +1,11 @@
1
+ module Minitest
2
+
3
+ module ErrorOnWarning # :nodoc:
4
+ def warn message, category: nil
5
+ message = "[#{category}] #{message}" if category
6
+ raise UnexpectedWarning, message
7
+ end
8
+ end
9
+
10
+ ::Warning.singleton_class.prepend ErrorOnWarning
11
+ end