coverage.go
 1  package language
 2  
 3  import (
 4  	"encoding/json"
 5  	"errors"
 6  	"os"
 7  	"regexp"
 8  
 9  	pkgerrors "github.com/pkg/errors"
10  	"github.com/symflower/eval-dev-quality/log"
11  )
12  
13  // CoverageBlockUnfolded is an unfolded representation of a coverage data block.
14  type CoverageBlockUnfolded struct {
15  	// FileRange holds the file range.
16  	FileRange string
17  	// CoverageType holds the covered coverage type.
18  	CoverageType string
19  	// Count holds the execution count.
20  	Count uint
21  }
22  
23  // FileRangeMatch match a textual file range with lines and columns.
24  var FileRangeMatch = regexp.MustCompile(`^(.+):(\d+):(\d+)-(.+):(\d+):(\d+)$`)
25  
26  // CoverageObjectCountOfFile parses the given coverage file and returns its coverage object count.
27  func CoverageObjectCountOfFile(logger *log.Logger, coverageFilePath string) (coverageObjectCount uint64, err error) {
28  	coverageFile, err := os.ReadFile(coverageFilePath)
29  	if err != nil {
30  		return 0, pkgerrors.WithMessage(pkgerrors.WithStack(err), coverageFilePath)
31  	}
32  
33  	// Log coverage objects.
34  	logger.Printf("coverage objects: %s", string(coverageFile))
35  
36  	var coverageData []CoverageBlockUnfolded
37  	if err := json.Unmarshal(coverageFile, &coverageData); err != nil {
38  		return 0, pkgerrors.WithMessage(pkgerrors.WithStack(err), string(coverageFile))
39  	}
40  	for _, c := range coverageData {
41  		if c.Count == 0 {
42  			continue
43  		}
44  
45  		fr := FileRangeMatch.FindStringSubmatch(c.FileRange)
46  		if fr == nil {
47  			return 0, pkgerrors.WithMessage(pkgerrors.WithStack(errors.New("could not match file range")), c.FileRange)
48  		}
49  
50  		coverageObjectCount++
51  	}
52  
53  	return coverageObjectCount, nil
54  }