Skip to content

Commit 6d54258

Browse files
committed
refactoring
1 parent d042e3f commit 6d54258

File tree

6 files changed

+212
-105
lines changed

6 files changed

+212
-105
lines changed

cmd/diff-cov/diff-cov.go

Lines changed: 40 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,25 @@ package main
33
import (
44
"flag"
55
"fmt"
6-
"github.com/waigani/diffparser"
7-
"golang.org/x/tools/cover"
8-
"io/ioutil"
6+
"github.com/nim4/diff-cov/diffCoverage"
7+
"github.com/nim4/diff-cov/git"
8+
"github.com/nim4/diff-cov/profile/coverprofile"
99
"os"
10-
"os/exec"
1110
"strings"
1211
)
1312

1413
var (
1514
flagPackage string
16-
flagIgnore string
15+
flagIgnoreFiles string
16+
flagIgnoreBranch string
1717
flagTargetBranch string
1818
flagCoverProfile string
1919
flagMinimumLine int
2020
flagMinimumCov float64
2121
flagFetchTarget bool
22-
ignore []string
22+
flagVerbose bool
2323
)
2424

25-
func fetch() error {
26-
out, err := exec.Command(
27-
"git", "fetch", "origin", "master:refs/remotes/origin/master",
28-
).CombinedOutput()
29-
if err != nil {
30-
fmt.Println(string(out))
31-
return err
32-
}
33-
34-
return nil
35-
}
36-
37-
func diff() ([]byte, error) {
38-
f, err := ioutil.TempFile(os.TempDir(), "diff-cov-")
39-
if err != nil {
40-
return nil, err
41-
}
42-
_ = f.Close()
43-
44-
output := fmt.Sprintf("--output=%s", f.Name())
45-
target := fmt.Sprintf("%s..HEAD", flagTargetBranch)
46-
out, err := exec.Command(
47-
"git", "diff",
48-
"--ignore-all-space", "--ignore-blank-lines",
49-
"--no-color", "--no-ext-diff", "-U0", output, target,
50-
).CombinedOutput()
51-
if err != nil {
52-
fmt.Println(string(out))
53-
return nil, err
54-
}
55-
56-
return ioutil.ReadFile(f.Name())
57-
}
58-
59-
func shouldCountFile(file string) bool {
60-
if !strings.HasSuffix(file, ".go") {
61-
return false
62-
}
63-
64-
for _, suffix := range ignore {
65-
if strings.HasSuffix(file, suffix) {
66-
return false
67-
}
68-
}
69-
70-
return true
71-
}
72-
7325
func setPackage() bool {
7426
if flagPackage == "" {
7527
dir, err := os.Getwd()
@@ -93,10 +45,13 @@ func setPackage() bool {
9345
func main() {
9446
flag.StringVar(&flagCoverProfile,
9547
"coverprofile", "cover.out",
96-
"Path of coverage profile file")
97-
flag.StringVar(&flagIgnore,
98-
"ignore", "_test.go,_gen.go,_mock.go",
48+
"Path of 'coverprofile' file")
49+
flag.StringVar(&flagIgnoreFiles,
50+
"ignore-files", "_test.go,_gen.go,_mock.go",
9951
"Ignore files with given suffix")
52+
flag.StringVar(&flagIgnoreBranch,
53+
"ignore-branches", "hotfix,bugfix",
54+
"Ignore branches which contains given words(case-insensitive)")
10055
flag.StringVar(&flagPackage,
10156
"package", "",
10257
"Package import path(if not set, will try to extract it from the current working directory)")
@@ -107,82 +62,62 @@ func main() {
10762
"fetch", true,
10863
"Fetch the target branch")
10964
flag.IntVar(&flagMinimumLine,
110-
"min-diff", 10,
65+
"min-diff", 0,
11166
"Minimum diff size to trigger coverage check")
11267
flag.Float64Var(&flagMinimumCov,
113-
"min-cov", 50,
68+
"min-cov", 0,
11469
"Minimum required test coverage")
70+
flag.BoolVar(&flagVerbose,
71+
"v", false,
72+
"Verbose")
11573
flag.Parse()
11674

75+
cb, err := git.CurrentBranch()
76+
if err != nil {
77+
fmt.Println(err)
78+
os.Exit(1)
79+
}
80+
81+
for _, ignoreBranch := range strings.Split(flagIgnoreBranch, ",") {
82+
if strings.Contains(strings.ToLower(cb), ignoreBranch) {
83+
fmt.Printf("Ignoring branch %q(contains %q)! Good bye!\n", cb, ignoreBranch)
84+
os.Exit(0)
85+
}
86+
}
87+
11788
if !setPackage() {
11889
fmt.Println("provide package import path: ex. github.com/nim4/example")
11990
os.Exit(1)
12091
}
12192

12293
if flagFetchTarget {
123-
err := fetch()
94+
err := git.Fetch()
12495
if err != nil {
125-
fmt.Printf("Error fetching %q: %v\n", flagTargetBranch, err)
96+
fmt.Println(err)
12697
os.Exit(1)
12798
}
12899
}
129100

130-
ignore = strings.Split(flagIgnore, ",")
131-
ps, err := cover.ParseProfiles(flagCoverProfile)
101+
ignoreFiles := strings.Split(flagIgnoreFiles, ",")
102+
profile := coverprofile.NewGoProfile(flagPackage, flagCoverProfile)
103+
coverage, err := profile.GetCoverage(ignoreFiles)
132104
if err != nil {
133-
fmt.Printf("Error parsing %q: %v\n", flagCoverProfile, err)
105+
fmt.Println(err)
134106
os.Exit(1)
135107
}
136108

137-
coverage := make(map[string]map[int]bool, len(ps))
138-
for _, p := range ps {
139-
file := strings.TrimPrefix(p.FileName, flagPackage)
140-
coverage[file] = make(map[int]bool)
141-
for _, block := range p.Blocks {
142-
for line := block.StartLine; line <= block.EndLine; line++ {
143-
coverage[file][line] = block.Count > 0
144-
}
145-
}
146-
}
147-
148-
b, err := diff()
109+
diff, err := git.Diff(flagTargetBranch)
149110
if err != nil {
150-
fmt.Printf("Error getting diff: %v\n", err)
111+
fmt.Println(err)
151112
os.Exit(1)
152113
}
153114

154-
p, err := diffparser.Parse(string(b))
115+
goDiff, goTestedDiff, err := diffCoverage.Calculate(coverage, diff, ignoreFiles, flagVerbose)
155116
if err != nil {
156-
fmt.Printf("Error parsing diff: %v\n", err)
117+
fmt.Println(err)
157118
os.Exit(1)
158119
}
159120

160-
goDiff := 0
161-
goTestedDiff := 0
162-
for file, changes := range p.Changed() {
163-
if !shouldCountFile(file) {
164-
continue
165-
}
166-
fmt.Printf("%s %d\n", file, len(changes))
167-
168-
// file has any test coverage?
169-
if _, ok := coverage[file]; !ok {
170-
fmt.Printf("No coverage found for %q! is %q updated?\n", file, flagCoverProfile)
171-
goDiff += len(changes)
172-
continue
173-
}
174-
175-
for _, line := range changes {
176-
177-
tested, ok := coverage[file][line]
178-
if ok {
179-
goDiff++
180-
if tested {
181-
goTestedDiff++
182-
}
183-
}
184-
}
185-
}
186121
difCov := 0.0
187122
if goDiff > 0 {
188123
difCov = float64(goTestedDiff) / float64(goDiff) * 100

diffCoverage/coverage.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package diffCoverage
2+
3+
import (
4+
"fmt"
5+
"github.com/nim4/diff-cov/profile"
6+
"github.com/nim4/diff-cov/utils"
7+
"github.com/waigani/diffparser"
8+
)
9+
10+
func Calculate(coverage profile.Coverage, diff string, ignoreFiles []string, verbose bool) (int, int, error) {
11+
goDiff := 0
12+
goTestedDiff := 0
13+
14+
p, err := diffparser.Parse(diff)
15+
if err != nil {
16+
return 0, 0, fmt.Errorf("error parsing diff: %v", err)
17+
}
18+
19+
for file, changes := range p.Changed() {
20+
if !utils.ShouldCountFile(file, ignoreFiles) {
21+
continue
22+
}
23+
if verbose {
24+
fmt.Printf("%s %d\n", file, len(changes))
25+
}
26+
27+
// file has any test diffCoverage?
28+
if _, ok := coverage[file]; !ok {
29+
fmt.Printf("No coverage found for %q! is coverprofile updated?\n", file)
30+
goDiff += len(changes)
31+
continue
32+
}
33+
34+
for _, line := range changes {
35+
tested, ok := coverage[file][line]
36+
if ok {
37+
goDiff++
38+
if tested {
39+
goTestedDiff++
40+
}
41+
}
42+
}
43+
}
44+
45+
return goDiff, goTestedDiff, nil
46+
}

git/git.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package git
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
"os/exec"
8+
)
9+
10+
// TODO: Fetch target not master
11+
func Fetch() error {
12+
out, err := exec.Command(
13+
"git", "fetch", "origin", "master:refs/remotes/origin/master",
14+
).CombinedOutput()
15+
if err != nil {
16+
return fmt.Errorf("error fetching: %v\n%v", string(out), err)
17+
}
18+
19+
return nil
20+
}
21+
22+
func CurrentBranch() (string, error) {
23+
out, err := exec.Command(
24+
"git", "branch", "--show-current",
25+
).CombinedOutput()
26+
if err != nil {
27+
fmt.Println(string(out))
28+
return "", fmt.Errorf("error getting current branch: %v\n%v", string(out), err)
29+
}
30+
31+
return string(out), nil
32+
}
33+
34+
func Diff(targetBranch string) (string, error) {
35+
f, err := ioutil.TempFile(os.TempDir(), "diff-cov-")
36+
if err != nil {
37+
return "", fmt.Errorf("error creating temp file: %v", err)
38+
}
39+
_ = f.Close()
40+
41+
output := fmt.Sprintf("--output=%s", f.Name())
42+
target := fmt.Sprintf("%s..HEAD", targetBranch)
43+
out, err := exec.Command(
44+
"git", "diff",
45+
"--ignore-all-space", "--ignore-blank-lines",
46+
"--no-color", "--no-ext-diff", "-U0", output, target,
47+
).CombinedOutput()
48+
if err != nil {
49+
return "", fmt.Errorf("error getting diff: %v\n%v", string(out), err)
50+
}
51+
52+
b, err := ioutil.ReadFile(f.Name())
53+
if err != nil {
54+
return "", fmt.Errorf("error reading temp file: %v", err)
55+
}
56+
_ = os.Remove(f.Name())
57+
58+
return string(b), nil
59+
}

profile/coverage.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package profile
2+
3+
type Coverage map[string]map[int]bool
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package coverprofile
2+
3+
import (
4+
"fmt"
5+
"github.com/nim4/diff-cov/profile"
6+
"github.com/nim4/diff-cov/utils"
7+
"golang.org/x/tools/cover"
8+
"strings"
9+
)
10+
11+
type GoProfile struct {
12+
packageName string
13+
profilePath string
14+
}
15+
16+
func NewGoProfile(packageName string, profilePath string) *GoProfile {
17+
return &GoProfile{
18+
packageName: packageName,
19+
profilePath: profilePath,
20+
}
21+
}
22+
23+
func (g GoProfile) GetCoverage(ignoreFiles []string) (profile.Coverage, error) {
24+
ps, err := cover.ParseProfiles(g.profilePath)
25+
if err != nil {
26+
return nil, fmt.Errorf("Error parsing %q: %v\n", g.profilePath, err)
27+
}
28+
29+
coverage := make(profile.Coverage, len(ps))
30+
for _, p := range ps {
31+
if !utils.ShouldCountFile(p.FileName, ignoreFiles) {
32+
continue
33+
}
34+
35+
file := strings.TrimPrefix(p.FileName, g.packageName)
36+
coverage[file] = make(map[int]bool)
37+
for _, block := range p.Blocks {
38+
for line := block.StartLine; line <= block.EndLine; line++ {
39+
coverage[file][line] = block.Count > 0
40+
}
41+
}
42+
}
43+
44+
return coverage, nil
45+
}

utils/utils.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package utils
2+
3+
import (
4+
"strings"
5+
)
6+
7+
func ShouldCountFile(file string, ignoreFiles []string) bool {
8+
if !strings.HasSuffix(file, ".go") {
9+
return false
10+
}
11+
12+
for _, suffix := range ignoreFiles {
13+
if strings.HasSuffix(file, suffix) {
14+
return false
15+
}
16+
}
17+
18+
return true
19+
}

0 commit comments

Comments
 (0)