generated from actions/typescript-action
-
Notifications
You must be signed in to change notification settings - Fork 26
/
test_logger.jl
97 lines (84 loc) · 2.97 KB
/
test_logger.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
module TestLogger
using Pkg
function parse_file_line(failed_line)
# The bits like `\e[91m\e[1m` are color codes that get printed by `Pkg.test`. We
# match with or without them.
r = r"(\e\[91m\e\[1m)?Test Failed(\e\[22m\e\[39m)? at (\e\[39m\e\[1m)?(?<path>[^\s\e]+)(\e\[22m)?"
m = match(r, failed_line)
m === nothing && return (nothing, nothing)
if m[:path] === nothing
return (nothing, nothing)
else
path_split_results = rsplit(m[:path], ":", limit=2)
if length(path_split_results) == 1
return (m[:path], nothing)
else
path, line_no = path_split_results
# Try to make sure line number is parseable to avoid false positives
line_no = tryparse(Int, line_no) === nothing ? nothing : line_no
return (path, line_no)
end
end
return (nothing, nothing)
end
function readlines_until(f, stream; keep_lines=true, io)
lines = String[]
while true
line = readline(stream; keep=true)
print(io, line)
# with `keep=true`, this should only happen when we're done?
# I think so...
if line == ""
return line, lines
end
if f(line)
return line, lines
else
keep_lines && push!(lines, line)
end
end
end
function has_test_failure(line)
contains(line, "Test Failed") || return false
file, line_no = parse_file_line(line)
return !isnothing(file) && !isnothing(line_no)
end
function build_stream(io)
stream = Base.BufferStream()
t = @async begin
while !eof(stream)
# Iterate through and print until we get to "Test Failed" and can parse it
failed_line, _ = readlines_until(has_test_failure, stream; keep_lines=false, io)
@label found_failed_line
# Parse file and line out
file, line_no = parse_file_line(failed_line)
# Grab everything until the stacktrace, OR we hit another `Test Failed`
stopped_line, msg_lines = readlines_until(stream; io) do line
contains(line, "Stacktrace:") || has_test_failure(line)
end
# If we stopped because we hit a 2nd test failure,
# let's assume somehow the stacktrace didn't show up for the first one.
# Let's continue by trying to find the info for this one, by jumping back.
if has_test_failure(stopped_line)
failed_line = stopped_line
@goto found_failed_line
end
if !isempty(msg_lines)
msg = string("Test Failed\n", chomp(join(msg_lines)))
# Now log it out
@error msg _file=file _line=line_no
end
end
end
return stream, t
end
function test(args...; kwargs...)
stream, t = build_stream(stdout)
Base.errormonitor(t)
return try
Pkg.test(args...; kwargs..., io=stream)
finally
close(stream)
end
end
end # module