Merge pull request #83 from liggitt/test-output

Include test output and stdout in test failures
This commit is contained in:
Joël Stemmer 2019-01-06 14:36:47 +00:00 committed by GitHub
commit 55f6716122
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 372 additions and 0 deletions

View File

@ -1212,6 +1212,219 @@ var testCases = []TestCase{
},
},
},
{
// generated by running go test on https://gist.github.com/liggitt/09a021ccec988b19917e0c2d60a18ee9
name: "30-stdout.txt",
reportName: "30-report.xml",
report: &parser.Report{
Packages: []parser.Package{
{
Name: "package/name1",
Duration: 4567 * time.Millisecond,
Time: 4567,
Tests: []*parser.Test{
{
Name: "TestFailWithStdoutAndTestOutput",
Duration: 100 * time.Millisecond,
Time: 100,
Result: parser.FAIL,
Output: []string{
`multi`,
`line`,
`stdout`,
`single-line stdout`,
`example_test.go:13: single-line error`,
`example_test.go:14: multi`,
` line`,
` error`,
},
},
{
Name: "TestFailWithStdoutAndNoTestOutput",
Duration: 150 * time.Millisecond,
Time: 150,
Result: parser.FAIL,
Output: []string{
`multi`,
`line`,
`stdout`,
`single-line stdout`,
},
},
{
Name: "TestFailWithTestOutput",
Duration: 200 * time.Millisecond,
Time: 200,
Result: parser.FAIL,
Output: []string{
`example_test.go:26: single-line error`,
`example_test.go:27: multi`,
` line`,
` error`,
},
},
{
Name: "TestFailWithNoTestOutput",
Duration: 250 * time.Millisecond,
Time: 250,
Result: parser.FAIL,
Output: []string{},
},
{
Name: "TestPassWithStdoutAndTestOutput",
Duration: 300 * time.Millisecond,
Time: 300,
Result: parser.PASS,
Output: []string{
`multi`,
`line`,
`stdout`,
`single-line stdout`,
`example_test.go:39: single-line info`,
`example_test.go:40: multi`,
` line`,
` info`,
},
},
{
Name: "TestPassWithStdoutAndNoTestOutput",
Duration: 350 * time.Millisecond,
Time: 350,
Result: parser.PASS,
Output: []string{
`multi`,
`line`,
`stdout`,
`single-line stdout`,
},
},
{
Name: "TestPassWithTestOutput",
Duration: 400 * time.Millisecond,
Time: 400,
Result: parser.PASS,
Output: []string{
`example_test.go:51: single-line info`,
`example_test.go:52: multi`,
` line`,
` info`,
},
},
{
Name: "TestPassWithNoTestOutput",
Duration: 500 * time.Millisecond,
Time: 500,
Result: parser.PASS,
Output: []string{},
},
{
Name: "TestSubtests",
Duration: 2270 * time.Millisecond,
Time: 2270,
Result: parser.FAIL,
},
{
Name: "TestSubtests/TestFailWithStdoutAndTestOutput",
Duration: 100 * time.Millisecond,
Time: 100,
Result: parser.FAIL,
Output: []string{
`1 multi`,
`line`,
`stdout`,
`1 single-line stdout`,
`example_test.go:65: 1 single-line error`,
`example_test.go:66: 1 multi`,
` line`,
` error`,
},
},
{
Name: "TestSubtests/TestFailWithStdoutAndNoTestOutput",
Duration: 150 * time.Millisecond,
Time: 150,
Result: parser.FAIL,
Output: []string{
`2 multi`,
`line`,
`stdout`,
`2 single-line stdout`,
},
},
{
Name: "TestSubtests/TestFailWithTestOutput",
Duration: 200 * time.Millisecond,
Time: 200,
Result: parser.FAIL,
Output: []string{
`example_test.go:78: 3 single-line error`,
`example_test.go:79: 3 multi`,
` line`,
` error`,
},
},
{
Name: "TestSubtests/TestFailWithNoTestOutput",
Duration: 250 * time.Millisecond,
Time: 250,
Result: parser.FAIL,
Output: []string{},
},
{
Name: "TestSubtests/TestPassWithStdoutAndTestOutput",
Duration: 300 * time.Millisecond,
Time: 300,
Result: parser.PASS,
Output: []string{
`4 multi`,
`line`,
`stdout`,
`4 single-line stdout`,
`example_test.go:91: 4 single-line info`,
`example_test.go:92: 4 multi`,
` line`,
` info`,
},
},
{
Name: "TestSubtests/TestPassWithStdoutAndNoTestOutput",
Duration: 350 * time.Millisecond,
Time: 350,
Result: parser.PASS,
Output: []string{
`5 multi`,
`line`,
`stdout`,
`5 single-line stdout`,
},
},
{
Name: "TestSubtests/TestPassWithTestOutput",
Duration: 400 * time.Millisecond,
Time: 400,
Result: parser.PASS,
Output: []string{
`example_test.go:103: 6 single-line info`,
`example_test.go:104: 6 multi`,
` line`,
` info`,
},
},
{
Name: "TestSubtests/TestPassWithNoTestOutput",
Duration: 500 * time.Millisecond,
Time: 500,
Result: parser.PASS,
Output: []string{},
},
},
},
},
},
},
}
func TestParser(t *testing.T) {

View File

@ -43,6 +43,8 @@ type Test struct {
Result Result
Output []string
SubtestIndent string
// Time is deprecated, use Duration instead.
Time int // in milliseconds
}
@ -59,6 +61,7 @@ type Benchmark struct {
var (
regexStatus = regexp.MustCompile(`--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)`)
regexIndent = regexp.MustCompile(`^([ \t]+)---`)
regexCoverage = regexp.MustCompile(`^coverage:\s+(\d+\.\d+)%\s+of\s+statements(?:\sin\s.+)?$`)
regexResult = regexp.MustCompile(`^(ok|FAIL)\s+([^ ]+)\s+(?:(\d+\.\d+)s|\(cached\)|(\[\w+ failed]))(?:\s+coverage:\s+(\d+\.\d+)%\sof\sstatements(?:\sin\s.+)?)?$`)
// regexBenchmark captures 3-5 groups: benchmark name, number of times ran, ns/op (with or without decimal), B/op (optional), and allocs/op (optional).
@ -195,6 +198,11 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
} else {
test.Result = FAIL
}
if matches := regexIndent.FindStringSubmatch(line); len(matches) == 2 {
test.SubtestIndent = matches[1]
}
test.Output = buffers[cur]
test.Name = matches[2]
@ -225,6 +233,14 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
} else if !seenSummary {
// buffer anything else that we didn't recognize
buffers[cur] = append(buffers[cur], line)
// if we have a current test, also append to its output
test := findTest(tests, cur)
if test != nil {
if strings.HasPrefix(line, test.SubtestIndent+" ") {
test.Output = append(test.Output, strings.TrimPrefix(line, test.SubtestIndent+" "))
}
}
}
}

