-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
135 lines (122 loc) · 3.25 KB
/
main.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
package main
import (
"bufio"
"encoding/json"
"flag"
"fmt"
"os"
"strconv"
"strings"
)
/*
a short program that asserts that unit test coverage is not below established lower limits.
*/
type coverage map[string]float64
var (
update = flag.Bool("update", false, "set --update to increase limits file (e.g. limits.json) to any current higher unit test coverages")
limitsFile = flag.String("limits", "limits.json", "path to the file containing lower bounds for unit test coverage")
coverageFile = flag.String("coverage", "coverage.txt", "path the to output of go test coverage (e.g. go test ./... -coverprofile cover.out > coverage.txt)")
bypass = flag.Bool("bypass", false, "set --bypass to avoid exit status 1 on insufficient coverage")
)
func main() {
flag.Parse()
limitsMap, err := getLimits(*limitsFile)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
coverageMap, err := getCoverage(*coverageFile)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
if *update {
err = updateCoverage(coverageMap, limitsMap, *limitsFile)
} else {
err = assertCoverage(coverageMap, limitsMap)
}
if err != nil {
fmt.Println(err.Error())
if !*bypass {
os.Exit(1)
}
}
os.Exit(0)
}
// getLimits returns a map of limits created from limits.json
func getLimits(filename string) (coverage, error) {
limitsMap := make(coverage)
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
err = json.NewDecoder(f).Decode(&limitsMap)
return limitsMap, err
}
// getCoverage returns a map of current coverage from coverage.txt
func getCoverage(filename string) (coverage, error) {
coverageMap := make(coverage)
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
vals := strings.Split(scanner.Text(), "\t")
percentage := 0.0
if len(vals) >= 4 {
percentage, err = strconv.ParseFloat(strings.TrimLeft(strings.TrimRight(vals[3], "% of statements"), "coverage: "), 64)
if err != nil {
return nil, err
}
coverageMap[vals[1]] = percentage
}
}
return coverageMap, err
}
// assertCoverage returns aggregated errors for coverages below limits
func assertCoverage(coverageMap, limitsMap coverage) error {
var errs []string
for path, percentage := range coverageMap {
limit, ok := limitsMap[path]
if !ok {
continue
}
if percentage < limit {
errs = append(errs, fmt.Sprintf("coverage for %s is %.2f but expected to be >= %.2f", path, percentage, limit))
}
}
if len(errs) > 0 {
return fmt.Errorf("coverage errors:\n%s", strings.Join(errs, "\n"))
}
return nil
}
// updateCoverage updates and writes the limits map with any new, higher coverages
func updateCoverage(coverageMap, limitsMap coverage, filename string) error {
var wasUpdated bool
for path, percentage := range coverageMap {
limit, ok := limitsMap[path]
if !ok || limit < percentage {
limitsMap[path] = percentage
wasUpdated = true
}
}
if wasUpdated {
f, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
j, err := json.MarshalIndent(limitsMap, "", "\t")
if err != nil {
return err
}
_, err = f.Write(j)
if err != nil {
return err
}
}
return nil
}