forked from thought-machine/please
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "Swap to go-junit-report for parsing go test output (thought-m…
…achine#982)" This reverts commit 13adc19.
- Loading branch information
1 parent
92a78a2
commit 3764e6a
Showing
5 changed files
with
130 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,143 @@ | ||
// Parser for output from Go's testing package. | ||
// | ||
// This is a fairly straightforward microformat so pretty easy to parse ourselves. | ||
// There's at least one package out there to convert it to JUnit XML but not worth | ||
// the complexity of getting that installed as a standalone tool. | ||
|
||
package test | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
|
||
parser "github.com/jstemmer/go-junit-report/parser" | ||
"time" | ||
|
||
"github.com/thought-machine/please/src/core" | ||
) | ||
|
||
func parseGoTestResults(data []byte) (core.TestSuite, error) { | ||
|
||
testPackage, err := parser.Parse(bytes.NewReader(data), "") | ||
|
||
if err != nil { | ||
return core.TestSuite{}, fmt.Errorf("Failed to parse go test output: %w", err) | ||
} | ||
|
||
results := fromGoJunitReport(testPackage) | ||
// Not sure what the -6 suffixes are about. | ||
var testStart = regexp.MustCompile(`^=== RUN (.*)(?:-6)?$`) | ||
var testResult = regexp.MustCompile(`^ *--- (PASS|FAIL|SKIP): (.*)(?:-6)? \(([0-9]+\.[0-9]+)s\)$`) | ||
|
||
return results, nil | ||
} | ||
|
||
// Conversion between a go-junit-report Report into a core.TestSuite. | ||
// A Package is mapped to TestSuite & Tests mapped onto testCases. | ||
func fromGoJunitReport(report *parser.Report) core.TestSuite { | ||
func parseGoTestResults(data []byte) (core.TestSuite, error) { | ||
results := core.TestSuite{} | ||
|
||
for _, pkg := range report.Packages { | ||
for _, test := range pkg.Tests { | ||
coreTestCase := core.TestCase{Name: test.Name} | ||
testOutput := strings.Join(test.Output, "\n") | ||
|
||
if test.Result == parser.PASS { | ||
coreTestCase.Executions = append(coreTestCase.Executions, core.TestExecution{ | ||
Stderr: testOutput, | ||
Duration: &test.Duration, | ||
lines := bytes.Split(data, []byte{'\n'}) | ||
testsStarted := map[string]bool{} | ||
var suiteDuration time.Duration | ||
testOutput := make([]string, 0) | ||
for i, line := range lines { | ||
testStartMatches := testStart.FindSubmatch(line) | ||
testResultMatches := testResult.FindSubmatch(line) | ||
if testStartMatches != nil { | ||
testsStarted[strings.TrimSpace(string(testStartMatches[1]))] = true | ||
} else if testResultMatches != nil { | ||
testName := strings.TrimSpace(string(testResultMatches[2])) | ||
if !testsStarted[testName] { | ||
continue | ||
} | ||
f, _ := strconv.ParseFloat(string(testResultMatches[3]), 64) | ||
duration := time.Duration(f * float64(time.Second)) | ||
suiteDuration += duration | ||
testCase := core.TestCase{ | ||
Name: testName, | ||
} | ||
if bytes.Equal(testResultMatches[1], []byte("PASS")) { | ||
testCase.Executions = append(testCase.Executions, core.TestExecution{ | ||
Duration: &duration, | ||
Stderr: strings.Join(testOutput, ""), | ||
}) | ||
} else if test.Result == parser.SKIP { | ||
coreTestCase.Executions = append(coreTestCase.Executions, core.TestExecution{ | ||
} else if bytes.Equal(testResultMatches[1], []byte("SKIP")) { | ||
// The skip message is found at the bottom of the test output segment. | ||
// Prior to Go 1.14 the test output segment follows the results line. | ||
// In Go 1.14 the test output segment sits between the start line and the results line. | ||
outputLines := getTestOutputLines(i, lines) | ||
skipMessage := "" | ||
if len(outputLines) > 0 { | ||
skipMessage = strings.TrimSpace(outputLines[len(outputLines)-1]) | ||
} | ||
|
||
testCase.Executions = append(testCase.Executions, core.TestExecution{ | ||
Skip: &core.TestResultSkip{ | ||
// Given the possibility of test setup, teardowns & custom logging, we can't do anything | ||
// more targeted than using the whole test output as the skip message. | ||
Message: testOutput, | ||
Message: skipMessage, | ||
}, | ||
Stderr: testOutput, | ||
Duration: &test.Duration, | ||
Stderr: strings.Join(testOutput, ""), | ||
Duration: &duration, | ||
}) | ||
} else { | ||
// A "FAIL" result | ||
coreTestCase.Executions = append(coreTestCase.Executions, core.TestExecution{ | ||
|
||
outputLines := getTestOutputLines(i, lines) | ||
|
||
output := strings.Join(outputLines, "\n") | ||
testCase.Executions = append(testCase.Executions, core.TestExecution{ | ||
Failure: &core.TestResultFailure{ | ||
Traceback: testOutput, | ||
Traceback: output, | ||
}, | ||
Stderr: testOutput, | ||
Duration: &test.Duration, | ||
Stderr: strings.Join(testOutput, ""), | ||
Duration: &duration, | ||
}) | ||
} | ||
results.TestCases = append(results.TestCases, coreTestCase) | ||
results.TestCases = append(results.TestCases, testCase) | ||
testOutput = make([]string, 0) | ||
} else if bytes.Equal(line, []byte("PASS")) { | ||
// Do nothing, all's well. | ||
} else if bytes.Equal(line, []byte("FAIL")) { | ||
if results.Failures() == 0 { | ||
return results, fmt.Errorf("Test indicated final failure but no failures found yet") | ||
} | ||
} else { | ||
testOutput = append(testOutput, string(line), "\n") | ||
} | ||
results.Duration += pkg.Duration | ||
} | ||
return results | ||
results.Duration = suiteDuration | ||
return results, nil | ||
} | ||
|
||
func getTestOutputLines(currentIndex int, lines [][]byte) []string { | ||
if resultLooksPriorGo114(currentIndex, lines) { | ||
return getPostResultOutput(currentIndex, lines) | ||
} | ||
return getPreResultOutput(currentIndex, lines) | ||
} | ||
|
||
// Go test output looks prior to 114 if the previous line matches against a start test block. | ||
// Only fully applicable for failing and skipped tests as a message may not | ||
// appear for passed tests. | ||
func resultLooksPriorGo114(currentIndex int, lines [][]byte) bool { | ||
if currentIndex == 0 { | ||
return false | ||
} | ||
|
||
prevLine := lines[currentIndex-1] | ||
prevLineMatchesStart := testStart.FindSubmatch(prevLine) | ||
|
||
return prevLineMatchesStart != nil | ||
} | ||
|
||
// Get the output for Go test output prior to Go 1.14 | ||
func getPostResultOutput(resultsIndex int, lines [][]byte) []string { | ||
output := []string{} | ||
for j := resultsIndex + 1; j < len(lines) && !lineMatchesRunOrResultsLine(lines[j]); j++ { | ||
output = append(output, string(lines[j])) | ||
} | ||
|
||
return output | ||
} | ||
|
||
// Get output for Go tests output after Go 1.14 | ||
func getPreResultOutput(resultsIndex int, lines [][]byte) []string { | ||
output := []string{} | ||
for j := resultsIndex - 1; j > 0 && !lineMatchesRunOrResultsLine(lines[j]); j-- { | ||
output = append([]string{string(lines[j])}, output...) | ||
} | ||
return output | ||
} | ||
|
||
func lineMatchesRunOrResultsLine(line []byte) bool { | ||
|
||
testStartMatches := testStart.FindSubmatch(line) | ||
matchesRunLine := (testStartMatches != nil) | ||
|
||
return matchesRunLine || bytes.Equal(line, []byte("PASS")) || bytes.Equal(line, []byte("FAIL")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
=== RUN TestA | ||
--- PASS: TestB (0.00s) | ||
=== RUN TestB | ||
--- PASS: TestA (0.00s) | ||
FAIL | ||
coverage: 22.9% of statements |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters