-
Notifications
You must be signed in to change notification settings - Fork 2
/
cover.go
141 lines (126 loc) · 4.11 KB
/
cover.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package tstat
import (
"github.com/nickfiggins/tstat/internal/gocover"
"github.com/nickfiggins/tstat/internal/gofunc"
"github.com/nickfiggins/tstat/internal/mathutil"
"golang.org/x/exp/maps"
)
// Coverage is the coverage statistics parsed from a single test profile.
type Coverage struct {
Percent float64 // Percent is the total percent of statements covered.
Packages []*PackageCoverage // Packages is the coverage of each package.
}
// Package returns the coverage of a single package in the run. It's a convenience method
// for finding a file in the list of file coverages.
func (c *Coverage) Package(name string) (*PackageCoverage, bool) {
for _, pkg := range c.Packages {
if pkg.Name == name {
return pkg, true
}
}
return nil, false
}
func newCoverage(coverPkgs []*gocover.PackageStatements, funcProfile []*gofunc.PackageFunctions) *Coverage {
packages := make(map[string]*PackageCoverage)
covered, total := int64(0), int64(0)
for _, pkg := range coverPkgs {
packages[pkg.Package] = newPackageCoverage(pkg)
covered += pkg.CoveredStmts
total += pkg.Stmts
}
for _, pkg := range funcProfile {
pkgCov, ok := packages[pkg.Package]
if !ok {
continue
}
pkgCov.add(pkg)
}
return &Coverage{
Percent: mathutil.Percent(covered, total),
Packages: maps.Values(packages),
}
}
// PackageCoverage is the coverage of a package.
type PackageCoverage struct {
Name string // Name is the name of the package.
Percent float64 // Percent is the percentage of statements covered in the package.
Files []*FileCoverage // Files is the coverage of each file in the package.
}
// File returns the coverage of a file in the package. It's a convenience method
// for finding a file in the list of file coverages.
func (pc *PackageCoverage) File(name string) (*FileCoverage, bool) {
for _, f := range pc.Files {
if f.Name == name {
return f, true
}
}
return nil, false
}
// Functions returns all functions in the package.
func (pc *PackageCoverage) Functions() []FunctionCoverage {
funcs := make([]FunctionCoverage, 0)
for _, f := range pc.Files {
funcs = append(funcs, f.Functions...)
}
return funcs
}
func newPackageCoverage(stmts *gocover.PackageStatements) *PackageCoverage {
files := make([]*FileCoverage, len(stmts.Files))
i := 0
for name, statements := range stmts.Files {
files[i] = &FileCoverage{
Name: name,
Functions: make([]FunctionCoverage, 0),
Percent: statements.Percent,
Stmts: int(statements.Stmts),
CoveredStmts: int(statements.CoveredStmts),
}
i++
}
return &PackageCoverage{
Name: stmts.Package,
Percent: mathutil.Percent(stmts.CoveredStmts, stmts.Stmts),
Files: files,
}
}
func (pc *PackageCoverage) add(pkgFn *gofunc.PackageFunctions) {
for name, file := range pkgFn.Files {
for _, f := range pc.Files {
if f.Name == name {
f.Functions = toFunctions(file.Functions)
return
}
}
}
}
type FileCoverage struct {
Name string // Name is the name of the file.
Percent float64 // Percent is the percent of statements covered.
Functions []FunctionCoverage // Functions is the coverage of each function in the file.
Stmts int // Stmts is the total number of statements in the file.
CoveredStmts int // CoveredStmts is the number of statements covered in the file.
}
// FunctionCoverage is the coverage of a function.
type FunctionCoverage struct {
Name string // Name is the name of the function.
Percent float64 // Percent is the percent of statements covered.
File string // File is the file the function is defined in.
Line int // Line is the line the function is defined on.
Internal bool // Internal is true if the function is internal to the package.
}
func toFunctions(fn []gofunc.Function) []FunctionCoverage {
fns := make([]FunctionCoverage, len(fn))
for i, f := range fn {
fns[i] = FunctionCoverage{
Name: f.Function,
Percent: f.Percent,
Internal: isLower(f.Function[0]),
File: f.File,
Line: f.Line,
}
}
return fns
}
func isLower(c byte) bool {
return c >= 'a' && c <= 'z'
}