github.com/fafucoder/cilium@v1.6.11/test/ginkgo-ext/junit_reporter.go (about) 1 package ginkgoext 2 3 /* 4 5 JUnit XML Reporter for Ginkgo 6 7 For usage instructions: http://onsi.github.io/ginkgo/#generating_junit_xml_output 8 9 Reference file: 10 https://github.com/onsi/ginkgo/blob/39febac9157b63aeba74d843d6b1c30990f3d7ed/reporters/junit_reporter.go 11 Junit Reference: 12 https://www.ibm.com/support/knowledgecenter/en/SSQ2R2_9.1.1/com.ibm.rsar.analysis.codereview.cobol.doc/topics/cac_useresults_junit.html 13 */ 14 15 import ( 16 "encoding/xml" 17 "fmt" 18 "math" 19 "os" 20 "strings" 21 22 "github.com/onsi/ginkgo/config" 23 "github.com/onsi/ginkgo/types" 24 ) 25 26 // JUnitTestSuite main struct to report all test in Junit Format 27 type JUnitTestSuite struct { 28 XMLName xml.Name `xml:"testsuite"` 29 TestCases []JUnitTestCase `xml:"testcase"` 30 Name string `xml:"name,attr"` 31 Tests int `xml:"tests,attr"` 32 Failures int `xml:"failures,attr"` 33 Errors int `xml:"errors,attr"` 34 Time float64 `xml:"time,attr"` 35 } 36 37 // JUnitTestCase test case struct to report in Junit Format. 38 type JUnitTestCase struct { 39 Name string `xml:"name,attr"` 40 ClassName string `xml:"classname,attr"` 41 FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"` 42 Skipped *JUnitSkipped `xml:"skipped,omitempty"` 43 Time float64 `xml:"time,attr"` 44 SystemErr string `xml:"system-err,omitempty"` 45 SystemOut string `xml:"system-out,omitempty"` 46 } 47 48 // JUnitFailureMessage failure message struct 49 type JUnitFailureMessage struct { 50 Type string `xml:"type,attr"` 51 Message string `xml:",chardata"` 52 } 53 54 // JUnitSkipped skipped struct to report XML 55 type JUnitSkipped struct { 56 XMLName xml.Name `xml:"skipped"` 57 } 58 59 // JUnitReporter struct that uses ginkgo to report 60 type JUnitReporter struct { 61 suite JUnitTestSuite 62 filename string 63 testSuiteName string 64 } 65 66 //NewJUnitReporter creates a new JUnit XML reporter. The XML will be stored in the passed in filename. 67 func NewJUnitReporter(filename string) *JUnitReporter { 68 return &JUnitReporter{ 69 filename: filename, 70 } 71 } 72 73 // SpecSuiteWillBegin create the main JUnitTestSuite based on Ginkgo parameters 74 func (reporter *JUnitReporter) SpecSuiteWillBegin(config config.GinkgoConfigType, summary *types.SuiteSummary) { 75 reporter.suite = JUnitTestSuite{ 76 Name: summary.SuiteDescription, 77 TestCases: []JUnitTestCase{}, 78 } 79 reporter.testSuiteName = summary.SuiteDescription 80 } 81 82 // SpecWillRun needed by ginkgo. Not used. 83 func (reporter *JUnitReporter) SpecWillRun(specSummary *types.SpecSummary) { 84 } 85 86 // BeforeSuiteDidRun Beforesuite report function 87 func (reporter *JUnitReporter) BeforeSuiteDidRun(setupSummary *types.SetupSummary) { 88 reporter.handleSetupSummary("BeforeSuite", setupSummary) 89 } 90 91 // AfterSuiteDidRun ginkgo.Aftersuite report function 92 func (reporter *JUnitReporter) AfterSuiteDidRun(setupSummary *types.SetupSummary) { 93 reporter.handleSetupSummary("AfterSuite", setupSummary) 94 } 95 96 func failureMessage(failure types.SpecFailure) string { 97 return fmt.Sprintf("%s\n%s\n%s", failure.ComponentCodeLocation.String(), failure.Message, failure.Location.String()) 98 } 99 100 func (reporter *JUnitReporter) handleSetupSummary(name string, setupSummary *types.SetupSummary) { 101 if setupSummary.State != types.SpecStatePassed { 102 testCase := JUnitTestCase{ 103 Name: name, 104 ClassName: reporter.testSuiteName, 105 } 106 107 testCase.FailureMessage = &JUnitFailureMessage{ 108 Type: reporter.failureTypeForState(setupSummary.State), 109 Message: failureMessage(setupSummary.Failure), 110 } 111 testCase.SystemErr = setupSummary.CapturedOutput 112 testCase.Time = setupSummary.RunTime.Seconds() 113 reporter.suite.TestCases = append(reporter.suite.TestCases, testCase) 114 } 115 } 116 117 // SpecDidComplete reports the test results in a JUTestCase struct 118 func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) { 119 testCase := JUnitTestCase{ 120 Name: strings.Join(specSummary.ComponentTexts[1:], " "), 121 ClassName: reporter.testSuiteName, 122 } 123 if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked { 124 testCase.FailureMessage = &JUnitFailureMessage{ 125 Type: reporter.failureTypeForState(specSummary.State), 126 Message: failureMessage(specSummary.Failure), 127 } 128 checks, output := reportChecks(specSummary.CapturedOutput) 129 testCase.SystemErr = checks 130 testCase.SystemOut = output 131 } 132 if specSummary.State == types.SpecStateSkipped || specSummary.State == types.SpecStatePending { 133 testCase.Skipped = &JUnitSkipped{} 134 } 135 testCase.Time = specSummary.RunTime.Seconds() 136 reporter.suite.TestCases = append(reporter.suite.TestCases, testCase) 137 } 138 139 // SpecSuiteDidEnd summary information to Suite summary in the xml report 140 func (reporter *JUnitReporter) SpecSuiteDidEnd(summary *types.SuiteSummary) { 141 reporter.suite.Tests = summary.NumberOfSpecsThatWillBeRun 142 reporter.suite.Time = math.Trunc(summary.RunTime.Seconds() * 1000 / 1000) 143 reporter.suite.Failures = summary.NumberOfFailedSpecs 144 reporter.suite.Errors = 0 145 file, err := os.Create(reporter.filename) 146 if err != nil { 147 fmt.Printf("Failed to create JUnit report file: %s\n\t%s", reporter.filename, err.Error()) 148 } 149 defer file.Close() 150 file.WriteString(xml.Header) 151 encoder := xml.NewEncoder(file) 152 encoder.Indent(" ", " ") 153 err = encoder.Encode(reporter.suite) 154 if err != nil { 155 fmt.Printf("Failed to generate JUnit report\n\t%s", err.Error()) 156 } 157 } 158 159 func (reporter *JUnitReporter) failureTypeForState(state types.SpecState) string { 160 switch state { 161 case types.SpecStateFailed: 162 return "Failure" 163 case types.SpecStateTimedOut: 164 return "Timeout" 165 case types.SpecStatePanicked: 166 return "Panic" 167 default: 168 return "Unknown" 169 } 170 } 171 172 // reportChecks filters the given string from the stodout and substract the 173 // information that is within the <Checks></Checks> labels. It'll return the 174 // given output as first result, and the check output in the second string. 175 func reportChecks(output string) (string, string) { 176 var checks string 177 var stdout string 178 var dest = "stdout" 179 180 for _, line := range strings.Split(output, "\n") { 181 if line == "<Checks>" { 182 dest = "checks" 183 continue 184 } 185 186 if line == "</Checks>" { 187 dest = "stdout" 188 continue 189 } 190 switch dest { 191 case "stdout": 192 stdout += line + "\n" 193 continue 194 case "checks": 195 checks += line + "\n" 196 continue 197 } 198 } 199 return stdout, checks 200 }