package gotest
import (
"regexp"
"github.com/openshift/origin/tools/junitreport/pkg/api"
)
func newTestDataParser() testDataParser {
return testDataParser{
// testStartPattern matches the line in verbose `go test` output that marks the declaration of a test.
// The first submatch of this regex is the name of the test
testStartPattern: regexp.MustCompile(`=== RUN\s+(.+)$`),
// testResultPattern matches the line in verbose `go test` output that marks the result of a test.
// The first submatch of this regex is the result of the test (PASS, FAIL, or SKIP)
// The second submatch of this regex is the name of the test
// The third submatch of this regex is the time taken in seconds for the test to finish
testResultPattern: regexp.MustCompile(`--- (PASS|FAIL|SKIP):\s+(.+)\s+\((\d+\.\d+)(s| seconds)\)`),
}
}
type testDataParser struct {
testStartPattern *regexp.Regexp
testResultPattern *regexp.Regexp
}
// MarksBeginning determines if the line marks the beginning of a test case
func (p *testDataParser) MarksBeginning(line string) bool {
return p.testStartPattern.MatchString(line)
}
// ExtractName extracts the name of the test case from test output line
func (p *testDataParser) ExtractName(line string) (string, bool) {
if matches := p.testStartPattern.FindStringSubmatch(line); len(matches) > 1 && len(matches[1]) > 0 {
return matches[1], true
}
if matches := p.testResultPattern.FindStringSubmatch(line); len(matches) > 2 && len(matches[2]) > 0 {
return matches[2], true
}
return "", false
}
// ExtractResult extracts the test result from a test output line
func (p *testDataParser) ExtractResult(line string) (api.TestResult, bool) {
if matches := p.testResultPattern.FindStringSubmatch(line); len(matches) > 1 && len(matches[1]) > 0 {
switch matches[1] {
case "PASS":
return api.TestResultPass, true
case "SKIP":
return api.TestResultSkip, true
case "FAIL":
return api.TestResultFail, true
}
}
return "", false
}
// ExtractDuration extracts the test duration from a test output line
func (p *testDataParser) ExtractDuration(line string) (string, bool) {
if matches := p.testResultPattern.FindStringSubmatch(line); len(matches) > 3 && len(matches[3]) > 0 {
return matches[3] + "s", true
}
return "", false
}
func newTestSuiteDataParser() testSuiteDataParser {
return testSuiteDataParser{
// coverageOutputPattern matches coverage output on a single line.
// The first submatch of this regex is the percent coverage
coverageOutputPattern: regexp.MustCompile(`coverage:\s+(\d+\.\d+)\% of statements`),
// packageResultPattern matches the `go test` output for the end of a package.
// The first submatch of this regex matches the result of the test (ok or FAIL)
// The second submatch of this regex matches the name of the package
// The third submatch of this regex matches the time taken in seconds for tests in the package to finish
// The sixth (optional) submatch of this regex is the percent coverage
packageResultPattern: regexp.MustCompile(`(ok|FAIL)\s+(.+)[\s\t]+(\d+\.\d+(s| seconds))([\s\t]+coverage:\s+(\d+\.\d+)\% of statements)?`),
}
}
type testSuiteDataParser struct {
coverageOutputPattern *regexp.Regexp
packageResultPattern *regexp.Regexp
}
// ExtractName extracts the name of the test suite from a test output line
func (p *testSuiteDataParser) ExtractName(line string) (string, bool) {
if matches := p.packageResultPattern.FindStringSubmatch(line); len(matches) > 2 && len(matches[2]) > 0 {
return matches[2], true
}
return "", false
}
// ExtractDuration extracts the package duration from a test output line
func (p *testSuiteDataParser) ExtractDuration(line string) (string, bool) {
if resultMatches := p.packageResultPattern.FindStringSubmatch(line); len(resultMatches) > 3 && len(resultMatches[3]) > 0 {
return resultMatches[3], true
}
return "", false
}
const (
coveragePropertyName string = "coverage.statements.pct"
)
// ExtractProperties extracts any metadata properties of the test suite from a test output line
func (p *testSuiteDataParser) ExtractProperties(line string) (map[string]string, bool) {
// the only test suite properties that Go testing can create are coverage values, which can either
// be present on their own line or in the package result line
if matches := p.coverageOutputPattern.FindStringSubmatch(line); len(matches) > 1 && len(matches[1]) > 0 {
return map[string]string{
coveragePropertyName: matches[1],
}, true
}
if resultMatches := p.packageResultPattern.FindStringSubmatch(line); len(resultMatches) > 6 && len(resultMatches[6]) > 0 {
return map[string]string{
coveragePropertyName: resultMatches[6],
}, true
}
return map[string]string{}, false
}
// MarksCompletion determines if the line marks the completion of a test suite
func (p *testSuiteDataParser) MarksCompletion(line string) bool {
return p.packageResultPattern.MatchString(line)
}