1 package main
2
3
4
5
6
7
8
9
10
11 import (
12 "bytes"
13 "fmt"
14 "go/build"
15 "io/ioutil"
16 "log"
17 "path/filepath"
18 "strings"
19
20 "golang.org/x/tools/cover"
21 )
22
23 func findFile(file string) (string, error) {
24 dir, file := filepath.Split(file)
25 pkg, err := build.Import(dir, ".", build.FindOnly)
26 if err != nil {
27 return "", fmt.Errorf("can't find %q: %v", file, err)
28 }
29 return filepath.Join(pkg.Dir, file), nil
30 }
31
32
33
34 func mergeProfs(pfss [][]*cover.Profile) []*cover.Profile {
35
36 for i := 0; i < len(pfss); i++ {
37 if len(pfss[i]) > 0 {
38 pfss = pfss[i:]
39 break
40 }
41 }
42 if len(pfss) == 0 {
43 return nil
44 } else if len(pfss) == 1 {
45 return pfss[0]
46 }
47 head, rest := pfss[0], pfss[1:]
48 ret := make([]*cover.Profile, 0, len(head))
49 for i, profile := range head {
50 for _, ps := range rest {
51
52 if len(ps) == 0 {
53 continue
54 } else if len(ps) < i+1 {
55 continue
56 } else if ps[i].FileName != profile.FileName {
57 continue
58 }
59 profile.Blocks = mergeProfBlocks(profile.Blocks, ps[i].Blocks)
60 }
61 ret = append(ret, profile)
62 }
63 return ret
64 }
65
66 func mergeProfBlocks(as, bs []cover.ProfileBlock) []cover.ProfileBlock {
67 if len(as) != len(bs) {
68 log.Fatal("Two block length should be same")
69 }
70
71
72 ret := make([]cover.ProfileBlock, 0, len(as))
73 for i, a := range as {
74 b := bs[i]
75 if a.StartLine != b.StartLine || a.StartCol != b.StartCol {
76 log.Fatal("Blocks are not sorted")
77 }
78 a.Count += b.Count
79 ret = append(ret, a)
80 }
81 return ret
82 }
83
84
85 func toSF(profs []*cover.Profile) ([]*SourceFile, error) {
86 var rv []*SourceFile
87 for _, prof := range profs {
88 path, err := findFile(prof.FileName)
89 if err != nil {
90 log.Fatalf("Can't find %v", err)
91 }
92 fb, err := ioutil.ReadFile(path)
93 if err != nil {
94 log.Fatalf("Error reading %v: %v", path, err)
95 }
96 sf := &SourceFile{
97 Name: getCoverallsSourceFileName(path),
98 Source: string(fb),
99 Coverage: make([]interface{}, 1+bytes.Count(fb, []byte{'\n'})),
100 }
101
102 for _, block := range prof.Blocks {
103 for i := block.StartLine; i <= block.EndLine; i++ {
104 count, _ := sf.Coverage[i-1].(int)
105 sf.Coverage[i-1] = count + block.Count
106 }
107 }
108
109 rv = append(rv, sf)
110 }
111
112 return rv, nil
113 }
114
115 func parseCover(fn string) ([]*SourceFile, error) {
116 var pfss [][]*cover.Profile
117 for _, p := range strings.Split(fn, ",") {
118 profs, err := cover.ParseProfiles(p)
119 if err != nil {
120 return nil, fmt.Errorf("Error parsing coverage: %v", err)
121 }
122 pfss = append(pfss, profs)
123 }
124
125 sourceFiles, err := toSF(mergeProfs(pfss))
126 if err != nil {
127 return nil, err
128 }
129
130 return sourceFiles, nil
131 }
132
View as plain text