forked from mattn/goveralls
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gocover.go
131 lines (116 loc) · 3.07 KB
/
gocover.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
package main
// Much of the core of this is copied from go's cover tool itself.
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The rest is written by Dustin Sallings
import (
"bytes"
"fmt"
"go/build"
"io/ioutil"
"log"
"path/filepath"
"strings"
"golang.org/x/tools/cover"
)
func findFile(file string) (string, error) {
dir, file := filepath.Split(file)
pkg, err := build.Import(dir, ".", build.FindOnly)
if err != nil {
return "", fmt.Errorf("can't find %q: %v", file, err)
}
return filepath.Join(pkg.Dir, file), nil
}
// mergeProfs merges profiles for same target packages.
// It assumes each profiles have same sorted FileName and Blocks.
func mergeProfs(pfss [][]*cover.Profile) []*cover.Profile {
// skip empty profiles ([no test files])
for i := 0; i < len(pfss); i++ {
if len(pfss[i]) > 0 {
pfss = pfss[i:]
break
}
}
if len(pfss) == 0 {
return nil
} else if len(pfss) == 1 {
return pfss[0]
}
head, rest := pfss[0], pfss[1:]
ret := make([]*cover.Profile, 0, len(head))
for i, profile := range head {
for _, ps := range rest {
// find profiles
if len(ps) == 0 {
continue
} else if len(ps) < i+1 {
continue
} else if ps[i].FileName != profile.FileName {
continue
}
profile.Blocks = mergeProfBlocks(profile.Blocks, ps[i].Blocks)
}
ret = append(ret, profile)
}
return ret
}
func mergeProfBlocks(as, bs []cover.ProfileBlock) []cover.ProfileBlock {
if len(as) != len(bs) {
log.Fatal("Two block length should be same")
}
// cover.ProfileBlock generated by cover.ParseProfiles() is sorted by
// StartLine and StartCol, so we can use index.
ret := make([]cover.ProfileBlock, 0, len(as))
for i, a := range as {
b := bs[i]
if a.StartLine != b.StartLine || a.StartCol != b.StartCol {
log.Fatal("Blocks are not sorted")
}
a.Count += b.Count
ret = append(ret, a)
}
return ret
}
// toSF converts profiles to sourcefiles for coveralls.
func toSF(profs []*cover.Profile) ([]*SourceFile, error) {
var rv []*SourceFile
for _, prof := range profs {
path, err := findFile(prof.FileName)
if err != nil {
log.Fatalf("Can't find %v", err)
}
fb, err := ioutil.ReadFile(path)
if err != nil {
log.Fatalf("Error reading %v: %v", path, err)
}
sf := &SourceFile{
Name: getCoverallsSourceFileName(path),
Source: string(fb),
Coverage: make([]interface{}, 1+bytes.Count(fb, []byte{'\n'})),
}
for _, block := range prof.Blocks {
for i := block.StartLine; i <= block.EndLine; i++ {
count, _ := sf.Coverage[i-1].(int)
sf.Coverage[i-1] = count + block.Count
}
}
rv = append(rv, sf)
}
return rv, nil
}
func parseCover(fn string) ([]*SourceFile, error) {
var pfss [][]*cover.Profile
for _, p := range strings.Split(fn, ",") {
profs, err := cover.ParseProfiles(p)
if err != nil {
return nil, fmt.Errorf("Error parsing coverage: %v", err)
}
pfss = append(pfss, profs)
}
sourceFiles, err := toSF(mergeProfs(pfss))
if err != nil {
return nil, err
}
return sourceFiles, nil
}