Use test level failure to indicate build failure

- Use single regex for package result line
- Add capturing of package build failure output
This commit is contained in:
Eyal Posener 2017-03-30 20:57:31 +03:00
parent 3c05bc8cb6
commit d10c0632c7
5 changed files with 153 additions and 83 deletions

View File

@ -37,7 +37,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if setExitCode && report.Failed() { if setExitCode && report.Failures() > 0 {
os.Exit(1) os.Exit(1)
} }
} }

View File

@ -27,9 +27,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 160, Time: 160,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestZ", Name: "TestZ",
@ -54,9 +53,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 151, Time: 151,
Result: parser.FAIL,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -86,9 +84,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 150, Time: 150,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -115,9 +112,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 160, Time: 160,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -142,9 +138,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 160, Time: 160,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -170,9 +165,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name1", Name: "package/name1",
Time: 160, Time: 160,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -189,9 +183,8 @@ var testCases = []TestCase{
}, },
}, },
{ {
Name: "package/name2", Name: "package/name2",
Time: 151, Time: 151,
Result: parser.FAIL,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -222,9 +215,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "test/package", Name: "test/package",
Time: 160, Time: 160,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -250,9 +242,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "github.com/dmitris/test-go-junit-report", Name: "github.com/dmitris/test-go-junit-report",
Time: 440, Time: 440,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestDoFoo", Name: "TestDoFoo",
@ -277,9 +268,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 160, Time: 160,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestZ", Name: "TestZ",
@ -305,9 +295,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package1/foo", Name: "package1/foo",
Time: 400, Time: 400,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestA", Name: "TestA",
@ -325,9 +314,8 @@ var testCases = []TestCase{
CoveragePct: "10.0", CoveragePct: "10.0",
}, },
{ {
Name: "package2/bar", Name: "package2/bar",
Time: 4200, Time: 4200,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestC", Name: "TestC",
@ -347,9 +335,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 50, Time: 50,
Result: parser.PASS,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -374,9 +361,8 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name",
Time: 50, Time: 50,
Result: parser.FAIL,
Tests: []*parser.Test{ Tests: []*parser.Test{
{ {
Name: "TestOne", Name: "TestOne",
@ -507,9 +493,54 @@ var testCases = []TestCase{
report: &parser.Report{ report: &parser.Report{
Packages: []parser.Package{ Packages: []parser.Package{
{ {
Name: "package/name", Name: "package/name/passing1",
Result: parser.FAIL, Time: 100,
Tests: []*parser.Test{}, Tests: []*parser.Test{
{
Name: "TestA",
Time: 100,
Result: parser.PASS,
Output: []string{},
},
},
},
{
Name: "package/name/passing2",
Time: 100,
Tests: []*parser.Test{
{
Name: "TestB",
Time: 100,
Result: parser.PASS,
Output: []string{},
},
},
},
{
Name: "package/name/failing1",
Tests: []*parser.Test{
{
Name: "build failed",
Time: 0,
Result: parser.FAIL,
Output: []string{
"failing1/failing_test.go:15: undefined: x",
},
},
},
},
{
Name: "package/name/failing2",
Tests: []*parser.Test{
{
Name: "build failed",
Time: 0,
Result: parser.FAIL,
Output: []string{
"failing2/another_failing_test.go:20: undefined: y",
},
},
},
}, },
}, },
}, },

View File

@ -29,7 +29,6 @@ type Package struct {
Time int Time int
Tests []*Test Tests []*Test
CoveragePct string CoveragePct string
Result Result
} }
// Test contains the results of a single test. // Test contains the results of a single test.
@ -41,11 +40,11 @@ type Test struct {
} }
var ( var (
regexStatus = regexp.MustCompile(`^\s*--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)$`) regexStatus = regexp.MustCompile(`^\s*--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)$`)
regexCoverage = regexp.MustCompile(`^coverage:\s+(\d+\.\d+)%\s+of\s+statements$`) 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)?$`) regexResult = regexp.MustCompile(`^(ok|FAIL)\s+([^ ]+)\s+(?:(\d+\.\d+)s|(\[build failed]))(?:\s+coverage:\s+(\d+\.\d+)%\sof\sstatements)?$`)
regexBuildFailure = regexp.MustCompile(`^FAIL\s+(.+)\s\[build failed]$`) regexOutput = regexp.MustCompile(`( )*\t(.*)`)
regexOutput = regexp.MustCompile(`( )*\t(.*)`) regexCapture = regexp.MustCompile(`^#\s(.+)`)
) )
// Parse parses go test output from reader r and returns a report with the // Parse parses go test output from reader r and returns a report with the
@ -68,8 +67,11 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
// coverage percentage report for current package // coverage percentage report for current package
var coveragePct string var coveragePct string
// whole package test result // stores mapping between package name and output of build failures
packageResult := FAIL var packageCaptures = map[string][]string{}
// the name of the package which it's build failure output is being captured
var capturedPackage string
// parse lines // parse lines
for { for {
@ -90,33 +92,39 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
Result: FAIL, Result: FAIL,
Output: make([]string, 0), Output: make([]string, 0),
}) })
} else if matches := regexResult.FindStringSubmatch(line); len(matches) == 5 {
if matches[4] != "" { // clear the current build package, so output lines won't be added to that build
coveragePct = matches[4] capturedPackage = ""
if matches[0] == "ok" { } else if matches := regexResult.FindStringSubmatch(line); len(matches) == 6 {
packageResult = PASS if matches[5] != "" {
} coveragePct = matches[5]
} }
// all tests in this package are finished // all tests in this package are finished
report.Packages = append(report.Packages, Package{ pack := Package{
Name: matches[2], Name: matches[2],
CoveragePct: coveragePct,
Time: parseTime(matches[3]), Time: parseTime(matches[3]),
Tests: tests, Tests: tests,
CoveragePct: coveragePct, }
Result: packageResult,
})
if matches[4] == "[build failed]" {
// the build of the package failed, inject a dummy test into the package
// which indicate about the failure and contain the failure description.
pack.Tests = []*Test{
{
Name: "build failed",
Result: FAIL,
Output: packageCaptures[matches[2]],
},
}
}
report.Packages = append(report.Packages, pack)
tests = make([]*Test, 0) tests = make([]*Test, 0)
coveragePct = "" coveragePct = ""
cur = "" cur = ""
testsTime = 0 testsTime = 0
} else if matches := regexBuildFailure.FindStringSubmatch(line); len(matches) == 2 {
report.Packages = append(report.Packages, Package{
Name: matches[1],
Result: FAIL,
})
} else if matches := regexStatus.FindStringSubmatch(line); len(matches) == 4 { } else if matches := regexStatus.FindStringSubmatch(line); len(matches) == 4 {
cur = matches[2] cur = matches[2]
test := findTest(tests, cur) test := findTest(tests, cur)
@ -148,6 +156,12 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
continue continue
} }
test.Output = append(test.Output, matches[2]) test.Output = append(test.Output, matches[2])
} else if matches := regexCapture.FindStringSubmatch(line); len(matches) == 2 {
// set the current build package
capturedPackage = matches[1]
} else if capturedPackage != "" {
// current line is build failure capture for the current built package
packageCaptures[capturedPackage] = append(packageCaptures[capturedPackage], line)
} }
} }
@ -158,7 +172,6 @@ func Parse(r io.Reader, pkgName string) (*Report, error) {
Time: testsTime, Time: testsTime,
Tests: tests, Tests: tests,
CoveragePct: coveragePct, CoveragePct: coveragePct,
Result: packageResult,
}) })
} }
@ -196,12 +209,3 @@ func (r *Report) Failures() int {
return count return count
} }
func (r *Report) Failed() bool {
for _, p := range r.Packages {
if p.Result == FAIL {
return true
}
}
return false
}

