code-intelligence.com/cifuzz@v0.40.0/internal/cmd/coverage/summary/jacoco.go (about)

     1  package summary
     2  
     3  import (
     4  	"encoding/xml"
     5  	"fmt"
     6  	"io"
     7  
     8  	"code-intelligence.com/cifuzz/pkg/log"
     9  )
    10  
    11  type jacocoCounter struct {
    12  	Text    string `xml:",chardata"`
    13  	Type    string `xml:"type,attr"`
    14  	Missed  int    `xml:"missed,attr"`
    15  	Covered int    `xml:"covered,attr"`
    16  }
    17  
    18  type jacocoReport struct {
    19  	XMLName     xml.Name `xml:"report"`
    20  	Text        string   `xml:",chardata"`
    21  	Name        string   `xml:"name,attr"`
    22  	Sessioninfo []struct {
    23  		Text  string `xml:",chardata"`
    24  		ID    string `xml:"id,attr"`
    25  		Start string `xml:"start,attr"`
    26  		Dump  string `xml:"dump,attr"`
    27  	} `xml:"sessioninfo"`
    28  	Packages []struct {
    29  		Text    string `xml:",chardata"`
    30  		Name    string `xml:"name,attr"`
    31  		Classes []struct {
    32  			Text           string `xml:",chardata"`
    33  			Name           string `xml:"name,attr"`
    34  			Sourcefilename string `xml:"sourcefilename,attr"`
    35  			Method         []struct {
    36  				Text    string          `xml:",chardata"`
    37  				Name    string          `xml:"name,attr"`
    38  				Desc    string          `xml:"desc,attr"`
    39  				Line    string          `xml:"line,attr"`
    40  				Counter []jacocoCounter `xml:"counter"`
    41  			} `xml:"method"`
    42  			Counter []jacocoCounter `xml:"counter"`
    43  		} `xml:"class"`
    44  		Sourcefiles []struct {
    45  			Text string `xml:",chardata"`
    46  			Name string `xml:"name,attr"`
    47  			Line []struct {
    48  				Text string `xml:",chardata"`
    49  				Nr   string `xml:"nr,attr"`
    50  				Mi   string `xml:"mi,attr"`
    51  				Ci   string `xml:"ci,attr"`
    52  				Mb   string `xml:"mb,attr"`
    53  				Cb   string `xml:"cb,attr"`
    54  			} `xml:"line"`
    55  			Counter []jacocoCounter `xml:"counter"`
    56  		} `xml:"sourcefile"`
    57  		Counter []jacocoCounter `xml:"counter"`
    58  	} `xml:"package"`
    59  	Counter []jacocoCounter `xml:"counter"`
    60  }
    61  
    62  func countJacoco(c *Coverage, counter *jacocoCounter) {
    63  	switch counter.Type {
    64  	case "LINE":
    65  		c.LinesFound += counter.Covered + counter.Missed
    66  		c.LinesHit += counter.Covered
    67  	case "BRANCH":
    68  		c.BranchesFound += counter.Covered + counter.Missed
    69  		c.BranchesHit += counter.Covered
    70  	case "METHOD":
    71  		c.FunctionsFound += counter.Covered + counter.Missed
    72  		c.FunctionsHit += counter.Covered
    73  	}
    74  }
    75  
    76  // ParseJacocoXML takes a jacoco xml report and turns it into
    77  // the `CoverageSummary` struct. The parsing is as forgiving
    78  // as possible. It will output debug/error logs instead of
    79  // failing, with the goal to gather as much information as
    80  // possible
    81  func ParseJacocoXML(in io.Reader) *CoverageSummary {
    82  	summary := &CoverageSummary{
    83  		Total: &Coverage{},
    84  	}
    85  
    86  	output, err := io.ReadAll(in)
    87  	if err != nil {
    88  		log.Debugf("Unable to read jacoco xml report")
    89  		return summary
    90  	}
    91  	report := &jacocoReport{}
    92  	err = xml.Unmarshal(output, report)
    93  	if err != nil {
    94  		log.Debugf("Unable to parse jacoco xml report")
    95  		return summary
    96  	}
    97  
    98  	var currentFile *FileCoverage
    99  	for _, xmlPackage := range report.Packages {
   100  		for _, sourcefile := range xmlPackage.Sourcefiles {
   101  			currentFile = &FileCoverage{
   102  				Filename: fmt.Sprintf("%s/%s", xmlPackage.Name, sourcefile.Name),
   103  				Coverage: &Coverage{},
   104  			}
   105  			for _, counter := range sourcefile.Counter {
   106  				countJacoco(summary.Total, &counter)
   107  				countJacoco(currentFile.Coverage, &counter)
   108  			}
   109  			summary.Files = append(summary.Files, currentFile)
   110  		}
   111  	}
   112  
   113  	return summary
   114  }