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  }