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

     1  package summary
     2  
     3  import (
     4  	"bufio"
     5  	"encoding/json"
     6  	"io"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"code-intelligence.com/cifuzz/pkg/log"
    11  )
    12  
    13  func count(c *Coverage, key string, value int) {
    14  	switch key {
    15  	case "FNF":
    16  		c.FunctionsFound += value
    17  	case "FNH":
    18  		c.FunctionsHit += value
    19  	case "BRF":
    20  		c.BranchesFound += value
    21  	case "BRH":
    22  		c.BranchesHit += value
    23  	case "LF":
    24  		c.LinesFound += value
    25  	case "LH":
    26  		c.LinesHit += value
    27  	}
    28  }
    29  
    30  // ParseLcov takes a lcov tracefile report and turns it into
    31  // the `CoverageSummary` struct. The parsing is as forgiving
    32  // as possible. It will output debug/error logs instead of
    33  // failing, with the goal to gather as much information as
    34  // possible
    35  func ParseLcov(in io.Reader) *CoverageSummary {
    36  	summary := &CoverageSummary{
    37  		Total: &Coverage{},
    38  	}
    39  
    40  	var currentFile *FileCoverage
    41  
    42  	// The definition of the lcov tracefile format can be viewed
    43  	// with `man geninfo`
    44  	scanner := bufio.NewScanner(in)
    45  	for scanner.Scan() {
    46  		parts := strings.SplitN(scanner.Text(), ":", 2)
    47  		key := parts[0]
    48  
    49  		switch key {
    50  
    51  		// SF starts a section (for a single file)
    52  		case "SF":
    53  			currentFile = &FileCoverage{
    54  				Filename: parts[1],
    55  				Coverage: &Coverage{},
    56  			}
    57  			summary.Files = append(summary.Files, currentFile)
    58  
    59  		// end of a section
    60  		case "end_of_record":
    61  			currentFile = nil
    62  
    63  			// high level coverage metrics
    64  		case "FNF", "FNH", "BRF", "BRH", "LF", "LH":
    65  			if len(parts) == 1 {
    66  				log.Debugf("Parsing lcov: no value for key '%s'", key)
    67  				break
    68  			}
    69  
    70  			value, err := strconv.Atoi(parts[1])
    71  			if err != nil {
    72  				log.Errorf(err, "Parsing lcov: unable to convert value %s to int", parts[1])
    73  				value = 0
    74  			}
    75  
    76  			count(summary.Total, key, value)
    77  			if currentFile != nil {
    78  				count(currentFile.Coverage, key, value)
    79  			}
    80  
    81  		// these keys are (currently) not relevant for cifuzz
    82  		// so we just ignore them
    83  		case "TN", "FN", "FNDA", "BRDA", "DA":
    84  			log.Debugf("Parsing lcov: Ignored key '%s'. Not implemented by now. ", key)
    85  
    86  		// this branch should only be reached if a key shows up
    87  		// that is not defined in the format specification
    88  		default:
    89  			log.Debugf("Parsing lcov: Unknown key '%s'", key)
    90  
    91  		}
    92  	}
    93  
    94  	out, err := json.MarshalIndent(summary, "", "    ")
    95  	if err != nil {
    96  		log.Error(err, "Parsing lcov: Unable to convert coverage summary to json")
    97  	} else {
    98  		log.Debugf("Successfully parsed lcov report : %s", string(out))
    99  	}
   100  
   101  	return summary
   102  }