package main import ( "bufio" "encoding/xml" "fmt" "io" "runtime" "strings" ) type JUnitTestSuites struct { XMLName xml.Name `xml:"testsuites"` Suites []JUnitTestSuite } type JUnitTestSuite struct { XMLName xml.Name `xml:"testsuite"` Tests int `xml:"tests,attr"` Failures int `xml:"failures,attr"` Time string `xml:"time,attr"` Name string `xml:"name,attr"` Properties []JUnitProperty `xml:"properties>property,omitempty"` TestCases []JUnitTestCase } type JUnitTestCase struct { XMLName xml.Name `xml:"testcase"` Classname string `xml:"classname,attr"` Name string `xml:"name,attr"` Time string `xml:"time,attr"` SkipMessage *JUnitSkipMessage `xml:"skipped,omitempty"` Failure *JUnitFailure `xml:"failure,omitempty"` } type JUnitSkipMessage struct { Message string `xml:"message,attr"` } type JUnitProperty struct { Name string `xml:"name,attr"` Value string `xml:"value,attr"` } type JUnitFailure struct { Message string `xml:"message,attr"` Type string `xml:"type,attr"` Contents string `xml:",chardata"` } func NewJUnitProperty(name, value string) JUnitProperty { return JUnitProperty{ Name: name, Value: value, } } // JUnitReportXML writes a junit xml representation of the given report to w // in the format described at http://windyroad.org/dl/Open%20Source/JUnit.xsd func JUnitReportXML(report *Report, noXmlHeader bool, w io.Writer) error { suites := JUnitTestSuites{} // convert Report to JUnit test suites for _, pkg := range report.Packages { ts := JUnitTestSuite{ Tests: len(pkg.Tests), Failures: 0, Time: formatTime(pkg.Time), Name: pkg.Name, Properties: []JUnitProperty{}, TestCases: []JUnitTestCase{}, } classname := pkg.Name if idx := strings.LastIndex(classname, "/"); idx > -1 && idx < len(pkg.Name) { classname = pkg.Name[idx+1:] } // properties ts.Properties = append(ts.Properties, NewJUnitProperty("go.version", runtime.Version())) // individual test cases for _, test := range pkg.Tests { testCase := JUnitTestCase{ Classname: classname, Name: test.Name, Time: formatTime(test.Time), Failure: nil, } if test.Result == FAIL { ts.Failures += 1 testCase.Failure = &JUnitFailure{ Message: "Failed", Type: "", Contents: strings.Join(test.Output, "\n"), } } if test.Result == SKIP { testCase.SkipMessage = &JUnitSkipMessage{strings.Join(test.Output, "\n")} } ts.TestCases = append(ts.TestCases, testCase) } suites.Suites = append(suites.Suites, ts) } // to xml bytes, err := xml.MarshalIndent(suites, "", "\t") if err != nil { return err } writer := bufio.NewWriter(w) if !noXmlHeader { writer.WriteString(xml.Header) } writer.Write(bytes) writer.WriteByte('\n') writer.Flush() return nil } func countFailures(tests []Test) (result int) { for _, test := range tests { if test.Result == FAIL { result += 1 } } return } func formatTime(time int) string { return fmt.Sprintf("%.3f", float64(time)/1000.0) }