internal/testrunner/reporters/formats/xunit.go (107 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package formats
import (
"encoding/xml"
"fmt"
"github.com/elastic/elastic-package/internal/testrunner"
)
func init() {
testrunner.RegisterReporterFormat(ReportFormatXUnit, reportXUnitFormat)
}
const (
// ReportFormatXUnit reports test results in the xUnit format
ReportFormatXUnit testrunner.TestReportFormat = "xUnit"
)
type testSuites struct {
XMLName xml.Name `xml:"testsuites"`
Suites []testSuite `xml:"testsuite"`
}
type testSuite struct {
Comment string `xml:",comment"`
Name string `xml:"name,attr"`
NumTests int `xml:"tests,attr,omitempty"`
NumFailures int `xml:"failures,attr,omitempty"`
NumErrors int `xml:"errors,attr,omitempty"`
NumSkipped int `xml:"skipped,attr,omitempty"`
Suites []testSuite `xml:"testsuite,omitempty"`
Cases []testCase `xml:"testcase,omitempty"`
}
type testCase struct {
Name string `xml:"name,attr"`
ClassName string `xml:"classname,attr"`
TimeInSeconds float64 `xml:"time,attr"`
Error string `xml:"error,omitempty"`
Failure string `xml:"failure,omitempty"`
Skipped *skipped `xml:"skipped,omitempty"`
}
type skipped struct {
Message string `xml:"message,attr"`
}
func reportXUnitFormat(results []testrunner.TestResult) (string, error) {
// test type => package => data stream => test cases
tests := map[string]map[string]map[string][]testCase{}
var numTests, numFailures, numErrors, numSkipped int
for _, r := range results {
testType := string(r.TestType)
if _, exists := tests[testType]; !exists {
tests[testType] = map[string]map[string][]testCase{}
}
if _, exists := tests[testType][r.Package]; !exists {
tests[testType][r.Package] = map[string][]testCase{}
}
if _, exists := tests[testType][r.Package][r.DataStream]; !exists {
tests[testType][r.Package][r.DataStream] = make([]testCase, 0)
}
var failure string
if r.FailureMsg != "" {
failure = r.FailureMsg
numFailures++
}
if r.FailureDetails != "" {
failure += ": " + r.FailureDetails
}
if r.ErrorMsg != "" {
numErrors++
}
if r.Skipped != nil {
numSkipped++
}
name := fmt.Sprintf("%s test", r.TestType)
if r.Name != "" {
name += ": " + r.Name
}
c := testCase{
Name: name,
ClassName: fmt.Sprintf("%s.%s", r.Package, r.DataStream),
TimeInSeconds: r.TimeElapsed.Seconds(),
Error: r.ErrorMsg,
Failure: failure,
}
if r.Skipped != nil {
c.Skipped = &skipped{r.Skipped.String()}
}
numTests++
tests[testType][r.Package][r.DataStream] = append(tests[testType][r.Package][r.DataStream], c)
}
var ts testSuites
ts.Suites = make([]testSuite, 0)
for testType, packages := range tests {
testTypeSuite := testSuite{
Comment: fmt.Sprintf("test suite for %s tests", testType),
Name: testType,
NumTests: numTests,
NumFailures: numFailures,
NumErrors: numErrors,
NumSkipped: numSkipped,
Cases: make([]testCase, 0),
}
for _, pkg := range packages {
for _, ds := range pkg {
testTypeSuite.Cases = append(testTypeSuite.Cases, ds...)
}
}
ts.Suites = append(ts.Suites, testTypeSuite)
}
out, err := xml.MarshalIndent(&ts, "", " ")
if err != nil {
return "", fmt.Errorf("unable to format test results as xUnit: %w", err)
}
return xml.Header + string(out), nil
}