github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/pkg/printers/junitxml.go (about)

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