Skip to content

Commit 9d78466

Browse files
committed
add go-coverage
0 parents  commit 9d78466

File tree

8 files changed

+13505
-0
lines changed

8 files changed

+13505
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea/
2+
*.iml

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# GO Coverage
2+
3+
go-coverage is a lcov/cobertura parser
4+
5+
## Usage
6+
7+
```
8+
lcovParser := New("./test/lcov.info", LCOV)
9+
report, err := lcovParser.Parse()
10+
```

coverage.go

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package coverage
2+
3+
import (
4+
"bufio"
5+
"encoding/xml"
6+
"fmt"
7+
"io"
8+
"io/ioutil"
9+
"os"
10+
"strconv"
11+
"strings"
12+
)
13+
14+
// New creates a new lcov parser
15+
func New(filePath string, mode CoverageMode) Parser {
16+
return Parser{
17+
path: filePath,
18+
mode: mode,
19+
}
20+
}
21+
22+
// Parse parses the lcov file
23+
func (l Parser) Parse() (Report, error) {
24+
switch l.mode {
25+
case LCOV:
26+
return l.processLcov()
27+
case COBERTURA:
28+
return l.processCobertura()
29+
}
30+
return Report{}, fmt.Errorf("coverage.parse> Unknown mode %s", l.mode)
31+
}
32+
33+
func (l Parser) processCobertura() (Report, error) {
34+
file, errF := os.Open(l.path)
35+
if errF != nil {
36+
return Report{}, fmt.Errorf("coverage.processCobertura> Unable to open file: %v", errF)
37+
}
38+
defer file.Close()
39+
40+
b, errR := ioutil.ReadAll(file)
41+
if errR != nil {
42+
return Report{}, fmt.Errorf("coverage.processCobertura> Unable to read file: %v", errR)
43+
}
44+
45+
var cobReport CoberturaCoverage
46+
if err := xml.Unmarshal(b, &cobReport); err != nil {
47+
return Report{}, fmt.Errorf("coverage.processCobertura> Unable to unmarshal content: %v", err)
48+
}
49+
50+
report := Report{
51+
TotalLines: getInt(cobReport.LinesValid),
52+
TotalBranches: getInt(cobReport.BranchesValid),
53+
CoveredLines: getInt(cobReport.LinesCovered),
54+
CoveredBranches: getInt(cobReport.BranchesCovered),
55+
}
56+
return report, nil
57+
}
58+
59+
func (l Parser) processLcov() (Report, error) {
60+
file, errF := os.Open(l.path)
61+
if errF != nil {
62+
return Report{}, fmt.Errorf("coverage.processLcov> Unable to open lcov file: %v", errF)
63+
}
64+
defer file.Close()
65+
66+
r := bufio.NewReader(file)
67+
68+
report := Report{
69+
Files: make([]FileReport, 0),
70+
}
71+
fileReport := FileReport{}
72+
for {
73+
line, errR := r.ReadString('\n')
74+
if errR != nil && errR != io.EOF {
75+
return report, fmt.Errorf("coverage.processLcov> Unable to read line: %v", errR)
76+
}
77+
if errR == io.EOF {
78+
break
79+
}
80+
line = strings.Replace(line, "\n", "", -1)
81+
82+
// Test new file
83+
if strings.HasPrefix(line, "SF:") {
84+
if fileReport.Path != "" {
85+
report.Files = append(report.Files, fileReport)
86+
}
87+
fileReport = FileReport{
88+
Path: strings.Replace(line, "SF:", "", 1),
89+
}
90+
} else {
91+
l.processLcovLine(line, &report, &fileReport)
92+
}
93+
94+
}
95+
return report, nil
96+
}
97+
98+
func (l Parser) processLcovLine(line string, report *Report, fileReport *FileReport) {
99+
switch {
100+
case strings.HasPrefix(line, "FNF:"):
101+
nb := getInt(strings.Replace(line, "FNF:", "", -1))
102+
fileReport.TotalFunctions = nb
103+
report.TotalFunctions += nb
104+
case strings.HasPrefix(line, "FNH:"):
105+
nb := getInt(strings.Replace(line, "FNH:", "", -1))
106+
fileReport.CoveredFunctions = nb
107+
report.CoveredFunctions += nb
108+
case strings.HasPrefix(line, "BRF:"):
109+
nb := getInt(strings.Replace(line, "BRF:", "", -1))
110+
fileReport.TotalBranches = nb
111+
report.TotalBranches += nb
112+
case strings.HasPrefix(line, "BRH:"):
113+
nb := getInt(strings.Replace(line, "BRH:", "", -1))
114+
fileReport.CoveredBranches = nb
115+
report.CoveredBranches += nb
116+
case strings.HasPrefix(line, "LF:"):
117+
nb := getInt(strings.Replace(line, "LF:", "", -1))
118+
fileReport.TotalLines = nb
119+
report.TotalLines += nb
120+
case strings.HasPrefix(line, "LH:"):
121+
nb := getInt(strings.Replace(line, "LH:", "", -1))
122+
fileReport.CoveredLines = nb
123+
report.CoveredLines += nb
124+
}
125+
}
126+
127+
func getInt(s string) int {
128+
i := 0
129+
i, _ = strconv.Atoi(s)
130+
return i
131+
}

