diff --git a/go-junit-report.go b/go-junit-report.go index 4ab76f2..bf388b0 100644 --- a/go-junit-report.go +++ b/go-junit-report.go @@ -37,7 +37,7 @@ func main() { os.Exit(1) } - if setExitCode && report.Failed() { + if setExitCode && report.Failures() > 0 { os.Exit(1) } } diff --git a/go-junit-report_test.go b/go-junit-report_test.go index 8649512..fc93779 100644 --- a/go-junit-report_test.go +++ b/go-junit-report_test.go @@ -27,9 +27,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, - Result: parser.PASS, + Name: "package/name", + Time: 160, Tests: []*parser.Test{ { Name: "TestZ", @@ -54,9 +53,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 151, - Result: parser.FAIL, + Name: "package/name", + Time: 151, Tests: []*parser.Test{ { Name: "TestOne", @@ -86,9 +84,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 150, - Result: parser.PASS, + Name: "package/name", + Time: 150, Tests: []*parser.Test{ { Name: "TestOne", @@ -115,9 +112,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, - Result: parser.PASS, + Name: "package/name", + Time: 160, Tests: []*parser.Test{ { Name: "TestOne", @@ -142,9 +138,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, - Result: parser.PASS, + Name: "package/name", + Time: 160, Tests: []*parser.Test{ { Name: "TestOne", @@ -170,9 +165,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name1", - Time: 160, - Result: parser.PASS, + Name: "package/name1", + Time: 160, Tests: []*parser.Test{ { Name: "TestOne", @@ -189,9 +183,8 @@ var testCases = []TestCase{ }, }, { - Name: "package/name2", - Time: 151, - Result: parser.FAIL, + Name: "package/name2", + Time: 151, Tests: []*parser.Test{ { Name: "TestOne", @@ -222,9 +215,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "test/package", - Time: 160, - Result: parser.PASS, + Name: "test/package", + Time: 160, Tests: []*parser.Test{ { Name: "TestOne", @@ -250,9 +242,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "github.com/dmitris/test-go-junit-report", - Time: 440, - Result: parser.PASS, + Name: "github.com/dmitris/test-go-junit-report", + Time: 440, Tests: []*parser.Test{ { Name: "TestDoFoo", @@ -277,9 +268,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, - Result: parser.PASS, + Name: "package/name", + Time: 160, Tests: []*parser.Test{ { Name: "TestZ", @@ -305,9 +295,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package1/foo", - Time: 400, - Result: parser.PASS, + Name: "package1/foo", + Time: 400, Tests: []*parser.Test{ { Name: "TestA", @@ -325,9 +314,8 @@ var testCases = []TestCase{ CoveragePct: "10.0", }, { - Name: "package2/bar", - Time: 4200, - Result: parser.PASS, + Name: "package2/bar", + Time: 4200, Tests: []*parser.Test{ { Name: "TestC", @@ -347,9 +335,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 50, - Result: parser.PASS, + Name: "package/name", + Time: 50, Tests: []*parser.Test{ { Name: "TestOne", @@ -374,9 +361,8 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 50, - Result: parser.FAIL, + Name: "package/name", + Time: 50, Tests: []*parser.Test{ { Name: "TestOne", @@ -507,9 +493,54 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Result: parser.FAIL, - Tests: []*parser.Test{}, + Name: "package/name/passing1", + Time: 100, + 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", + }, + }, + }, }, }, }, diff --git a/parser/parser.go b/parser/parser.go index f141f1c..76b5254 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -29,7 +29,6 @@ type Package struct { Time int Tests []*Test CoveragePct string - Result Result } // Test contains the results of a single test. @@ -41,11 +40,11 @@ type Test struct { } var ( - regexStatus = regexp.MustCompile(`^\s*--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)$`) - 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)?$`) - regexBuildFailure = regexp.MustCompile(`^FAIL\s+(.+)\s\[build failed]$`) - regexOutput = regexp.MustCompile(`( )*\t(.*)`) + regexStatus = regexp.MustCompile(`^\s*--- (PASS|FAIL|SKIP): (.+) \((\d+\.\d+)(?: seconds|s)\)$`) + regexCoverage = regexp.MustCompile(`^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)?$`) + regexOutput = regexp.MustCompile(`( )*\t(.*)`) + regexCapture = regexp.MustCompile(`^#\s(.+)`) ) // 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 var coveragePct string - // whole package test result - packageResult := FAIL + // stores mapping between package name and output of build failures + var packageCaptures = map[string][]string{} + + // the name of the package which it's build failure output is being captured + var capturedPackage string // parse lines for { @@ -90,33 +92,39 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { Result: FAIL, Output: make([]string, 0), }) - } else if matches := regexResult.FindStringSubmatch(line); len(matches) == 5 { - if matches[4] != "" { - coveragePct = matches[4] - if matches[0] == "ok" { - packageResult = PASS - } + + // clear the current build package, so output lines won't be added to that build + capturedPackage = "" + } else if matches := regexResult.FindStringSubmatch(line); len(matches) == 6 { + if matches[5] != "" { + coveragePct = matches[5] } // all tests in this package are finished - report.Packages = append(report.Packages, Package{ + pack := Package{ Name: matches[2], + CoveragePct: coveragePct, Time: parseTime(matches[3]), 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) coveragePct = "" cur = "" 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 { cur = matches[2] test := findTest(tests, cur) @@ -148,6 +156,12 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { continue } 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, Tests: tests, CoveragePct: coveragePct, - Result: packageResult, }) } @@ -196,12 +209,3 @@ func (r *Report) Failures() int { return count } - -func (r *Report) Failed() bool { - for _, p := range r.Packages { - if p.Result == FAIL { - return true - } - } - return false -} diff --git a/tests/13-report.xml b/tests/13-report.xml index 1d6d23a..19c988f 100644 --- a/tests/13-report.xml +++ b/tests/13-report.xml @@ -1,8 +1,31 @@ - + + + + + + + + + + + + + + + failing1/failing_test.go:15: undefined: x + + + + + + + + failing2/another_failing_test.go:20: undefined: y + diff --git a/tests/13-syntax-error.txt b/tests/13-syntax-error.txt index 0a4d5ce..ac47126 100644 --- a/tests/13-syntax-error.txt +++ b/tests/13-syntax-error.txt @@ -1,2 +1,14 @@ -package/name/file_test.go:9: undefined: x -FAIL package/name [build failed] +# package/name/failing1 +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]