package oscmd
import (
"regexp"
"github.com/openshift/origin/tools/junitreport/pkg/api"
"github.com/openshift/origin/tools/junitreport/pkg/parser/stack"
)
func newTestDataParser() stack.TestDataParser {
return &testDataParser{
// testStartPattern matches the test beginning bookend
testStartPattern: regexp.MustCompile(`=== BEGIN TEST CASE ===`),
// testDeclarationPattern matches the test declaration line, the full match is the name of the test being run
// as we're starting the test declaration line with a Unix path, misprinting a leading newline will cause this
// pattern to match the entire misprinted line
testDeclarationPattern: regexp.MustCompile(`.+:[0-9]+: executing '.+' expecting .+`),
// testConclusionPattern matches the test conclusion line, and contains the following submatches:
// - 1: test result
// - 2: test duration
// - 3: test name
// - 5: test result message
// In order to make this regex sane, we *require* a end-line anchor and therefore make us a little more fragile
// in the face of broken input
testConclusionPattern: regexp.MustCompile(`(SUCCESS|FAILURE) after ([0-9]+\.[0-9]+s): (.+:[0-9]+: executing '.*' expecting .*?)(: (.*))?$`),
// testEndPattern matches the test end bookend
testEndPattern: regexp.MustCompile(`=== END TEST CASE ===`),
}
}
type testDataParser struct {
testStartPattern *regexp.Regexp
testDeclarationPattern *regexp.Regexp
testConclusionPattern *regexp.Regexp
testEndPattern *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 lines
func (p *testDataParser) ExtractName(line string) (string, bool) {
// The test declaration pattern is technically a subset of the test conclusion pattern, and will therefore
// match anything the test declaration pattern matches. The match from the test conclusion pattern is more
// correct, if it exists, so we check the conclusion pattern first and return if we have a name candidate.
if matches := p.testConclusionPattern.FindStringSubmatch(line); len(matches) > 3 && len(matches[3]) > 0 {
return matches[3], true
}
if matches := p.testDeclarationPattern.FindStringSubmatch(line); len(matches) > 0 && len(matches[0]) > 0 {
return matches[0], 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.testConclusionPattern.FindStringSubmatch(line); len(matches) > 1 && len(matches[1]) > 0 {
switch matches[1] {
case "SUCCESS":
return api.TestResultPass, true
case "FAILURE":
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.testConclusionPattern.FindStringSubmatch(line); len(matches) > 2 && len(matches[2]) > 0 {
return matches[2], true
}
return "", false
}
// ExtractMessage extracts a message (e.g. for signalling why a failure or skip occurred) from a test output line
func (p *testDataParser) ExtractMessage(line string) (string, bool) {
if matches := p.testConclusionPattern.FindStringSubmatch(line); len(matches) > 5 && len(matches[5]) > 0 {
return matches[5], true
}
return "", false
}
// MarksCompletion determines if the line marks the completion of a test case
func (p *testDataParser) MarksCompletion(line string) bool {
return p.testEndPattern.MatchString(line)
}
func newTestSuiteDataParser() stack.TestSuiteDataParser {
return &testSuiteDataParser{
// suiteDeclarationPattern matches the suite declaration line and has the following submatches:
// - 1: suite name
suiteDeclarationPattern: regexp.MustCompile(`=== BEGIN TEST SUITE (.*) ===`),
// suiteConclusionPattern matches the suite conclusion line
suiteConclusionPattern: regexp.MustCompile(`=== END TEST SUITE ===`),
}
}
type testSuiteDataParser struct {
suiteDeclarationPattern *regexp.Regexp
suiteConclusionPattern *regexp.Regexp
}
// MarksBeginning determines if the line marks the beginning of a test suite
func (p *testSuiteDataParser) MarksBeginning(line string) bool {
return p.suiteDeclarationPattern.MatchString(line)
}
// ExtractName extracts the name of the test suite from a test output line
func (p *testSuiteDataParser) ExtractName(line string) (string, bool) {
if matches := p.suiteDeclarationPattern.FindStringSubmatch(line); len(matches) > 1 && len(matches[1]) > 0 {
return matches[1], true
}
return "", false
}
// ExtractProperties extracts any metadata properties of the test suite from a test output line
func (p *testSuiteDataParser) ExtractProperties(line string) (map[string]string, bool) {
// `os::cmd` suites cannot expose properties
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.suiteConclusionPattern.MatchString(line)
}