From 3c05bc8cb6a5d98e396e65746816bfab56ddc376 Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Sun, 22 Jan 2017 02:12:25 +0200 Subject: [PATCH 1/3] Fail package in case of build error In case of build error, the unittest fail, but the junit-report with the set-exit-code flag on returns status code 0. This commit will make it return the right status code. --- go-junit-report.go | 2 +- go-junit-report_test.go | 83 ++++++++++++++++++++++++++------------- parser/parser.go | 33 ++++++++++++++-- tests/13-report.xml | 8 ++++ tests/13-syntax-error.txt | 2 + 5 files changed, 95 insertions(+), 33 deletions(-) create mode 100644 tests/13-report.xml create mode 100644 tests/13-syntax-error.txt diff --git a/go-junit-report.go b/go-junit-report.go index bf388b0..4ab76f2 100644 --- a/go-junit-report.go +++ b/go-junit-report.go @@ -37,7 +37,7 @@ func main() { os.Exit(1) } - if setExitCode && report.Failures() > 0 { + if setExitCode && report.Failed() { os.Exit(1) } } diff --git a/go-junit-report_test.go b/go-junit-report_test.go index 4b23b44..8649512 100644 --- a/go-junit-report_test.go +++ b/go-junit-report_test.go @@ -27,8 +27,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, + Name: "package/name", + Time: 160, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestZ", @@ -53,8 +54,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 151, + Name: "package/name", + Time: 151, + Result: parser.FAIL, Tests: []*parser.Test{ { Name: "TestOne", @@ -84,8 +86,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 150, + Name: "package/name", + Time: 150, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestOne", @@ -112,8 +115,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, + Name: "package/name", + Time: 160, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestOne", @@ -138,8 +142,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, + Name: "package/name", + Time: 160, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestOne", @@ -165,8 +170,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name1", - Time: 160, + Name: "package/name1", + Time: 160, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestOne", @@ -183,8 +189,9 @@ var testCases = []TestCase{ }, }, { - Name: "package/name2", - Time: 151, + Name: "package/name2", + Time: 151, + Result: parser.FAIL, Tests: []*parser.Test{ { Name: "TestOne", @@ -215,8 +222,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "test/package", - Time: 160, + Name: "test/package", + Time: 160, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestOne", @@ -242,8 +250,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "github.com/dmitris/test-go-junit-report", - Time: 440, + Name: "github.com/dmitris/test-go-junit-report", + Time: 440, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestDoFoo", @@ -268,8 +277,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 160, + Name: "package/name", + Time: 160, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestZ", @@ -295,8 +305,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package1/foo", - Time: 400, + Name: "package1/foo", + Time: 400, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestA", @@ -314,8 +325,9 @@ var testCases = []TestCase{ CoveragePct: "10.0", }, { - Name: "package2/bar", - Time: 4200, + Name: "package2/bar", + Time: 4200, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestC", @@ -335,8 +347,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 50, + Name: "package/name", + Time: 50, + Result: parser.PASS, Tests: []*parser.Test{ { Name: "TestOne", @@ -361,8 +374,9 @@ var testCases = []TestCase{ report: &parser.Report{ Packages: []parser.Package{ { - Name: "package/name", - Time: 50, + Name: "package/name", + Time: 50, + Result: parser.FAIL, Tests: []*parser.Test{ { Name: "TestOne", @@ -487,6 +501,19 @@ var testCases = []TestCase{ }, }, }, + { + name: "13-syntax-error.txt", + reportName: "13-report.xml", + report: &parser.Report{ + Packages: []parser.Package{ + { + Name: "package/name", + Result: parser.FAIL, + Tests: []*parser.Test{}, + }, + }, + }, + }, } func TestParser(t *testing.T) { diff --git a/parser/parser.go b/parser/parser.go index 88da817..f141f1c 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -29,6 +29,7 @@ type Package struct { Time int Tests []*Test CoveragePct string + Result Result } // Test contains the results of a single test. @@ -40,10 +41,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)?$`) - 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(?:\s+coverage:\s+(\d+\.\d+)%\s+of\s+statements)?$`) + regexBuildFailure = regexp.MustCompile(`^FAIL\s+(.+)\s\[build failed]$`) + regexOutput = regexp.MustCompile(`( )*\t(.*)`) ) // Parse parses go test output from reader r and returns a report with the @@ -66,6 +68,9 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { // coverage percentage report for current package var coveragePct string + // whole package test result + packageResult := FAIL + // parse lines for { l, _, err := reader.ReadLine() @@ -88,6 +93,9 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { } else if matches := regexResult.FindStringSubmatch(line); len(matches) == 5 { if matches[4] != "" { coveragePct = matches[4] + if matches[0] == "ok" { + packageResult = PASS + } } // all tests in this package are finished @@ -96,12 +104,19 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { Time: parseTime(matches[3]), Tests: tests, CoveragePct: coveragePct, + Result: packageResult, }) 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) @@ -143,6 +158,7 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { Time: testsTime, Tests: tests, CoveragePct: coveragePct, + Result: packageResult, }) } @@ -180,3 +196,12 @@ 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 new file mode 100644 index 0000000..1d6d23a --- /dev/null +++ b/tests/13-report.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/13-syntax-error.txt b/tests/13-syntax-error.txt new file mode 100644 index 0000000..0a4d5ce --- /dev/null +++ b/tests/13-syntax-error.txt @@ -0,0 +1,2 @@ +package/name/file_test.go:9: undefined: x +FAIL package/name [build failed] From d10c0632c7295a06f477b447d1d719e319df08e1 Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Thu, 30 Mar 2017 20:57:31 +0300 Subject: [PATCH 2/3] Use test level failure to indicate build failure - Use single regex for package result line - Add capturing of package build failure output --- go-junit-report.go | 2 +- go-junit-report_test.go | 121 ++++++++++++++++++++++++-------------- parser/parser.go | 72 ++++++++++++----------- tests/13-report.xml | 25 +++++++- tests/13-syntax-error.txt | 16 ++++- 5 files changed, 153 insertions(+), 83 deletions(-) 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] From e9729a3e7ecf900323bd8f9a685d0b23ff5df35b Mon Sep 17 00:00:00 2001 From: Eyal Posener Date: Sat, 1 Apr 2017 08:36:21 +0300 Subject: [PATCH 3/3] Fix CR comments * Change regexpCapture to HasPrefix function * Use same addition pattern of the faild build test * Fix readme --- README.md | 2 +- parser/parser.go | 37 ++++++++++++++++--------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index dad712f..0f3ccd3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ go-junit-report reads the `go test` verbose output from standard in and writes junit compatible XML to standard out. ```bash -go test -v | go-junit-report > report.xml +go test -v 2>&1 | go-junit-report > report.xml ``` [travis-badge]: https://travis-ci.org/jstemmer/go-junit-report.svg diff --git a/parser/parser.go b/parser/parser.go index 76b5254..aaaaff3 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -44,7 +44,6 @@ var ( 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 @@ -99,28 +98,24 @@ func Parse(r io.Reader, pkgName string) (*Report, error) { if matches[5] != "" { coveragePct = matches[5] } - - // all tests in this package are finished - pack := Package{ - Name: matches[2], - CoveragePct: coveragePct, - Time: parseTime(matches[3]), - Tests: tests, - } - 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]], - }, - } + tests = append(tests, &Test{ + Name: "build failed", + Result: FAIL, + Output: packageCaptures[matches[2]], + }) } - report.Packages = append(report.Packages, pack) + // all tests in this package are finished + report.Packages = append(report.Packages, Package{ + Name: matches[2], + Time: parseTime(matches[3]), + Tests: tests, + CoveragePct: coveragePct, + }) + tests = make([]*Test, 0) coveragePct = "" cur = "" @@ -156,9 +151,9 @@ 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 strings.HasPrefix(line, "# ") { + // indicates a capture of build output of a package. set the current build package. + capturedPackage = line[2:] } else if capturedPackage != "" { // current line is build failure capture for the current built package packageCaptures[capturedPackage] = append(packageCaptures[capturedPackage], line)