View File

@ -1,8 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuites> <testsuites>
<testsuite tests="0" failures="0" time="0.000" name="package/name"> <testsuite tests="1" failures="0" time="0.100" name="package/name/passing1">
<properties> <properties>
<property name="go.version" value="1.0"></property> <property name="go.version" value="1.0"></property>
</properties> </properties>
<testcase classname="passing1" name="TestA" time="0.100"></testcase>
</testsuite>
<testsuite tests="1" failures="0" time="0.100" name="package/name/passing2">
<properties>
<property name="go.version" value="1.0"></property>
</properties>
<testcase classname="passing2" name="TestB" time="0.100"></testcase>
</testsuite>
<testsuite tests="1" failures="1" time="0.000" name="package/name/failing1">
<properties>
<property name="go.version" value="1.0"></property>
</properties>
<testcase classname="failing1" name="build failed" time="0.000">
<failure message="Failed" type="">failing1/failing_test.go:15: undefined: x</failure>
</testcase>
</testsuite>
<testsuite tests="1" failures="1" time="0.000" name="package/name/failing2">
<properties>
<property name="go.version" value="1.0"></property>
</properties>
<testcase classname="failing2" name="build failed" time="0.000">
<failure message="Failed" type="">failing2/another_failing_test.go:20: undefined: y</failure>
</testcase>
</testsuite> </testsuite>
</testsuites> </testsuites>

View File

@ -1,2 +1,14 @@
package/name/file_test.go:9: undefined: x # package/name/failing1
FAIL package/name [build failed] failing1/failing_test.go:15: undefined: x
# package/name/failing2
failing2/another_failing_test.go:20: undefined: y
=== RUN TestA
--- PASS: TestA (0.10 seconds)
PASS
ok package/name/passing1 0.100s
=== RUN TestB
--- PASS: TestB (0.10 seconds)
PASS
ok package/name/passing2 0.100s
FAIL package/name/failing1 [build failed]
FAIL package/name/failing2 [build failed]