Skip to content

Commit 434396f

Browse files
CI: improve the content of results.json
1 parent beb928b commit 434396f

File tree

1 file changed

+75
-50
lines changed

1 file changed

+75
-50
lines changed

test/buildkitetestjson.jl

+75-50
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,20 @@ function json_repr(io::IO, val::AbstractVector; indent::Int=0)
3333
print(io, '[')
3434
for i in eachindex(val)
3535
print(io, '\n', ' '^(indent + 2))
36-
json_repr(io, val[i]; indent=indent+2)
36+
json_repr(io, val[i]; indent=indent + 2)
3737
i == lastindex(val) || print(io, ',')
3838
end
3939
print(io, '\n', ' '^indent, ']')
4040
end
4141
function json_repr(io::IO, val::Dict; indent::Int=0)
4242
print(io, '{')
43+
len = length(val)
4344
for (i, (k, v)) in enumerate(pairs(val))
4445
print(io, '\n', ' '^(indent + 2))
4546
json_repr(io, string(k))
4647
print(io, ": ")
47-
json_repr(io, v; indent=indent+2)
48-
i === length(val) || print(io, ',')
48+
json_repr(io, v; indent=indent + 2)
49+
i == len || print(io, ',')
4950
end
5051
print(io, '\n', ' '^indent, '}')
5152
end
@@ -54,81 +55,105 @@ json_repr(io::IO, val::Any; indent::Int=0) = json_repr(io, string(val))
5455
# Test result processing
5556

