Skip to content

Commit

Permalink
+ Added Minitest::Compress#compress and added it to UnexpectedError
Browse files Browse the repository at this point in the history
[git-p4: depot-paths = "//src/minitest/dev/": change = 14004]
  • Loading branch information
zenspider committed Jan 11, 2024
1 parent 43d4efc commit ea319f7
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 1 deletion.
1 change: 1 addition & 0 deletions Manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ lib/minitest.rb
lib/minitest/assertions.rb
lib/minitest/autorun.rb
lib/minitest/benchmark.rb
lib/minitest/compress.rb
lib/minitest/expectations.rb
lib/minitest/hell.rb
lib/minitest/mock.rb
Expand Down
14 changes: 13 additions & 1 deletion lib/minitest.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require "optparse"
require "minitest/parallel"
require "stringio"
require "etc"

require_relative "minitest/parallel"
require_relative "minitest/compress"

##
# :include: README.rdoc

Expand Down Expand Up @@ -985,11 +987,21 @@ def result_label # :nodoc:
# Assertion wrapping an unexpected error that was raised during a run.

class UnexpectedError < Assertion
include Minitest::Compress

# TODO: figure out how to use `cause` instead
attr_accessor :error # :nodoc:

def initialize error # :nodoc:
super "Unexpected exception"

if SystemStackError === error then
bt = error.backtrace
new_bt = compress bt
error = error.exception "#{bt.size} -> #{new_bt.size}"
error.set_backtrace new_bt
end

self.error = error
end

Expand Down
81 changes: 81 additions & 0 deletions lib/minitest/compress.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module Minitest
##
# Compresses backtraces.

module Compress

##
# Takes a backtrace (array of strings) and compresses repeating
# cycles in it to make it more readable.

def compress orig
ary = orig

eswo = ->(ary, n, off) { # each_slice_with_offset
if off.zero? then
ary.each_slice n
else
# [ ...off... [...n...] [...n...] ... ]
front, back = ary.take(off), ary.drop(off)
[front].chain back.each_slice n
end
}

3.times do # maybe don't use loop do here?
index = ary # [ a b c b c b c d ]
.size
.times # 0...size
.group_by { |i| ary[i] } # { a: [0] b: [1 3 5], c: [2 4 6], d: [7] }

order = index
.reject { |k, v| v.size == 1 } # { b: [1 3 5], c: [2 4 6] }
.sort_by { |k, ary| ### sort by max dist + min offset
d = ary.each_cons(2).sum { |a, b| b-a }
[-d, ary.first]
} # b: [1 3 5] c: [2 4 6]

ranges = order
.map { |k, ary| # [[1..2 3..4] [2..3 4..5]]
ary
.each_cons(2)
.map { |a, b| a..b-1 }
}

big_ranges = ranges
.flat_map { |a| # [1..2 3..4 2..3 4..5]
a.sort_by { |r| [-r.size, r.first] }.first 5
}
.first(100)

culprits = big_ranges
.map { |r|
eswo[ary, r.size, r.begin] # [o1 s1 s1 s2 s2]
.chunk_while { |a,b| a == b } # [[o1] [s1 s1] [s2 s2]]
.map { |a| [a.size, a.first] } # [[1 o1] [2 s1] [2 s2]]
}
.select { |chunks|
chunks.any? { |a| a.first > 1 } # compressed anything?
}

min = culprits
.min_by { |a| a.flatten.size } # most compressed

break unless min

ary = min.flat_map { |(n, lines)|
if n > 1 then
[
" +->> #{n} cycles of #{lines.size} lines:",
*lines.map { |s| " | #{s}" },
" +-<<",
]
else
lines
end
}
end

ary
end
end
end
55 changes: 55 additions & 0 deletions test/minitest/test_minitest_reporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ def error_test
@et
end

def system_stack_error_test
unless defined? @sse then

ex = SystemStackError.new

pre = ("a".."c").to_a
mid = ("aa".."ad").to_a * 67
post = ("d".."f").to_a
ary = pre + mid + post

ex.set_backtrace ary

@sse = Minitest::Test.new(:woot)
@sse.failures << Minitest::UnexpectedError.new(ex)
@sse = Minitest::Result.from @sse
end
@sse
end

def fail_test
unless defined? @ft then
@ft = Minitest::Test.new(:woot)
Expand Down Expand Up @@ -314,6 +333,42 @@ def test_report_error
assert_equal exp, normalize_output(io.string)
end

def test_report_error__sse
r.start
r.record system_stack_error_test
r.report

exp = clean <<-EOM
Run options:
# Running:
E
Finished in 0.00
1) Error:
Minitest::Test#woot:
SystemStackError: 274 -> 12
a
b
c
+->> 67 cycles of 4 lines:
| aa
| ab
| ac
| ad
+-<<
d
e
f
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
EOM

assert_equal exp, normalize_output(io.string)
end

def test_report_skipped
r.start
r.record skip_test
Expand Down
95 changes: 95 additions & 0 deletions test/minitest/test_minitest_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1280,3 +1280,98 @@ def test_method
end
end
end

class TestUnexpectedError < Minitest::Test
def assert_compress exp, input
e = Minitest::UnexpectedError.new RuntimeError.new

exp = exp.lines.map(&:chomp) if String === exp
act = e.compress input

assert_equal exp, act
end

ACT1 = %w[ a b c b c b c b c d ]

def test_normal
assert_compress <<~EXP, %w[ a b c b c b c b c d ]
a
+->> 4 cycles of 2 lines:
| b
| c
+-<<
d
EXP
end

def test_normal2
assert_compress <<~EXP, %w[ a b c b c b c b c ]
a
+->> 4 cycles of 2 lines:
| b
| c
+-<<
EXP
end

def test_longer_c_than_b
# the extra c in the front makes the overall length longer sorting it first
assert_compress <<~EXP, %w[ c a b c b c b c b c b d ]
c
a
b
+->> 4 cycles of 2 lines:
| c
| b
+-<<
d
EXP
end

def test_1_line_cycles
assert_compress <<~EXP, %w[ c a b c b c b c b c b b b d ]
c
a
+->> 4 cycles of 2 lines:
| b
| c
+-<<
+->> 3 cycles of 1 lines:
| b
+-<<
d
EXP
end

def test_sanity3
pre = ("aa".."am").to_a
mid = ("a".."z").to_a * 67
post = ("aa".."am").to_a
ary = pre + mid + post

exp = pre +
[" +->> 67 cycles of 26 lines:"] +
("a".."z").map { |s| " | #{s}" } +
[" +-<<"] +
post

assert_compress exp, ary
end

def test_absurd_patterns
skip "NOOOO!!! but I don't care enough right now."

assert_compress <<~EXP, %w[ a b c b c a b c b c a b c ]
+->> 2 cycles of 5 lines:
| a
| +->> 2 cycles of 2 lines:
| | b
| | c
| +-<<
+-<<
a
b
c
EXP
end
end

0 comments on commit ea319f7

Please sign in to comment.