coverage_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package coverage
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func TestLCOV(t *testing.T) {
9+
lcovParser := New("./test/lcov.info", LCOV)
10+
report, err := lcovParser.Parse()
11+
12+
assert.NoError(t, err)
13+
assert.Equal(t, 67, report.CoveredBranches)
14+
assert.Equal(t, 1757, report.TotalBranches)
15+
assert.Equal(t, 412, report.CoveredFunctions)
16+
assert.Equal(t, 1695, report.TotalFunctions)
17+
assert.Equal(t, 2798, report.CoveredLines)
18+
assert.Equal(t, 6395, report.TotalLines)
19+
}
20+
21+
func TestCobertura(t *testing.T) {
22+
coberturaParser := New("./test/coverage.xml", COBERTURA)
23+
report, err := coberturaParser.Parse()
24+
25+
assert.NoError(t, err)
26+
assert.Equal(t, 8, report.TotalLines)
27+
assert.Equal(t, 6, report.CoveredLines)
28+
assert.Equal(t, 4, report.TotalBranches)
29+
assert.Equal(t, 2, report.CoveredBranches)
30+
}

test/coverage.xml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" ?>
2+
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
3+
<coverage lines-valid="8" lines-covered="6" line-rate="1" branches-valid="4" branches-covered="2" branch-rate="1" timestamp="1394890504210" complexity="0" version="0.1">
4+
<sources>
5+
<source>/Users/leobalter/dev/testing/solutions/3</source>
6+
</sources>
7+
<packages>
8+
<package name="3" line-rate="1" branch-rate="1" >
9+
<classes>
10+
<class name="cc.js" filename="cc.js" line-rate="1" branch-rate="1" >
11+
<methods>
12+
<method name="normalize" hits="11" signature="()V" >
13+
<lines><line number="1" hits="11" /></lines>
14+
</method>
15+
<method name="getBrand" hits="7" signature="()V" >
16+
<lines><line number="5" hits="7" /></lines>
17+
</method>
18+
</methods>
19+
<lines>
20+
<line number="1" hits="1" branch="false" />
21+
<line number="2" hits="11" branch="false" />
22+
<line number="5" hits="1" branch="false" />
23+
<line number="6" hits="7" branch="false" />
24+
<line number="15" hits="7" branch="false" />
25+
<line number="17" hits="7" branch="false" />
26+
<line number="18" hits="25" branch="true" condition-coverage="100% (4/4)" />
27+
<line number="20" hits="6" branch="false" />
28+
</lines>
29+
</class>
30+
</classes>
31+
</package>
32+
</packages>
33+
</coverage>

0 commit comments

Comments
 (0)