diff --git a/main.go b/main.go index f18e4872..32119e66 100644 --- a/main.go +++ b/main.go @@ -197,7 +197,7 @@ func run(opts *options) error { return err } - goTestProc, err := startGoTest(ctx, goTestCmdArgs(opts, rerunOpts{})) + goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, rerunOpts{})) if err != nil { return err } @@ -216,27 +216,40 @@ func run(opts *options) error { if err != nil { return err } - goTestExitErr := goTestProc.cmd.Wait() + exitErr := goTestProc.cmd.Wait() + if exitErr == nil || opts.rerunFailsMaxAttempts == 0 { + return finishRun(opts, exec, exitErr) + } + if err := hasErrors(exitErr, exec); err != nil { + return finishRun(opts, exec, err) + } - if goTestExitErr != nil && opts.rerunFailsMaxAttempts > 0 { - goTestExitErr = hasErrors(goTestExitErr, exec) - if goTestExitErr == nil { - cfg := testjson.ScanConfig{Execution: exec, Handler: handler} - goTestExitErr = rerunFailed(ctx, opts, cfg) - } + failed := len(rerunFailsFilter(opts)(exec.Failed())) + if failed > opts.rerunFailsMaxInitialFailures { + err := fmt.Errorf( + "number of test failures (%d) exceeds maximum (%d) set by --rerun-fails-max-failures", + failed, opts.rerunFailsMaxInitialFailures) + return finishRun(opts, exec, err) } - testjson.PrintSummary(opts.stdout, exec, opts.noSummary.value) - if err := writeJUnitFile(opts, exec); err != nil { + cfg = testjson.ScanConfig{Execution: exec, Handler: handler} + exitErr = rerunFailed(ctx, opts, cfg) + if err := writeRerunFailsReport(opts, exec); err != nil { return err } - if err := writeRerunFailsReport(opts, exec); err != nil { + return finishRun(opts, exec, exitErr) +} + +func finishRun(opts *options, exec *testjson.Execution, exitErr error) error { + testjson.PrintSummary(opts.stdout, exec, opts.noSummary.value) + + if err := writeJUnitFile(opts, exec); err != nil { return err } if err := postRunHook(opts, exec); err != nil { return err } - return goTestExitErr + return exitErr } func goTestCmdArgs(opts *options, rerunOpts rerunOpts) []string { diff --git a/main_test.go b/main_test.go index 9e1b5d28..dd558f9d 100644 --- a/main_test.go +++ b/main_test.go @@ -2,6 +2,8 @@ package main import ( "bytes" + "os" + "strings" "testing" "gotest.tools/v3/assert" @@ -286,3 +288,71 @@ func TestGoTestCmdArgs(t *testing.T) { }) } } + +func TestRun_RerunFails_WithTooManyInitialFailures(t *testing.T) { + jsonFailed := `{"Package": "pkg", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "fail"} +{"Package": "pkg", "Test": "TestTwo", "Action": "run"} +{"Package": "pkg", "Test": "TestTwo", "Action": "fail"} +{"Package": "pkg", "Action": "fail"} +` + + fn := func(args []string) proc { + return proc{ + cmd: fakeWaiter{result: newExitCode("failed", 1)}, + stdout: strings.NewReader(jsonFailed), + stderr: bytes.NewReader(nil), + } + } + reset := patchStartGoTestFn(fn) + defer reset() + + out := new(bytes.Buffer) + opts := &options{ + rawCommand: true, + args: []string{"./test.test"}, + format: "testname", + rerunFailsMaxAttempts: 3, + rerunFailsMaxInitialFailures: 1, + stdout: out, + stderr: os.Stderr, + noSummary: newNoSummaryValue(), + } + err := run(opts) + assert.ErrorContains(t, err, "number of test failures (2) exceeds maximum (1)", out.String()) +} + +func TestRun_RerunFails_BuildErrorPreventsRerun(t *testing.T) { + jsonFailed := `{"Package": "pkg", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "fail"} +{"Package": "pkg", "Test": "TestTwo", "Action": "run"} +{"Package": "pkg", "Test": "TestTwo", "Action": "fail"} +{"Package": "pkg", "Action": "fail"} +` + + fn := func(args []string) proc { + return proc{ + cmd: fakeWaiter{result: newExitCode("failed", 1)}, + stdout: strings.NewReader(jsonFailed), + stderr: strings.NewReader("anything here is an error\n"), + } + } + reset := patchStartGoTestFn(fn) + defer reset() + + out := new(bytes.Buffer) + opts := &options{ + rawCommand: true, + args: []string{"./test.test"}, + format: "testname", + rerunFailsMaxAttempts: 3, + rerunFailsMaxInitialFailures: 1, + stdout: out, + stderr: os.Stderr, + noSummary: newNoSummaryValue(), + } + err := run(opts) + assert.ErrorContains(t, err, "rerun aborted because previous run had errors", out.String()) +} diff --git a/rerunfails.go b/rerunfails.go index 3d2e0b3e..cf222397 100644 --- a/rerunfails.go +++ b/rerunfails.go @@ -45,12 +45,6 @@ func rerunFailsFilter(o *options) testCaseFilter { func rerunFailed(ctx context.Context, opts *options, scanConfig testjson.ScanConfig) error { tcFilter := rerunFailsFilter(opts) - failed := len(tcFilter(scanConfig.Execution.Failed())) - if failed > opts.rerunFailsMaxInitialFailures { - return fmt.Errorf( - "number of test failures (%d) exceeds maximum (%d) set by --rerun-fails-max-failures", - failed, opts.rerunFailsMaxInitialFailures) - } rec := newFailureRecorderFromExecution(scanConfig.Execution) for attempts := 0; rec.count() > 0 && attempts < opts.rerunFailsMaxAttempts; attempts++ {