mirror of
https://github.com/jstemmer/go-junit-report.git
synced 2025-04-05 21:18:08 -05:00

* Subtests and nested subtests have spaces at the beginning of their PASS statements. This has been added to the regexp.
180 lines
3.7 KiB
Go
180 lines
3.7 KiB
Go
package parser
|
|
|
|
import (
|
|
"bufio"
|
|
"io"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Result represents a test result.
|
|
type Result int
|
|
|
|
// Test result constants
|
|
const (
|
|
PASS Result = iota
|
|
FAIL
|
|
SKIP
|
|
)
|
|
|
|
// Report is a collection of package tests.
|
|
type Report struct {
|
|
Packages []Package
|
|
}
|
|
|
|
// Package contains the test results of a single package.
|
|
type Package struct {
|
|
Name string
|
|
Time int
|
|
Tests []*Test
|
|
CoveragePct string
|
|
}
|
|
|
|
// Test contains the results of a single test.
|
|
type Test struct {
|
|
Name string
|
|
Time int
|
|
Result Result
|
|
Output []string
|
|
}
|
|
|
|
var (
|
|
regexStatus = regexp.MustCompile(`^\s*--- (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
|
|
// results. An optional pkgName can be given, which is used in case a package
|
|
// result line is missing.
|
|
func Parse(r io.Reader, pkgName string) (*Report, error) {
|
|
reader := bufio.NewReader(r)
|
|
|
|
report := &Report{make([]Package, 0)}
|
|
|
|
// keep track of tests we find
|
|
var tests []*Test
|
|
|
|
// sum of tests' time, use this if current test has no result line (when it is compiled test)
|
|
testsTime := 0
|
|
|
|
// current test
|
|
var cur string
|
|
|
|
// coverage percentage report for current package
|
|
var coveragePct string
|
|
|
|
// parse lines
|
|
for {
|
|
l, _, err := reader.ReadLine()
|
|
if err != nil && err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
line := string(l)
|
|
|
|
if strings.HasPrefix(line, "=== RUN ") {
|
|
// new test
|
|
cur = strings.TrimSpace(line[8:])
|
|
tests = append(tests, &Test{
|
|
Name: cur,
|
|
Result: FAIL,
|
|
Output: make([]string, 0),
|
|
})
|
|
} 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,
|
|
CoveragePct: coveragePct,
|
|
})
|
|
|
|
tests = make([]*Test, 0)
|
|
coveragePct = ""
|
|
cur = ""
|
|
testsTime = 0
|
|
} else if matches := regexStatus.FindStringSubmatch(line); len(matches) == 4 {
|
|
cur = matches[2]
|
|
test := findTest(tests, cur)
|
|
if test == nil {
|
|
continue
|
|
}
|
|
|
|
// test status
|
|
if matches[1] == "PASS" {
|
|
test.Result = PASS
|
|
} else if matches[1] == "SKIP" {
|
|
test.Result = SKIP
|
|
} else {
|
|
test.Result = FAIL
|
|
}
|
|
|
|
test.Name = matches[2]
|
|
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)
|
|
if test == nil {
|
|
continue
|
|
}
|
|
test.Output = append(test.Output, line[1:])
|
|
}
|
|
}
|
|
|
|
if len(tests) > 0 {
|
|
// no result line found
|
|
report.Packages = append(report.Packages, Package{
|
|
Name: pkgName,
|
|
Time: testsTime,
|
|
Tests: tests,
|
|
CoveragePct: coveragePct,
|
|
})
|
|
}
|
|
|
|
return report, nil
|
|
}
|
|
|
|
func parseTime(time string) int {
|
|
t, err := strconv.Atoi(strings.Replace(time, ".", "", -1))
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return t
|
|
}
|
|
|
|
func findTest(tests []*Test, name string) *Test {
|
|
for i := 0; i < len(tests); i++ {
|
|
if tests[i].Name == name {
|
|
return tests[i]
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Failures counts the number of failed tests in this report
|
|
func (r *Report) Failures() int {
|
|
count := 0
|
|
|
|
for _, p := range r.Packages {
|
|
for _, t := range p.Tests {
|
|
if t.Result == FAIL {
|
|
count++
|
|
}
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|