package main import ( "bufio" "encoding/xml" "fmt" "io" "runtime" "strings" ) // JUnitTestSuites is a collection of JUnit test suites. type JUnitTestSuites struct { XMLName xml.Name `xml:"testsuites"` Suites []JUnitTestSuite } // JUnitTestSuite is a single JUnit test suite which may contain many // testcases. 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 } // JUnitTestCase is a single test case with its result. 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"` } // JUnitSkipMessage contains the reason why a testcase was skipped. type JUnitSkipMessage struct { Message string `xml:"message,attr"` } // JUnitProperty represents a key/value pair used to define properties. type JUnitProperty struct { Name string `xml:"name,attr"` Value string `xml:"value,attr"` } // JUnitFailure contains data related to a failed test. type JUnitFailure struct { Message string `xml:"message,attr"` Type string `xml:"type,attr"` Contents string `xml:",chardata"` } // 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, JUnitProperty{"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++ 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++ } } return } func formatTime(time int) string { return fmt.Sprintf("%.3f", float64(time)/1000.0) }