• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package main
2
3import (
4	"encoding/json"
5	"flag"
6	"fmt"
7	"log"
8
9	"go/ast"
10	"go/parser"
11	"go/token"
12
13	"golang.org/x/tools/cover"
14)
15
16type CoverageTotal struct {
17	Count     int     `json:"count"`
18	Covered   int     `json:"covered"`
19	Uncovered int     `json:"notcovered"`
20	Percent   float64 `json:"percent"`
21}
22
23type CoverageTotals struct {
24	Functions      CoverageTotal `json:"functions,omitempty"`
25	Lines          CoverageTotal `json:"lines,omitempty"`
26	Regions        CoverageTotal `json:"regions,omitempty"`
27	Instantiations CoverageTotal `json:"instantiations,omitempty"`
28	Branches       CoverageTotal `json:"branches,omitempty"`
29}
30
31type CoverageFile struct {
32	Summary  CoverageTotals `json:"summary,omitempty"`
33	Filename string         `json:"filename,omitempty"`
34}
35
36type CoverageData struct {
37	Totals CoverageTotals `json:"totals,omitempty"`
38	Files  []CoverageFile `json:"files,omitempty"`
39}
40
41type PositionInterval struct {
42	start token.Position
43	end   token.Position
44}
45
46type CoverageSummary struct {
47	Data    []CoverageData `json:"data,omitempty"`
48	Type    string         `json:"type,omitempty"`
49	Version string         `json:"version,omitempty"`
50}
51
52func isFunctionCovered(s token.Position, e token.Position, blocks []cover.ProfileBlock) bool {
53	for _, b := range blocks {
54		if b.StartLine >= s.Line && b.StartLine <= e.Line && b.EndLine >= s.Line && b.EndLine <= e.Line {
55			if b.Count > 0 {
56				return true
57			}
58		}
59	}
60	return false
61}
62
63func computePercent(s *CoverageTotals) {
64	s.Regions.Percent = float64(100*s.Regions.Covered) / float64(s.Regions.Count)
65	s.Lines.Percent = float64(100*s.Lines.Covered) / float64(s.Lines.Count)
66	s.Functions.Percent = float64(100*s.Functions.Covered) / float64(s.Functions.Count)
67}
68
69func main() {
70	flag.Parse()
71
72	if len(flag.Args()) != 1 {
73		log.Fatalf("needs exactly one argument")
74	}
75	profiles, err := cover.ParseProfiles(flag.Args()[0])
76	if err != nil {
77		log.Fatalf("failed to parse profiles: %v", err)
78	}
79	r := CoverageSummary{}
80	r.Type = "oss-fuzz.go.coverage.json.export"
81	r.Version = "2.0.1"
82	r.Data = make([]CoverageData, 1)
83	for _, p := range profiles {
84		fset := token.NewFileSet() // positions are relative to fset
85		f, err := parser.ParseFile(fset, p.FileName, nil, 0)
86		if err != nil {
87			panic(err)
88		}
89		fileCov := CoverageFile{}
90		fileCov.Filename = p.FileName
91		ast.Inspect(f, func(n ast.Node) bool {
92			switch x := n.(type) {
93			case *ast.FuncLit:
94				startf := fset.Position(x.Pos())
95				endf := fset.Position(x.End())
96				fileCov.Summary.Functions.Count++
97				if isFunctionCovered(startf, endf, p.Blocks) {
98					fileCov.Summary.Functions.Covered++
99				} else {
100					fileCov.Summary.Functions.Uncovered++
101				}
102			case *ast.FuncDecl:
103				startf := fset.Position(x.Pos())
104				endf := fset.Position(x.End())
105				fileCov.Summary.Functions.Count++
106				if isFunctionCovered(startf, endf, p.Blocks) {
107					fileCov.Summary.Functions.Covered++
108				} else {
109					fileCov.Summary.Functions.Uncovered++
110				}
111			}
112			return true
113		})
114
115		for _, b := range p.Blocks {
116			fileCov.Summary.Regions.Count++
117			if b.Count > 0 {
118				fileCov.Summary.Regions.Covered++
119			} else {
120				fileCov.Summary.Regions.Uncovered++
121			}
122
123			fileCov.Summary.Lines.Count += b.NumStmt
124			if b.Count > 0 {
125				fileCov.Summary.Lines.Covered += b.NumStmt
126			} else {
127				fileCov.Summary.Lines.Uncovered += b.NumStmt
128			}
129		}
130		r.Data[0].Totals.Regions.Count += fileCov.Summary.Regions.Count
131		r.Data[0].Totals.Regions.Covered += fileCov.Summary.Regions.Covered
132		r.Data[0].Totals.Regions.Uncovered += fileCov.Summary.Regions.Uncovered
133		r.Data[0].Totals.Lines.Count += fileCov.Summary.Lines.Count
134		r.Data[0].Totals.Lines.Covered += fileCov.Summary.Lines.Covered
135		r.Data[0].Totals.Lines.Uncovered += fileCov.Summary.Lines.Uncovered
136		r.Data[0].Totals.Functions.Count += fileCov.Summary.Functions.Count
137		r.Data[0].Totals.Functions.Covered += fileCov.Summary.Functions.Covered
138		r.Data[0].Totals.Functions.Uncovered += fileCov.Summary.Functions.Uncovered
139
140		computePercent(&fileCov.Summary)
141		r.Data[0].Files = append(r.Data[0].Files, fileCov)
142	}
143
144	computePercent(&r.Data[0].Totals)
145	o, _ := json.Marshal(r)
146	fmt.Printf(string(o))
147}
148