5657
function result_dict(testset::Test.DefaultTestSet, prefix::String="")
57-
Dict{String, Any}(
58+
scope = if isempty(prefix)
59+
testset.description == "Overall" ? "" : testset.description
60+
else
61+
join((prefix, testset.description), '/')
62+
end
63+
data = Dict{String,Any}(
5864
"id" => Base.UUID(rand(UInt128)),
59-
"scope" => join((prefix, testset.description), '/'),
65+
"scope" => scope,
66+
"tags" => Dict{String,String}(
67+
"job_label" => get(ENV, "BUILDKITE_LABEL", "unknown"),
68+
"job_id" => get(ENV, "BUILDKITE_JOB_ID", "unknown"),
69+
"job_group" => get(ENV, "BUILDKITE_GROUP_LABEL", "unknown"),
70+
"os" => string(Sys.KERNEL),
71+
"arch" => string(Sys.ARCH),
72+
"julia_version" => string(VERSION),
73+
"testset" => testset.description,
74+
),
6075
"history" => if !isnothing(testset.time_end)
61-
Dict{String, Any}(
76+
Dict{String,Any}(
6277
"start_at" => testset.time_start,
6378
"end_at" => testset.time_end,
6479
"duration" => testset.time_end - testset.time_start)
6580
else
66-
Dict{String, Any}("start_at" => testset.time_start, "duration" => 0.0)
81+
Dict{String,Any}("start_at" => testset.time_start, "duration" => 0.0)
6782
end)
83+
return data
6884
end
6985

70-
function result_dict(result::Test.Result)
71-
file, line = if !hasproperty(result, :source) || isnothing(result.source)
72-
"unknown", 0
73-
else
74-
something(result.source.file, "unknown"), result.source.line
75-
end
76-
status = if result isa Test.Pass && result.test_type === :skipped
86+
# Test paths on runners are often in deep directories, so just make them contain enough information
87+
# to be able to identify the file. Also convert Windows-style paths to Unix-style paths so tests can
88+
# be grouped by file.
89+
function generalize_file_paths(path::AbstractString)
90+
pathsep = Sys.iswindows() ? '\\' : '/'
91+
path = replace(path,
92+
string(Sys.STDLIB, pathsep) => "",
93+
string(normpath(Sys.BUILD_ROOT_PATH), pathsep) => "",
94+
string(dirname(Sys.BINDIR), pathsep) => ""
95+
)
96+
return Sys.iswindows() ? replace(path, "\\" => "/") : path
97+
end
98+
99+
# passed, failed, skipped, or unknown
100+
function get_status(result)
101+
if result isa Test.Pass && result.test_type === :skipped
77102
"skipped"
103+
elseif result isa Test.Broken
104+
"skipped" # buildkite don't have a "broken" status
78105
elseif result isa Test.Pass
79106
"passed"
80107
elseif result isa Test.Fail || result isa Test.Error
81108
"failed"
82109
else
83110
"unknown"
84111
end
85-
data = Dict{String, Any}(
86-
"name" => "$(result.test_type): $(result.orig_expr)",
112+
end
113+
114+
function result_dict(result::Test.Result)
115+
file, line = if !hasproperty(result, :source) || isnothing(result.source)
116+
"unknown", 0
117+
else
118+
something(result.source.file, "unknown"), result.source.line
119+
end
120+
file = generalize_file_paths(string(file))
121+
122+
status = get_status(result)
123+
124+
result_show = sprint(show, result; context=:color => false)
125+
firstline = split(result_show, '\n')[1]
126+
primary_reason = split(firstline, " at ")[1]
127+
128+
data = Dict{String,Any}(
129+
"name" => "$(primary_reason). Expression: $(result.orig_expr)",
87130
"location" => string(file, ':', line),
88131
"file_name" => file,
89132
"result" => status)
90-
add_failure_info!(data, result)
91-
end
92133

93-
function add_failure_info!(data::Dict{String, Any}, result::Test.Result)
94-
if result isa Test.Fail
95-
data["failure_reason"] = if result.test_type === :test && !isnothing(result.data)
96-
"Evaluated: $(result.data)"
97-
elseif result.test_type === :test_throws_nothing
98-
"No exception thrown"
99-
elseif result.test_type === :test_throws_wrong
100-
"Wrong exception type thrown"
101-
else
102-
"unknown"
103-
end
104-
elseif result isa Test.Error
105-
data["failure_reason"] = if result.test_type === :test_error
106-
if occursin("\nStacktrace:\n", result.backtrace)
107-
err, trace = split(result.backtrace, "\nStacktrace:\n", limit=2)
108-
data["failure_expanded"] =
109-
[Dict{String,Any}("expanded" => split(err, '\n'),
110-
"backtrace" => split(trace, '\n'))]
111-
end
112-
"Exception (unexpectedly) thrown during test"
113-
elseif result.test_type === :test_nonbool
114-
"Expected the expression to evaluate to a Bool, not a $(typeof(result.data))"
115-
elseif result.test_type === :test_unbroken
116-
"Expected this test to be broken, but it passed"
134+
job_label = replace(get(ENV, "BUILDKITE_LABEL", "job label not found"), r":\w+:\s*" => "")
135+
if result isa Test.Fail || result isa Test.Error
136+
data["failure_reason"] = generalize_file_paths(firstline) * " | $job_label"
137+
err_trace = split(result_show, "\nStacktrace:\n", limit=2)
138+
if length(err_trace) == 2
139+
err, trace = err_trace
140+
data["failure_expanded"] = [Dict{String,Any}("expanded" => split(err, '\n'), "backtrace" => split(trace, '\n'))]
117141
else
118-
"unknown"
142+
data["failure_expanded"] = [Dict{String,Any}("expanded" => split(result_show, '\n'), "backtrace" => [])]
119143
end
120144
end
121-
data
145+
return data
122146
end
123147

124-
function collect_results!(results::Vector{Dict{String, Any}}, testset::Test.DefaultTestSet, prefix::String="")
148+
function collect_results!(results::Vector{Dict{String,Any}}, testset::Test.DefaultTestSet, prefix::String="")
125149
common_data = result_dict(testset, prefix)
126150
result_offset = length(results) + 1
127-
result_counts = Dict{Tuple{String, String}, Int}()
151+
result_counts = Dict{Tuple{String,String},Int}()
152+
get_rid(rdata) = (rdata["location"], rdata["result"])
128153
for (i, result) in enumerate(testset.results)
129154
if result isa Test.Result
130155
rdata = result_dict(result)
131-
rid = (rdata["location"], rdata["result"])
156+
rid = get_rid(rdata)
132157
if haskey(result_counts, rid)
133158
result_counts[rid] += 1
134159
else
@@ -142,17 +167,17 @@ function collect_results!(results::Vector{Dict{String, Any}}, testset::Test.Defa
142167
# Modify names to hold `result_counts`
143168
for i in result_offset:length(results)
144169
result = results[i]
145-
rid = (result["location"], result["result"])
170+
rid = get_rid(result)
146171
if get(result_counts, rid, 0) > 1
147172
result["name"] = replace(result["name"], r"^([^:]):" =>
148173
SubstitutionString("\\1 (x$(result_counts[rid])):"))
149174
end
150175
end
151-
results
176+
return results
152177
end
153178

154179
function write_testset_json_files(dir::String, testset::Test.DefaultTestSet)
155-
data = Dict{String, Any}[]
180+
data = Dict{String,Any}[]
156181
collect_results!(data, testset)
157182
files = String[]
158183
# Buildkite is limited to 5000 results per file https://buildkite.com/docs/test-analytics/importing-json

0 commit comments

Comments
 (0)