diff --git a/.golangci.yml b/.golangci.yml index e8e20adc..cc85fdc8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -15,7 +15,7 @@ issues: - text: 'always receives' linters: [unparam] - path: _test\.go - linters: [errcheck, staticcheck] + linters: [errcheck, staticcheck, lll] - path: internal/difflib/difflib\.go text: . - text: 'return value of .*Close` is not checked' diff --git a/assert/assert_go113.go b/assert/assert_go113.go new file mode 100644 index 00000000..1e3cede4 --- /dev/null +++ b/assert/assert_go113.go @@ -0,0 +1,20 @@ +// +build go1.13 + +package assert + +import ( + "gotest.tools/v3/assert/cmp" + "gotest.tools/v3/internal/assert" +) + +// ErrorIs fails the test if err is nil, or the error does not match expected +// when compared using errors.Is. See https://golang.org/pkg/errors/#Is for +// accepted argument values. +func ErrorIs(t TestingT, err error, expected error, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if !assert.Eval(t, assert.ArgsAfterT, cmp.ErrorIs(err, expected), msgAndArgs...) { + t.FailNow() + } +} diff --git a/assert/assert_go113_test.go b/assert/assert_go113_test.go new file mode 100644 index 00000000..db54936e --- /dev/null +++ b/assert/assert_go113_test.go @@ -0,0 +1,35 @@ +// +build go1.13 + +package assert + +import ( + "fmt" + "os" + "testing" +) + +func TestErrorIs(t *testing.T) { + t.Run("nil error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + var err error + ErrorIs(fakeT, err, os.ErrNotExist) + expected := `assertion failed: error is nil, not "file does not exist" (os.ErrNotExist *errors.errorString)` + expectFailNowed(t, fakeT, expected) + }) + t.Run("different error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("the actual error") + ErrorIs(fakeT, err, os.ErrNotExist) + expected := `assertion failed: error is "the actual error" (err *errors.errorString), not "file does not exist" (os.ErrNotExist *errors.errorString)` + expectFailNowed(t, fakeT, expected) + }) + t.Run("same error", func(t *testing.T) { + fakeT := &fakeTestingT{} + + err := fmt.Errorf("some wrapping: %w", os.ErrNotExist) + ErrorIs(fakeT, err, os.ErrNotExist) + expectSuccess(t, fakeT) + }) +} diff --git a/assert/cmp/compare_go113.go b/assert/cmp/compare_go113.go new file mode 100644 index 00000000..58af7445 --- /dev/null +++ b/assert/cmp/compare_go113.go @@ -0,0 +1,29 @@ +// +build go1.13 + +package cmp + +import ( + "errors" +) + +// ErrorIs succeeds if errors.Is(actual, expected) returns true. See +// https://golang.org/pkg/errors/#Is for accepted argument values. +func ErrorIs(actual error, expected error) Comparison { + return func() Result { + if errors.Is(actual, expected) { + return ResultSuccess + } + + return ResultFailureTemplate(`error is + {{- if not .Data.a }} nil,{{ else }} + {{- printf " \"%v\"" .Data.a}} ( + {{- with callArg 0 }}{{ formatNode . }} {{end -}} + {{- printf "%T" .Data.a -}} + ), + {{- end }} not {{ printf "\"%v\"" .Data.x}} ( + {{- with callArg 1 }}{{ formatNode . }} {{end -}} + {{- printf "%T" .Data.x -}} + )`, + map[string]interface{}{"a": actual, "x": expected}) + } +} diff --git a/assert/cmp/compare_go113_test.go b/assert/cmp/compare_go113_test.go new file mode 100644 index 00000000..fdcd5bdb --- /dev/null +++ b/assert/cmp/compare_go113_test.go @@ -0,0 +1,42 @@ +// +build go1.13 + +package cmp + +import ( + "go/ast" + "os" + "testing" +) + +func TestErrorIs(t *testing.T) { + t.Run("equal", func(t *testing.T) { + result := ErrorIs(stubError{}, stubError{})() + assertSuccess(t, result) + }) + t.Run("actual is nil", func(t *testing.T) { + result := ErrorIs(nil, stubError{})() + args := []ast.Expr{ + &ast.Ident{Name: "err"}, + &ast.StarExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: "errors"}, + Sel: &ast.Ident{Name: "errorString"}, + }}, + } + expected := `error is nil, not "stub error" (*errors.errorString cmp.stubError)` + assertFailureTemplate(t, result, args, expected) + }) + t.Run("not equal", func(t *testing.T) { + result := ErrorIs(os.ErrClosed, stubError{})() + args := []ast.Expr{ + &ast.Ident{Name: "err"}, + &ast.StarExpr{ + X: &ast.SelectorExpr{ + X: &ast.Ident{Name: "errors"}, + Sel: &ast.Ident{Name: "errorString"}, + }}, + } + expected := `error is "file already closed" (err *errors.errorString), not "stub error" (*errors.errorString cmp.stubError)` + assertFailureTemplate(t, result, args, expected) + }) +} diff --git a/poll/check.go b/poll/check.go index 060b0998..46880f5b 100644 --- a/poll/check.go +++ b/poll/check.go @@ -11,16 +11,20 @@ type Check func(t LogT) Result // FileExists looks on filesystem and check that path exists. func FileExists(path string) Check { return func(t LogT) Result { + if h, ok := t.(helperT); ok { + h.Helper() + } + _, err := os.Stat(path) - if os.IsNotExist(err) { + switch { + case os.IsNotExist(err): t.Logf("waiting on file %s to exist", path) return Continue("file %s does not exist", path) - } - if err != nil { + case err != nil: return Error(err) + default: + return Success() } - - return Success() } } @@ -29,6 +33,10 @@ func FileExists(path string) Check { // address parameters. func Connection(network, address string) Check { return func(t LogT) Result { + if h, ok := t.(helperT); ok { + h.Helper() + } + _, err := net.Dial(network, address) if err != nil { t.Logf("waiting on socket %s://%s to be available...", network, address)