@@ -33,19 +33,20 @@ function json_repr(io::IO, val::AbstractVector; indent::Int=0)
33
33
print (io, ' [' )
34
34
for i in eachindex (val)
35
35
print (io, ' \n ' , ' ' ^ (indent + 2 ))
36
- json_repr (io, val[i]; indent= indent+ 2 )
36
+ json_repr (io, val[i]; indent= indent + 2 )
37
37
i == lastindex (val) || print (io, ' ,' )
38
38
end
39
39
print (io, ' \n ' , ' ' ^ indent, ' ]' )
40
40
end
41
41
function json_repr (io:: IO , val:: Dict ; indent:: Int = 0 )
42
42
print (io, ' {' )
43
+ len = length (val)
43
44
for (i, (k, v)) in enumerate (pairs (val))
44
45
print (io, ' \n ' , ' ' ^ (indent + 2 ))
45
46
json_repr (io, string (k))
46
47
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, ' ,' )
49
50
end
50
51
print (io, ' \n ' , ' ' ^ indent, ' }' )
51
52
end
@@ -54,81 +55,105 @@ json_repr(io::IO, val::Any; indent::Int=0) = json_repr(io, string(val))
54
55
# Test result processing
55
56
56
57
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} (
58
64
" 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
+ ),
60
75
" history" => if ! isnothing (testset. time_end)
61
- Dict {String, Any} (
76
+ Dict {String,Any} (
62
77
" start_at" => testset. time_start,
63
78
" end_at" => testset. time_end,
64
79
" duration" => testset. time_end - testset. time_start)
65
80
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 )
67
82
end )
83
+ return data
68
84
end
69
85
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
77
102
" skipped"
103
+ elseif result isa Test. Broken
104
+ " skipped" # buildkite don't have a "broken" status
78
105
elseif result isa Test. Pass
79
106
" passed"
80
107
elseif result isa Test. Fail || result isa Test. Error
81
108
" failed"
82
109
else
83
110
" unknown"
84
111
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) " ,
87
130
" location" => string (file, ' :' , line),
88
131
" file_name" => file,
89
132
" result" => status)
90
- add_failure_info! (data, result)
91
- end
92
133
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 (" \n Stacktrace:\n " , result. backtrace)
107
- err, trace = split (result. backtrace, " \n Stacktrace:\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, " \n Stacktrace:\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 ' ))]
117
141
else
118
- " unknown "
142
+ data[ " failure_expanded " ] = [ Dict {String,Any} ( " expanded " => split (result_show, ' \n ' ), " backtrace " => [])]
119
143
end
120
144
end
121
- data
145
+ return data
122
146
end
123
147
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 = " " )
125
149
common_data = result_dict (testset, prefix)
126
150
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" ])
128
153
for (i, result) in enumerate (testset. results)
129
154
if result isa Test. Result
130
155
rdata = result_dict (result)
131
- rid = (rdata[ " location " ], rdata[ " result " ] )
156
+ rid = get_rid (rdata)
132
157
if haskey (result_counts, rid)
133
158
result_counts[rid] += 1
134
159
else
@@ -142,17 +167,17 @@ function collect_results!(results::Vector{Dict{String, Any}}, testset::Test.Defa
142
167
# Modify names to hold `result_counts`
143
168
for i in result_offset: length (results)
144
169
result = results[i]
145
- rid = (result[ " location " ], result[ " result " ] )
170
+ rid = get_rid (result)
146
171
if get (result_counts, rid, 0 ) > 1
147
172
result[" name" ] = replace (result[" name" ], r" ^([^:]):" =>
148
173
SubstitutionString (" \\ 1 (x$(result_counts[rid]) ):" ))
149
174
end
150
175
end
151
- results
176
+ return results
152
177
end
153
178
154
179
function write_testset_json_files (dir:: String , testset:: Test.DefaultTestSet )
155
- data = Dict{String, Any}[]
180
+ data = Dict{String,Any}[]
156
181
collect_results! (data, testset)
157
182
files = String[]
158
183
# Buildkite is limited to 5000 results per file https://buildkite.com/docs/test-analytics/importing-json
0 commit comments