oss-fuzz/infra/go/coverage/gocovsum/gocovsum.go

127 lines
3.2 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"go/ast"
"go/parser"
"go/token"
"os"
"path"
"golang.org/x/tools/cover"
)
type CoverageTotal struct {
Count int `json:"count"`
Covered int `json:"covered"`
Uncovered int `json:"notcovered"`
Percent float64 `json:"percent"`
}
type CoverageTotals struct {
Functions CoverageTotal `json:"functions,omitempty"`
Lines CoverageTotal `json:"lines,omitempty"`
Regions CoverageTotal `json:"regions,omitempty"`
}
type CoverageData struct {
Totals CoverageTotals `json:"totals,omitempty"`
}
type PositionInterval struct {
start token.Position
end token.Position
}
type CoverageSummary struct {
Data []CoverageData `json:"data,omitempty"`
Type string `json:"type,omitempty"`
Version string `json:"version,omitempty"`
}
func isFunctionCovered(s token.Position, e token.Position, blocks []cover.ProfileBlock) bool {
for _, b := range blocks {
if b.StartLine >= s.Line && b.StartLine <= e.Line && b.EndLine >= s.Line && b.EndLine <= e.Line {
if b.Count > 0 {
return true
}
}
}
return false
}
func main() {
flag.Parse()
if len(flag.Args()) != 1 {
log.Fatalf("needs exactly one argument")
}
profiles, err := cover.ParseProfiles(flag.Args()[0])
if err != nil {
log.Fatalf("failed to parse profiles: %v", err)
}
r := CoverageSummary{}
r.Type = "oss-fuzz.go.coverage.json.export"
r.Version = "1.0.0"
r.Data = make([]CoverageData, 1)
gopath := os.Getenv("GOPATH")
if len(gopath) == 0 {
gopath = os.Getenv("HOME") + "/go"
}
for _, p := range profiles {
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, path.Join(gopath, "src", p.FileName), nil, 0)
if err != nil {
panic(err)
}
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.FuncLit:
startf := fset.Position(x.Pos())
endf := fset.Position(x.End())
r.Data[0].Totals.Functions.Count++
if isFunctionCovered(startf, endf, p.Blocks) {
r.Data[0].Totals.Functions.Covered++
} else {
r.Data[0].Totals.Functions.Uncovered++
}
case *ast.FuncDecl:
startf := fset.Position(x.Pos())
endf := fset.Position(x.End())
r.Data[0].Totals.Functions.Count++
if isFunctionCovered(startf, endf, p.Blocks) {
r.Data[0].Totals.Functions.Covered++
} else {
r.Data[0].Totals.Functions.Uncovered++
}
}
return true
})
for _, b := range p.Blocks {
r.Data[0].Totals.Regions.Count++
if b.Count > 0 {
r.Data[0].Totals.Regions.Covered++
} else {
r.Data[0].Totals.Regions.Uncovered++
}
r.Data[0].Totals.Lines.Count += b.NumStmt
if b.Count > 0 {
r.Data[0].Totals.Lines.Covered += b.NumStmt
} else {
r.Data[0].Totals.Lines.Uncovered += b.NumStmt
}
}
}
r.Data[0].Totals.Regions.Percent = float64(100*r.Data[0].Totals.Regions.Covered) / float64(r.Data[0].Totals.Regions.Count)
r.Data[0].Totals.Lines.Percent = float64(100*r.Data[0].Totals.Lines.Covered) / float64(r.Data[0].Totals.Lines.Count)
r.Data[0].Totals.Functions.Percent = float64(100*r.Data[0].Totals.Functions.Covered) / float64(r.Data[0].Totals.Functions.Count)
o, _ := json.Marshal(r)
fmt.Printf(string(o))
}