diff --git a/go-junit-report_test.go b/go-junit-report_test.go index daa3cd8..6b62447 100644 --- a/go-junit-report_test.go +++ b/go-junit-report_test.go @@ -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) + } } } } diff --git a/junit-formatter.go b/junit-formatter.go index e247612..932f5aa 100644 --- a/junit-formatter.go +++ b/junit-formatter.go @@ -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 { diff --git a/parser.go b/parser.go index 978abe9..535be35 100644 --- a/parser.go +++ b/parser.go @@ -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, }) } diff --git a/tests/09-coverage.txt b/tests/09-coverage.txt new file mode 100644 index 0000000..ee362d8 --- /dev/null +++ b/tests/09-coverage.txt @@ -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 diff --git a/tests/09-report.xml b/tests/09-report.xml new file mode 100644 index 0000000..9bbc3c2 --- /dev/null +++ b/tests/09-report.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/10-multipkg-coverage.txt b/tests/10-multipkg-coverage.txt new file mode 100644 index 0000000..3c4f213 --- /dev/null +++ b/tests/10-multipkg-coverage.txt @@ -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 diff --git a/tests/10-report.xml b/tests/10-report.xml new file mode 100644 index 0000000..ceb97fb --- /dev/null +++ b/tests/10-report.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + +