From e1d4f60af969a479a6b43e07fbee22f74dfa1236 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Sat, 29 Aug 2020 02:27:16 -0400 Subject: [PATCH] Add a test for the rerun-fails where the last test passes --- main.go | 2 + rerunfails.go | 5 +- rerunfails_test.go | 112 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index b7116401..f18e4872 100644 --- a/main.go +++ b/main.go @@ -377,3 +377,5 @@ func exitCodeWithDefault(err error) int { type exitCoder interface { ExitCode() int } + +var _ exitCoder = &exec.ExitError{} diff --git a/rerunfails.go b/rerunfails.go index 9a58a740..09936e01 100644 --- a/rerunfails.go +++ b/rerunfails.go @@ -60,7 +60,7 @@ func rerunFailed(ctx context.Context, opts *options, scanConfig testjson.ScanCon nextRec := newFailureRecorder(scanConfig.Handler) for _, tc := range tcFilter(rec.failures) { - goTestProc, err := startGoTest(ctx, goTestCmdArgs(opts, newRerunOptsFromTestCase(tc))) + goTestProc, err := startGoTestFn(ctx, goTestCmdArgs(opts, newRerunOptsFromTestCase(tc))) if err != nil { return err } @@ -85,6 +85,9 @@ func rerunFailed(ctx context.Context, opts *options, scanConfig testjson.ScanCon return lastErr } +// startGoTestFn is a shim for testing +var startGoTestFn = startGoTest + func hasErrors(err error, exec *testjson.Execution) error { switch { case len(exec.Errors()) > 0: diff --git a/rerunfails_test.go b/rerunfails_test.go index 8fc6f55c..225d431f 100644 --- a/rerunfails_test.go +++ b/rerunfails_test.go @@ -2,7 +2,10 @@ package main import ( "bytes" + "context" + "fmt" "io/ioutil" + "strings" "testing" "gotest.tools/gotestsum/testjson" @@ -59,3 +62,112 @@ func TestGoTestRunFlagFromTestCases(t *testing.T) { }) } } + +func TestRerunFailed_ReturnsAnErrorWhenTheLastTestIsSuccessful(t *testing.T) { + type result struct { + out string + err error + } + jsonFailed := `{"Package": "pkg", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "fail"} +{"Package": "pkg", "Action": "fail"} +` + events := []result{ + {out: jsonFailed, err: newExitCode("run-failed-1", 1)}, + {out: jsonFailed, err: newExitCode("run-failed-2", 1)}, + {out: jsonFailed, err: newExitCode("run-failed-3", 1)}, + { + out: `{"Package": "pkg", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "run"} +{"Package": "pkg", "Test": "TestOne", "Action": "pass"} +{"Package": "pkg", "Action": "pass"} +`, + }, + } + + fn := func(args []string) proc { + next := events[0] + events = events[1:] + return proc{ + cmd: fakeWaiter{result: next.err}, + stdout: strings.NewReader(next.out), + stderr: bytes.NewReader(nil), + } + } + reset := patchStartGoTestFn(fn) + defer reset() + + stdout := new(bytes.Buffer) + ctx := context.Background() + opts := &options{ + rerunFailsMaxInitialFailures: 10, + rerunFailsMaxAttempts: 2, + stdout: stdout, + } + cfg := testjson.ScanConfig{ + Execution: newExecutionWithTwoFailures(t), + Handler: noopHandler{}, + } + err := rerunFailed(ctx, opts, cfg) + assert.Error(t, err, "run-failed-3") +} + +func patchStartGoTestFn(f func(args []string) proc) func() { + orig := startGoTestFn + startGoTestFn = func(ctx context.Context, args []string) (proc, error) { + return f(args), nil + } + return func() { + startGoTestFn = orig + } +} + +func newExecutionWithTwoFailures(t *testing.T) *testjson.Execution { + t.Helper() + + out := `{"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"} +` + exec, err := testjson.ScanTestOutput(testjson.ScanConfig{ + Stdout: strings.NewReader(out), + Stderr: strings.NewReader(""), + }) + assert.NilError(t, err) + return exec +} + +type fakeWaiter struct { + result error +} + +func (f fakeWaiter) Wait() error { + return f.result +} + +type exitCodeError struct { + error + code int +} + +func (e exitCodeError) ExitCode() int { + return e.code +} + +func newExitCode(msg string, code int) error { + return exitCodeError{error: fmt.Errorf(msg), code: code} +} + +type noopHandler struct{} + +func (s noopHandler) Event(testjson.TestEvent, *testjson.Execution) error { + return nil +} + +func (s noopHandler) Err(string) error { + return nil +}