github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/linter/report/reporters/jUnitReporter.go (about) 1 package reporters 2 3 import ( 4 "encoding/xml" 5 "fmt" 6 "io" 7 8 "github.com/yoheimuta/go-protoparser/v4/parser/meta" 9 10 "github.com/yoheimuta/protolint/linter/report" 11 ) 12 13 const ( 14 packageName = "net.protolint" 15 ) 16 17 // JUnitTestSuites is a collection of JUnit test suites. 18 type JUnitTestSuites struct { 19 XMLName xml.Name `xml:"testsuites"` 20 Suites []JUnitTestSuite 21 } 22 23 // JUnitTestSuite is a single JUnit test suite which may contain many testcases. 24 type JUnitTestSuite struct { 25 XMLName xml.Name `xml:"testsuite"` 26 Package string `xml:"package"` 27 Tests int `xml:"tests,attr"` 28 Failures int `xml:"failures,attr"` 29 Time string `xml:"time,attr"` 30 TestCases []JUnitTestCase 31 } 32 33 // JUnitTestCase is a single test case with its result. 34 type JUnitTestCase struct { 35 XMLName xml.Name `xml:"testcase"` 36 ClassName string `xml:"classname,attr"` 37 Name string `xml:"name,attr"` 38 Time string `xml:"time,attr"` 39 Failure *JUnitFailure `xml:"failure,omitempty"` 40 } 41 42 // JUnitFailure contains data related to a failed test. 43 type JUnitFailure struct { 44 Message string `xml:"message,attr"` 45 Type string `xml:"type,attr"` 46 Contents string `xml:",cdata"` 47 } 48 49 func constructTestCaseName(ruleID string) string { 50 return packageName + "." + ruleID 51 } 52 53 func constructContents(pos meta.Position) string { 54 return fmt.Sprintf("line %d, col %d", pos.Line, pos.Column) 55 } 56 57 // JUnitReporter prints failures in JUnit XML format. 58 type JUnitReporter struct{} 59 60 // Report writes failures to w. 61 func (r JUnitReporter) Report(w io.Writer, fs []report.Failure) error { 62 suites := &JUnitTestSuites{} 63 if 0 < len(fs) { 64 var testcases []JUnitTestCase 65 for _, f := range fs { 66 testcase := JUnitTestCase{ 67 Name: constructTestCaseName(f.RuleID()), 68 ClassName: f.FilenameWithoutExt(), 69 Time: "0", 70 Failure: &JUnitFailure{ 71 Message: f.Message(), 72 Type: "error", 73 Contents: constructContents(f.Pos()), 74 }, 75 } 76 testcases = append(testcases, testcase) 77 } 78 79 suite := JUnitTestSuite{ 80 Package: packageName, 81 Tests: len(fs), 82 Failures: len(fs), 83 Time: "0", 84 TestCases: testcases, 85 } 86 suites.Suites = append(suites.Suites, suite) 87 } else { 88 suites.Suites = []JUnitTestSuite{ 89 { 90 Package: packageName, 91 Tests: 1, 92 Time: "0", 93 TestCases: []JUnitTestCase{ 94 { 95 ClassName: constructTestCaseName("ALL_RULES"), 96 Name: "All Rules", 97 Time: "0", 98 }, 99 }, 100 }, 101 } 102 } 103 104 _, err := w.Write([]byte(xml.Header)) 105 if err != nil { 106 return err 107 } 108 109 enc := xml.NewEncoder(w) 110 enc.Indent(" ", " ") 111 err = enc.Encode(suites) 112 if err != nil { 113 return err 114 } 115 116 _, err = w.Write([]byte("\n")) 117 if err != nil { 118 return err 119 } 120 return nil 121 }