Skip to content

Commit

Permalink
Merge pull request #288 from flowchartsman/junit_hide_empty
Browse files Browse the repository at this point in the history
Hide empty packages on junit output
  • Loading branch information
dnephin authored Dec 10, 2022
2 parents f9eefe9 + fb867e3 commit 3c0fb00
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 1 deletion.
8 changes: 8 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,11 @@ func (s *stringSlice) Set(raw string) error {
func (s *stringSlice) Type() string {
return "list"
}

func truthyFlag(s string) bool {
switch strings.ToLower(s) {
case "true", "yes", "1":
return true
}
return false
}
1 change: 1 addition & 0 deletions cmd/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func writeJUnitFile(opts *options, execution *testjson.Execution) error {
ProjectName: opts.junitProjectName,
FormatTestSuiteName: opts.junitTestSuiteNameFormat.Value(),
FormatTestCaseClassname: opts.junitTestCaseClassnameFormat.Value(),
HideEmptyPackages: opts.junitHideEmptyPackages,
})
}

Expand Down
4 changes: 4 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ func setupFlags(name string) (*pflag.FlagSet, *options) {
flags.StringVar(&opts.junitProjectName, "junitfile-project-name",
lookEnvWithDefault("GOTESTSUM_JUNITFILE_PROJECT_NAME", ""),
"name of the project used in the junit.xml file")
flags.BoolVar(&opts.junitHideEmptyPackages, "junitfile-hide-empty-pkg",
truthyFlag(lookEnvWithDefault("GOTESTSUM_JUNIT_HIDE_EMPTY_PKG", "")),
"omit packages with no tests from the junit.xml file")

flags.IntVar(&opts.rerunFailsMaxAttempts, "rerun-fails", 0,
"rerun failed tests until they all pass, or attempts exceeds maximum. Defaults to max 2 reruns when enabled.")
Expand Down Expand Up @@ -160,6 +163,7 @@ type options struct {
junitTestSuiteNameFormat *junitFieldFormatValue
junitTestCaseClassnameFormat *junitFieldFormatValue
junitProjectName string
junitHideEmptyPackages bool
rerunFailsMaxAttempts int
rerunFailsMaxInitialFailures int
rerunFailsReportFile string
Expand Down
1 change: 1 addition & 0 deletions cmd/testdata/gotestsum-help-text
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Flags:
--hide-summary summary hide sections of the summary: skipped,failed,errors,output (default none)
--jsonfile string write all TestEvents to file
--junitfile string write a JUnit XML file
--junitfile-hide-empty-pkg omit packages with no tests from the junit.xml file
--junitfile-project-name string name of the project used in the junit.xml file
--junitfile-testcase-classname field-format format the testcase classname field as: full, relative, short (default full)
--junitfile-testsuite-name field-format format the testsuite name field as: full, relative, short (default full)
Expand Down
4 changes: 4 additions & 0 deletions internal/junitxml/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type Config struct {
ProjectName string
FormatTestSuiteName FormatFunc
FormatTestCaseClassname FormatFunc
HideEmptyPackages bool
// This is used for tests to have a consistent timestamp
customTimestamp string
customElapsed string
Expand Down Expand Up @@ -104,6 +105,9 @@ func generate(exec *testjson.Execution, cfg Config) JUnitTestSuites {
}
for _, pkgname := range exec.Packages() {
pkg := exec.Package(pkgname)
if cfg.HideEmptyPackages && pkg.IsEmpty() {
continue
}
junitpkg := JUnitTestSuite{
Name: cfg.FormatTestSuiteName(pkgname),
Tests: pkg.Total,
Expand Down
15 changes: 15 additions & 0 deletions internal/junitxml/report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ func TestWrite(t *testing.T) {
golden.Assert(t, out.String(), "junitxml-report.golden")
}

func TestWrite_HideEmptyPackages(t *testing.T) {
out := new(bytes.Buffer)
exec := createExecution(t)

env.Patch(t, "GOVERSION", "go7.7.7")
err := Write(out, exec, Config{
ProjectName: "test",
HideEmptyPackages: true,
customTimestamp: new(time.Time).Format(time.RFC3339),
customElapsed: "2.1",
})
assert.NilError(t, err)
golden.Assert(t, out.String(), "junitxml-report-skip-empty.golden")
}

func createExecution(t *testing.T) *testjson.Execution {
exec, err := testjson.ScanTestOutput(testjson.ScanConfig{
Stdout: readTestData(t, "out"),
Expand Down
119 changes: 119 additions & 0 deletions internal/junitxml/testdata/junitxml-report-skip-empty.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuites name="test" tests="59" failures="13" errors="1" time="2.1">
<testsuite tests="0" failures="0" time="0.001000" name="gotest.tools/gotestsum/testjson/internal/badmain" timestamp="0001-01-01T00:00:00Z">
<properties>
<property name="go.version" value="go7.7.7"></property>
</properties>
<testcase classname="" name="TestMain" time="0.000000">
<failure message="Failed" type="">sometimes main can exit 2&#xA;FAIL&#x9;gotest.tools/gotestsum/testjson/internal/badmain&#x9;0.001s&#xA;</failure>
</testcase>
</testsuite>
<testsuite tests="18" failures="0" time="0.000000" name="gotest.tools/gotestsum/testjson/internal/good" timestamp="0001-01-01T00:00:00Z">
<properties>
<property name="go.version" value="go7.7.7"></property>
</properties>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestSkipped" time="0.000000">
<skipped message="=== RUN TestSkipped&#xA; good_test.go:23: &#xA;--- SKIP: TestSkipped (0.00s)&#xA;"></skipped>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestSkippedWitLog" time="0.000000">
<skipped message="=== RUN TestSkippedWitLog&#xA; good_test.go:27: the skip message&#xA;--- SKIP: TestSkippedWitLog (0.00s)&#xA;"></skipped>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestPassed" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestPassedWithLog" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestPassedWithStdout" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestWithStderr" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/a/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/a" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/b/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/b" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/c/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/c" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/d/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess/d" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestNestedSuccess" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestParallelTheFirst" time="0.010000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestParallelTheThird" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/good" name="TestParallelTheSecond" time="0.010000"></testcase>
</testsuite>
<testsuite tests="12" failures="8" time="0.020000" name="gotest.tools/gotestsum/testjson/internal/parallelfails" timestamp="0001-01-01T00:00:00Z">
<properties>
<property name="go.version" value="go7.7.7"></property>
</properties>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestNestedParallelFailures/a" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedParallelFailures/a&#xA;=== PAUSE TestNestedParallelFailures/a&#xA;=== CONT TestNestedParallelFailures/a&#xA; fails_test.go:50: failed sub a&#xA; --- FAIL: TestNestedParallelFailures/a (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestNestedParallelFailures/d" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedParallelFailures/d&#xA;=== PAUSE TestNestedParallelFailures/d&#xA;=== CONT TestNestedParallelFailures/d&#xA; fails_test.go:50: failed sub d&#xA; --- FAIL: TestNestedParallelFailures/d (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestNestedParallelFailures/c" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedParallelFailures/c&#xA;=== PAUSE TestNestedParallelFailures/c&#xA;=== CONT TestNestedParallelFailures/c&#xA; fails_test.go:50: failed sub c&#xA; --- FAIL: TestNestedParallelFailures/c (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestNestedParallelFailures/b" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedParallelFailures/b&#xA;=== PAUSE TestNestedParallelFailures/b&#xA;=== CONT TestNestedParallelFailures/b&#xA; fails_test.go:50: failed sub b&#xA; --- FAIL: TestNestedParallelFailures/b (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestNestedParallelFailures" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedParallelFailures&#xA;--- FAIL: TestNestedParallelFailures (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestParallelTheFirst" time="0.010000">
<failure message="Failed" type="">=== RUN TestParallelTheFirst&#xA;=== PAUSE TestParallelTheFirst&#xA;=== CONT TestParallelTheFirst&#xA; fails_test.go:29: failed the first&#xA;--- FAIL: TestParallelTheFirst (0.01s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestParallelTheThird" time="0.000000">
<failure message="Failed" type="">=== RUN TestParallelTheThird&#xA;=== PAUSE TestParallelTheThird&#xA;=== CONT TestParallelTheThird&#xA; fails_test.go:41: failed the third&#xA;--- FAIL: TestParallelTheThird (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestParallelTheSecond" time="0.010000">
<failure message="Failed" type="">=== RUN TestParallelTheSecond&#xA;=== PAUSE TestParallelTheSecond&#xA;=== CONT TestParallelTheSecond&#xA; fails_test.go:35: failed the second&#xA;--- FAIL: TestParallelTheSecond (0.01s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestPassed" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestPassedWithLog" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestPassedWithStdout" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/parallelfails" name="TestWithStderr" time="0.000000"></testcase>
</testsuite>
<testsuite tests="29" failures="4" time="0.020000" name="gotest.tools/gotestsum/testjson/internal/withfails" timestamp="0001-01-01T00:00:00Z">
<properties>
<property name="go.version" value="go7.7.7"></property>
</properties>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestFailed" time="0.000000">
<failure message="Failed" type="">=== RUN TestFailed&#xA; fails_test.go:34: this failed&#xA;--- FAIL: TestFailed (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestFailedWithStderr" time="0.000000">
<failure message="Failed" type="">=== RUN TestFailedWithStderr&#xA;this is stderr&#xA; fails_test.go:43: also failed&#xA;--- FAIL: TestFailedWithStderr (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/c" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedWithFailure/c&#xA; fails_test.go:65: failed&#xA; --- FAIL: TestNestedWithFailure/c (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure" time="0.000000">
<failure message="Failed" type="">=== RUN TestNestedWithFailure&#xA;--- FAIL: TestNestedWithFailure (0.00s)&#xA;</failure>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestSkipped" time="0.000000">
<skipped message="=== RUN TestSkipped&#xA; fails_test.go:26: &#xA;--- SKIP: TestSkipped (0.00s)&#xA;"></skipped>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestSkippedWitLog" time="0.000000">
<skipped message="=== RUN TestSkippedWitLog&#xA; fails_test.go:30: the skip message&#xA;--- SKIP: TestSkippedWitLog (0.00s)&#xA;"></skipped>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestTimeout" time="0.000000">
<skipped message="=== RUN TestTimeout&#xA; timeout_test.go:13: skipping slow test&#xA;--- SKIP: TestTimeout (0.00s)&#xA;"></skipped>
</testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestPassed" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestPassedWithLog" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestPassedWithStdout" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestWithStderr" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/a/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/a" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/b/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/b" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/d/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedWithFailure/d" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/a/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/a" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/b/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/b" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/c/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/c" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/d/sub" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess/d" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestNestedSuccess" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestParallelTheFirst" time="0.010000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestParallelTheThird" time="0.000000"></testcase>
<testcase classname="gotest.tools/gotestsum/testjson/internal/withfails" name="TestParallelTheSecond" time="0.010000"></testcase>
</testsuite>
</testsuites>
2 changes: 1 addition & 1 deletion testjson/dotformat.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func (d *dotFormatter) Format(event TestEvent, exec *Execution) error {

sort.Slice(d.order, d.orderByLastUpdated)
for _, pkg := range d.order {
if d.opts.HideEmptyPackages && exec.Package(pkg).Total == 0 {
if d.opts.HideEmptyPackages && exec.Package(pkg).IsEmpty() {
continue
}

Expand Down
5 changes: 5 additions & 0 deletions testjson/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ func (p *Package) TestMainFailed() bool {
return p.action == ActionFail && len(p.Failed) == 0
}

// IsEmpty returns true if this package contains no tests.
func (p *Package) IsEmpty() bool {
return p.Total == 0 && !p.TestMainFailed()
}

const neverFinished time.Duration = -1

// end adds any tests that were missing an ActionFail TestEvent to the list of
Expand Down

0 comments on commit 3c0fb00

Please sign in to comment.