blob: 2795bb2e0ef4b64b9fccf2e0cabc133f014f0e75 [file] [log] [blame]
package errors
import (
"bytes"
"errors"
"fmt"
"io"
"reflect"
"testing"
)
func TestNew(t *testing.T) {
tests := []struct {
err string
want error
}{
{"", fmt.Errorf("")},
{"foo", fmt.Errorf("foo")},
{"foo", New("foo")},
{"string with format specifiers: %v", errors.New("string with format specifiers: %v")},
}
for _, tt := range tests {
got := New(tt.err)
if got.Error() != tt.want.Error() {
t.Errorf("New.Error(): got: %q, want %q", got, tt.want)
}
}
}
func TestWrapNil(t *testing.T) {
got := Wrap(nil, "no error")
if got != nil {
t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got)
}
}
func TestWrap(t *testing.T) {
tests := []struct {
err error
message string
want string
}{
{io.EOF, "read error", "read error: EOF"},
{Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"},
}
for _, tt := range tests {
got := Wrap(tt.err, tt.message).Error()
if got != tt.want {
t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
}
}
}
type nilError struct{}
func (nilError) Error() string { return "nil error" }
type causeError struct {
cause error
}
func (e *causeError) Error() string { return "cause error" }
func (e *causeError) Cause() error { return e.cause }
func TestCause(t *testing.T) {
x := New("error")
tests := []struct {
err error
want error
}{{
// nil error is nil
err: nil,
want: nil,
}, {
// explicit nil error is nil
err: (error)(nil),
want: nil,
}, {
// typed nil is nil
err: (*nilError)(nil),
want: (*nilError)(nil),
}, {
// uncaused error is unaffected
err: io.EOF,
want: io.EOF,
}, {
// caused error returns cause
err: &causeError{cause: io.EOF},
want: io.EOF,
}, {
err: x, // return from errors.New
want: x,
}}
for i, tt := range tests {
got := Cause(tt.err)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want)
}
}
}
func TestFprint(t *testing.T) {
x := New("error")
tests := []struct {
err error
want string
}{{
// nil error is nil
err: nil,
}, {
// explicit nil error is nil
err: (error)(nil),
}, {
// uncaused error is unaffected
err: io.EOF,
want: "EOF\n",
}, {
// caused error returns cause
err: &causeError{cause: io.EOF},
want: "cause error\nEOF\n",
}, {
err: x, // return from errors.New
want: "github.com/pkg/errors/errors_test.go:106: error\n",
}, {
err: Wrap(x, "message"),
want: "github.com/pkg/errors/errors_test.go:128: message\ngithub.com/pkg/errors/errors_test.go:106: error\n",
}, {
err: Wrap(Wrap(x, "message"), "another message"),
want: "github.com/pkg/errors/errors_test.go:131: another message\ngithub.com/pkg/errors/errors_test.go:131: message\ngithub.com/pkg/errors/errors_test.go:106: error\n",
}, {
err: Wrapf(x, "message"),
want: "github.com/pkg/errors/errors_test.go:134: message\ngithub.com/pkg/errors/errors_test.go:106: error\n",
}}
for i, tt := range tests {
var w bytes.Buffer
Fprint(&w, tt.err)
got := w.String()
if got != tt.want {
t.Errorf("test %d: Fprint(w, %q): got %q, want %q", i+1, tt.err, got, tt.want)
}
}
}
func TestWrapfNil(t *testing.T) {
got := Wrapf(nil, "no error")
if got != nil {
t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got)
}
}
func TestWrapf(t *testing.T) {
tests := []struct {
err error
message string
want string
}{
{io.EOF, "read error", "read error: EOF"},
{Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"},
{Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"},
}
for _, tt := range tests {
got := Wrapf(tt.err, tt.message).Error()
if got != tt.want {
t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
}
}
}
func TestErrorf(t *testing.T) {
tests := []struct {
err error
want string
}{
{Errorf("read error without format specifiers"), "read error without format specifiers"},
{Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"},
}
for _, tt := range tests {
got := tt.err.Error()
if got != tt.want {
t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want)
}
}
}
func TestStack(t *testing.T) {
type fileline struct {
file string
line int
}
tests := []struct {
err error
want []fileline
}{{
New("ooh"), []fileline{
{"github.com/pkg/errors/errors_test.go", 200},
},
}, {
Wrap(New("ooh"), "ahh"), []fileline{
{"github.com/pkg/errors/errors_test.go", 204}, // this is the stack of Wrap, not New
},
}, {
Cause(Wrap(New("ooh"), "ahh")), []fileline{
{"github.com/pkg/errors/errors_test.go", 208}, // this is the stack of New
},
}, {
func() error { return New("ooh") }(), []fileline{
{"github.com/pkg/errors/errors_test.go", 212}, // this is the stack of New
{"github.com/pkg/errors/errors_test.go", 212}, // this is the stack of New's caller
},
}, {
Cause(func() error {
return func() error {
return Errorf("hello %s", fmt.Sprintf("world"))
}()
}()), []fileline{
{"github.com/pkg/errors/errors_test.go", 219}, // this is the stack of Errorf
{"github.com/pkg/errors/errors_test.go", 220}, // this is the stack of Errorf's caller
{"github.com/pkg/errors/errors_test.go", 221}, // this is the stack of Errorf's caller's caller
},
}}
for _, tt := range tests {
x, ok := tt.err.(interface {
Stack() []uintptr
})
if !ok {
t.Errorf("expected %#v to implement Stack()", tt.err)
continue
}
st := x.Stack()
for i, want := range tt.want {
file, line := location(st[i] - 1)
if file != want.file || line != want.line {
t.Errorf("frame %d: expected %s:%d, got %s:%d", i, want.file, want.line, file, line)
}
}
}
}
// errors.New, etc values are not expected to be compared by value
// but the change in errors#27 made them incomparable. Assert that
// various kinds of errors have a functional equality operator, even
// if the result of that equality is always false.
func TestErrorEquality(t *testing.T) {
tests := []struct {
err1, err2 error
}{
{io.EOF, io.EOF},
{io.EOF, nil},
{io.EOF, errors.New("EOF")},
{io.EOF, New("EOF")},
{New("EOF"), New("EOF")},
{New("EOF"), Errorf("EOF")},
{New("EOF"), Wrap(io.EOF, "EOF")},
}
for _, tt := range tests {
_ = tt.err1 == tt.err2 // mustn't panic
}
}