43
testdata/30-report.xml vendored Normal file
View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite tests="17" failures="9" time="4.567" name="package/name1">
<properties>
<property name="go.version" value="1.0"></property>
</properties>
<testcase classname="name1" name="TestFailWithStdoutAndTestOutput" time="0.100">
<failure message="Failed" type="">multi&#xA;line&#xA;stdout&#xA;single-line stdout&#xA;example_test.go:13: single-line error&#xA;example_test.go:14: multi&#xA; line&#xA; error</failure>
</testcase>
<testcase classname="name1" name="TestFailWithStdoutAndNoTestOutput" time="0.150">
<failure message="Failed" type="">multi&#xA;line&#xA;stdout&#xA;single-line stdout</failure>
</testcase>
<testcase classname="name1" name="TestFailWithTestOutput" time="0.200">
<failure message="Failed" type="">example_test.go:26: single-line error&#xA;example_test.go:27: multi&#xA; line&#xA; error</failure>
</testcase>
<testcase classname="name1" name="TestFailWithNoTestOutput" time="0.250">
<failure message="Failed" type=""></failure>
</testcase>
<testcase classname="name1" name="TestPassWithStdoutAndTestOutput" time="0.300"></testcase>
<testcase classname="name1" name="TestPassWithStdoutAndNoTestOutput" time="0.350"></testcase>
<testcase classname="name1" name="TestPassWithTestOutput" time="0.400"></testcase>
<testcase classname="name1" name="TestPassWithNoTestOutput" time="0.500"></testcase>
<testcase classname="name1" name="TestSubtests" time="2.270">
<failure message="Failed" type=""></failure>
</testcase>
<testcase classname="name1" name="TestSubtests/TestFailWithStdoutAndTestOutput" time="0.100">
<failure message="Failed" type="">1 multi&#xA;line&#xA;stdout&#xA;1 single-line stdout&#xA;example_test.go:65: 1 single-line error&#xA;example_test.go:66: 1 multi&#xA; line&#xA; error</failure>
</testcase>
<testcase classname="name1" name="TestSubtests/TestFailWithStdoutAndNoTestOutput" time="0.150">
<failure message="Failed" type="">2 multi&#xA;line&#xA;stdout&#xA;2 single-line stdout</failure>
</testcase>
<testcase classname="name1" name="TestSubtests/TestFailWithTestOutput" time="0.200">
<failure message="Failed" type="">example_test.go:78: 3 single-line error&#xA;example_test.go:79: 3 multi&#xA; line&#xA; error</failure>
</testcase>
<testcase classname="name1" name="TestSubtests/TestFailWithNoTestOutput" time="0.250">
<failure message="Failed" type=""></failure>
</testcase>
<testcase classname="name1" name="TestSubtests/TestPassWithStdoutAndTestOutput" time="0.300"></testcase>
<testcase classname="name1" name="TestSubtests/TestPassWithStdoutAndNoTestOutput" time="0.350"></testcase>
<testcase classname="name1" name="TestSubtests/TestPassWithTestOutput" time="0.400"></testcase>
<testcase classname="name1" name="TestSubtests/TestPassWithNoTestOutput" time="0.500"></testcase>
</testsuite>
</testsuites>

