github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/pkg/printers/junitxml.go (about)

     1  package printers
     2  
     3  import (
     4  	"encoding/xml"
     5  	"fmt"
     6  	"io"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/chenfeining/golangci-lint/pkg/result"
    11  )
    12  
    13  type testSuitesXML struct {
    14  	XMLName    xml.Name `xml:"testsuites"`
    15  	TestSuites []testSuiteXML
    16  }
    17  
    18  type testSuiteXML struct {
    19  	XMLName   xml.Name      `xml:"testsuite"`
    20  	Suite     string        `xml:"name,attr"`
    21  	Tests     int           `xml:"tests,attr"`
    22  	Errors    int           `xml:"errors,attr"`
    23  	Failures  int           `xml:"failures,attr"`
    24  	TestCases []testCaseXML `xml:"testcase"`
    25  }
    26  
    27  type testCaseXML struct {
    28  	Name      string     `xml:"name,attr"`
    29  	ClassName string     `xml:"classname,attr"`
    30  	Failure   failureXML `xml:"failure"`
    31  }
    32  
    33  type failureXML struct {
    34  	Message string `xml:"message,attr"`
    35  	Type    string `xml:"type,attr"`
    36  	Content string `xml:",cdata"`
    37  }
    38  
    39  type JunitXML struct {
    40  	w io.Writer
    41  }
    42  
    43  func NewJunitXML(w io.Writer) *JunitXML {
    44  	return &JunitXML{w: w}
    45  }
    46  
    47  func (p JunitXML) Print(issues []result.Issue) error {
    48  	suites := make(map[string]testSuiteXML) // use a map to group by file
    49  
    50  	for ind := range issues {
    51  		i := &issues[ind]
    52  		suiteName := i.FilePath()
    53  		testSuite := suites[suiteName]
    54  		testSuite.Suite = i.FilePath()
    55  		testSuite.Tests++
    56  		testSuite.Failures++
    57  
    58  		tc := testCaseXML{
    59  			Name:      i.FromLinter,
    60  			ClassName: i.Pos.String(),
    61  			Failure: failureXML{
    62  				Type:    i.Severity,
    63  				Message: i.Pos.String() + ": " + i.Text,
    64  				Content: fmt.Sprintf("%s: %s\nCategory: %s\nFile: %s\nLine: %d\nDetails: %s",
    65  					i.Severity, i.Text, i.FromLinter, i.Pos.Filename, i.Pos.Line, strings.Join(i.SourceLines, "\n")),
    66  			},
    67  		}
    68  
    69  		testSuite.TestCases = append(testSuite.TestCases, tc)
    70  		suites[suiteName] = testSuite
    71  	}
    72  
    73  	var res testSuitesXML
    74  	for _, val := range suites {
    75  		res.TestSuites = append(res.TestSuites, val)
    76  	}
    77  
    78  	sort.Slice(res.TestSuites, func(i, j int) bool {
    79  		return res.TestSuites[i].Suite < res.TestSuites[j].Suite
    80  	})
    81  
    82  	enc := xml.NewEncoder(p.w)
    83  	enc.Indent("", "  ")
    84  	if err := enc.Encode(res); err != nil {
    85  		return err
    86  	}
    87  	return nil
    88  }