Extract and report coverage information.

When `go test` is run on multiple packages and with coverage collection
enabled, it appends coverage information to the final result line for
each package.

With this change, properly handle this additional information and add it
into the JUnit XML report as a property of the test suite.
This commit is contained in:
Jeff Grafton 2015-06-29 17:36:33 -07:00
parent 38eb577ca2
commit 6087bd544c
7 changed files with 147 additions and 12 deletions

View File

@ -260,6 +260,73 @@ var testCases = []TestCase{
},
},
},
{
name: "09-coverage.txt",
reportName: "09-report.xml",
report: &Report{
Packages: []Package{
{
Name: "package/name",
Time: 160,
Tests: []*Test{
{
Name: "TestZ",
Time: 60,
Result: PASS,
Output: []string{},
},
{
Name: "TestA",
Time: 100,
Result: PASS,
Output: []string{},
},
},
CoveragePct: "13.37",
},
},
},
},
{
name: "10-multipkg-coverage.txt",
reportName: "10-report.xml",
report: &Report{
Packages: []Package{
{
Name: "package1/foo",
Time: 400,
Tests: []*Test{
{
Name: "TestA",
Time: 100,
Result: PASS,
Output: []string{},
},
{
Name: "TestB",
Time: 300,
Result: PASS,
Output: []string{},
},
},
CoveragePct: "10.0",
},
{
Name: "package2/bar",
Time: 4200,
Tests: []*Test{
{
Name: "TestC",
Time: 4200,
Result: PASS,
Output: []string{},
},
},
CoveragePct: "99.8",
},
},
},
},
}
func TestParser(t *testing.T) {
@ -321,6 +388,9 @@ func TestParser(t *testing.T) {
t.Errorf("Test.Output ==\n%s\n, want\n%s", testOutput, expTestOutput)
}
}
if pkg.CoveragePct != expPkg.CoveragePct {
t.Errorf("Package.CoveragePct == %s, want %s", pkg.CoveragePct, expPkg.CoveragePct)
}
}
}
}

View File

@ -78,6 +78,9 @@ func JUnitReportXML(report *Report, noXMLHeader bool, w io.Writer) error {
// properties
ts.Properties = append(ts.Properties, JUnitProperty{"go.version", runtime.Version()})
if pkg.CoveragePct != "" {
ts.Properties = append(ts.Properties, JUnitProperty{"coverage.statements.pct", pkg.CoveragePct})
}
// individual test cases
for _, test := range pkg.Tests {

View File

@ -25,9 +25,10 @@ type Report struct {
// Package contains the test results of a single package.
type Package struct {
Name string
Time int
Tests []*Test
Name string
Time int
Tests []*Test
CoveragePct string
}
// Test contains the results of a single test.
@ -39,8 +40,9 @@ type Test struct {
}
var (
regexStatus = regexp.MustCompile(`^--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)$`)
regexResult = regexp.MustCompile(`^(ok|FAIL)\s+(.+)\s(\d+\.\d+)s$`)
regexStatus = regexp.MustCompile(`^--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)$`)
regexCoverage = regexp.MustCompile(`^coverage:\s+(\d+\.\d+)%\s+of\s+statements$`)
regexResult = regexp.MustCompile(`^(ok|FAIL)\s+(.+)\s(\d+\.\d+)s(?:\s+coverage:\s+(\d+\.\d+)%\s+of\s+statements)?$`)
)
// Parse parses go test output from reader r and returns a report with the
@ -60,6 +62,9 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
// current test
var cur string
// coverage percentage report for current package
var coveragePct string
// parse lines
for {
l, _, err := reader.ReadLine()
@ -79,15 +84,21 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
Result: FAIL,
Output: make([]string, 0),
})
} else if matches := regexResult.FindStringSubmatch(line); len(matches) == 4 {
} else if matches := regexResult.FindStringSubmatch(line); len(matches) == 5 {
if matches[4] != "" {
coveragePct = matches[4]
}
// all tests in this package are finished
report.Packages = append(report.Packages, Package{
Name: matches[2],
Time: parseTime(matches[3]),
Tests: tests,
Name: matches[2],
Time: parseTime(matches[3]),
Tests: tests,
CoveragePct: coveragePct,
})
tests = make([]*Test, 0)
coveragePct = ""
cur = ""
testsTime = 0
} else if matches := regexStatus.FindStringSubmatch(line); len(matches) == 4 {
@ -110,6 +121,8 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
testTime := parseTime(matches[3]) * 10
test.Time = testTime
testsTime += testTime
} else if matches := regexCoverage.FindStringSubmatch(line); len(matches) == 2 {
coveragePct = matches[1]
} else if strings.HasPrefix(line, "\t") {
// test output
test := findTest(tests, cur)
@ -123,9 +136,10 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
if len(tests) > 0 {
// no result line found
report.Packages = append(report.Packages, Package{
Name: pkgName,
Time: testsTime,
Tests: tests,
Name: pkgName,
Time: testsTime,
Tests: tests,
CoveragePct: coveragePct,
})
}

7
tests/09-coverage.txt Normal file
View File

@ -0,0 +1,7 @@
=== RUN TestZ
--- PASS: TestZ (0.06 seconds)
=== RUN TestA
--- PASS: TestA (0.10 seconds)
PASS
coverage: 13.37% of statements
ok package/name 0.160s

11
tests/09-report.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite tests="2" failures="0" time="0.160" name="package/name">
<properties>
<property name="go.version" value="1.0"></property>
<property name="coverage.statements.pct" value="13.37"></property>
</properties>
<testcase classname="name" name="TestZ" time="0.060"></testcase>
<testcase classname="name" name="TestA" time="0.100"></testcase>
</testsuite>
</testsuites>

View File

@ -0,0 +1,12 @@
=== RUN TestA
--- PASS: TestA (0.10 seconds)
=== RUN TestB
--- PASS: TestB (0.30 seconds)
PASS
coverage: 10% of statements
ok package1/foo 0.400s coverage: 10.0% of statements
=== RUN TestC
--- PASS: TestC (4.20 seconds)
PASS
coverage: 99.8% of statements
ok package2/bar 4.200s coverage: 99.8% of statements

18
tests/10-report.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite tests="2" failures="0" time="0.400" name="package1/foo">
<properties>
<property name="go.version" value="go1.4.2"></property>
<property name="coverage.statements.pct" value="10.0"></property>
</properties>
<testcase classname="foo" name="TestA" time="0.100"></testcase>
<testcase classname="foo" name="TestB" time="0.300"></testcase>
</testsuite>
<testsuite tests="1" failures="0" time="4.200" name="package2/bar">
<properties>
<property name="go.version" value="go1.4.2"></property>
<property name="coverage.statements.pct" value="99.8"></property>
</properties>
<testcase classname="bar" name="TestC" time="4.200"></testcase>
</testsuite>
</testsuites>