100
testdata/30-stdout.txt vendored Normal file
View File

@ -0,0 +1,100 @@
=== RUN TestFailWithStdoutAndTestOutput
multi
line
stdout
single-line stdout
--- FAIL: TestFailWithStdoutAndTestOutput (0.10s)
example_test.go:13: single-line error
example_test.go:14: multi
line
error
=== RUN TestFailWithStdoutAndNoTestOutput
multi
line
stdout
single-line stdout
--- FAIL: TestFailWithStdoutAndNoTestOutput (0.15s)
=== RUN TestFailWithTestOutput
--- FAIL: TestFailWithTestOutput (0.20s)
example_test.go:26: single-line error
example_test.go:27: multi
line
error
=== RUN TestFailWithNoTestOutput
--- FAIL: TestFailWithNoTestOutput (0.25s)
=== RUN TestPassWithStdoutAndTestOutput
multi
line
stdout
single-line stdout
--- PASS: TestPassWithStdoutAndTestOutput (0.30s)
example_test.go:39: single-line info
example_test.go:40: multi
line
info
=== RUN TestPassWithStdoutAndNoTestOutput
multi
line
stdout
single-line stdout
--- PASS: TestPassWithStdoutAndNoTestOutput (0.35s)
=== RUN TestPassWithTestOutput
--- PASS: TestPassWithTestOutput (0.40s)
example_test.go:51: single-line info
example_test.go:52: multi
line
info
=== RUN TestPassWithNoTestOutput
--- PASS: TestPassWithNoTestOutput (0.50s)
=== RUN TestSubtests
=== RUN TestSubtests/TestFailWithStdoutAndTestOutput
1 multi
line
stdout
1 single-line stdout
=== RUN TestSubtests/TestFailWithStdoutAndNoTestOutput
2 multi
line
stdout
2 single-line stdout
=== RUN TestSubtests/TestFailWithTestOutput
=== RUN TestSubtests/TestFailWithNoTestOutput
=== RUN TestSubtests/TestPassWithStdoutAndTestOutput
4 multi
line
stdout
4 single-line stdout
=== RUN TestSubtests/TestPassWithStdoutAndNoTestOutput
5 multi
line
stdout
5 single-line stdout
=== RUN TestSubtests/TestPassWithTestOutput
=== RUN TestSubtests/TestPassWithNoTestOutput
--- FAIL: TestSubtests (2.27s)
--- FAIL: TestSubtests/TestFailWithStdoutAndTestOutput (0.10s)
example_test.go:65: 1 single-line error
example_test.go:66: 1 multi
line
error
--- FAIL: TestSubtests/TestFailWithStdoutAndNoTestOutput (0.15s)
--- FAIL: TestSubtests/TestFailWithTestOutput (0.20s)
example_test.go:78: 3 single-line error
example_test.go:79: 3 multi
line
error
--- FAIL: TestSubtests/TestFailWithNoTestOutput (0.25s)
--- PASS: TestSubtests/TestPassWithStdoutAndTestOutput (0.30s)
example_test.go:91: 4 single-line info
example_test.go:92: 4 multi
line
info
--- PASS: TestSubtests/TestPassWithStdoutAndNoTestOutput (0.35s)
--- PASS: TestSubtests/TestPassWithTestOutput (0.40s)
example_test.go:103: 6 single-line info
example_test.go:104: 6 multi
line
info
--- PASS: TestSubtests/TestPassWithNoTestOutput (0.50s)
FAIL
FAIL package/name1 4.567s