215
}
216
217
>
func (j *junitReport) collectTestCases() map[string]struct{} {
junit.go
218
>
cases := make(map[string]struct{})
219
>
for _, suite := range j.Suites {
220
>
for _, tc := range suite.Testcases {
221
>
cases[tc.Name] = struct{}{}
222
>
}
223
}
225
}
226
227
>
func (j *junitReport) collectTestCaseFailures() []string {
junit.go
228
>
var failures []string
229
>
for _, suite := range j.Suites {
230
>
if suite.Failures == 0 {
231
continue
232
}
233
>
for _, tc := range suite.Testcases {
junit.go
234
>
if tc.Failure != nil {
235
>
failures = append(failures, tc.Name)
236
>
}
237
}
238
}
239
240
// Sort lexicographically
242
>
243
>
// Find leaf failures using the simplified algorithm
244
>
var leafFailures []string
245
>
for i := 0; i < len(failures)-1; i++ {
246
>
if !strings.HasPrefix(failures[i+1], failures[i]+"/") {
junit.go
247
leafFailures = append(leafFailures, failures[i])
248
}
249
}
251
>
leafFailures = append(leafFailures, failures[len(failures)-1])
252
>
}
253
255
}
256
257
>
func mergeReports(reports []*junitReport) (*junitReport, error) {
junit.go
258
>
if len(reports) == 0 {
259
return nil, errors.New("no reports to merge")
260
}
261
262
>
var reportingErrs []error
junit.go
263
>
var combined junit.Testsuites
264
>
combined.XMLName = reports[0].XMLName
265
>
combined.Name = reports[0].Name
266
>
267
>
for i, report := range reports {
268
>
combined.Tests += report.Tests
269
>
combined.Errors += report.Errors
270
>
combined.Failures += report.Failures
271
>
combined.Skipped += report.Skipped
272
>
combined.Disabled += report.Disabled
273
>
combined.Time += report.Time
274
>
275
>
// If the report is for a retry ...
276
>
var suffix string
277
>
if i > 0 {
278
>
suffix = fmt.Sprintf(" (retry %d)", i)
junit.go
279
>
if i == len(reports)-1 {
280
>
suffix += " (final)"
281
>
}
282
>
prevFailures := reports[i-1].collectTestCaseFailures()
283
>
currCases := report.collectTestCases()
284
>
285
>
var missing []string
286
>
for _, f := range prevFailures {
287
>
if _, ok := currCases[f]; !ok {
288
>
missing = append(missing, f)
junit.go
289
>
}
290
}
292
>
reportingErrs = append(reportingErrs, fmt.Errorf(
junit.go
293
>
"expected rerun of all failures from previous attempt, missing: %v", missing))
294
>
}
295
}
296
297
>
for _, suite := range report.Suites {
junit.go
298
>
if len(suite.Testcases) == 0 {
299
continue
300
}
301
302
>
newSuite := suite // shallow copy
junit.go
303
>
newSuite.Name += suffix
304
>
newSuite.Testcases = make([]junit.Testcase, 0, len(suite.Testcases))
305
>
306
>
// Sort test cases by name.
307
>
slices.SortFunc(suite.Testcases, func(a, b junit.Testcase) int {
308
>
return strings.Compare(a.Name, b.Name)
junit.go
309
>
})
310
311
// Collect test cases.
312
>
for j := range len(suite.Testcases) {
junit.go
313
>
testCase := suite.Testcases[j]
314
>
// Check if this is a parent test case (ie prefix of next subtest).
315
>
// Use testCase.Name+"/" to avoid matching iteration suffixes like #01.
316
>
if j != len(suite.Testcases)-1 && strings.HasPrefix(suite.Testcases[j+1].Name, testCase.Name+"/") {
317
>
// Discard test case parents since they provide no value.
junit.go
318
>
continue
319
}
320
321
// Parse failure details from Failure.Data, if present.
322
>
if testCase.Failure != nil && testCase.Failure.Data != "" {
junit.go
323
>
if details := parseFailureDetails(testCase.Failure.Data); details != noFailureDetails {
324
>
testCase.Failure.Data = details
junit.go
325
>
}
326
}
327
328
// Failure.Type carries the canonical kind in merged JUnit.
329
>
if testCase.Failure != nil {
junit.go
330
>
if suite.Name == alertsSuiteName {
331
if testCase.Failure.Type == "" {
332
testCase.Failure.Type = testCase.Failure.Message
333
}
335
>
testCase.Failure.Type = string(failureTypeFailed)
336
>
}
337
}
339
>
newSuite.Testcases = append(newSuite.Testcases, testCase)
340
}
341
>
combined.Suites = append(combined.Suites, newSuite)
junit.go
342
}
343
}
344
346
>
Testsuites: combined,
347
>
reportingErrs: reportingErrs,
348
>
}, nil
349
}
350