| package errors |
| |
| import ( |
| "fmt" |
| "runtime" |
| "testing" |
| ) |
| |
| var initpc, _, _, _ = runtime.Caller(0) |
| |
| func TestFrameLine(t *testing.T) { |
| var tests = []struct { |
| Frame |
| want int |
| }{{ |
| Frame(initpc), |
| 9, |
| }, { |
| func() Frame { |
| var pc, _, _, _ = runtime.Caller(0) |
| return Frame(pc) |
| }(), |
| 20, |
| }, { |
| func() Frame { |
| var pc, _, _, _ = runtime.Caller(1) |
| return Frame(pc) |
| }(), |
| 28, |
| }, { |
| Frame(0), // invalid PC |
| 0, |
| }} |
| |
| for _, tt := range tests { |
| got := tt.Frame.line() |
| want := tt.want |
| if want != got { |
| t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) |
| } |
| } |
| } |
| |
| type X struct{} |
| |
| func (x X) val() Frame { |
| var pc, _, _, _ = runtime.Caller(0) |
| return Frame(pc) |
| } |
| |
| func (x *X) ptr() Frame { |
| var pc, _, _, _ = runtime.Caller(0) |
| return Frame(pc) |
| } |
| |
| func TestFrameFormat(t *testing.T) { |
| var tests = []struct { |
| Frame |
| format string |
| want string |
| }{{ |
| Frame(initpc), |
| "%s", |
| "stack_test.go", |
| }, { |
| Frame(initpc), |
| "%+s", |
| "github.com/pkg/errors/stack_test.go", |
| }, { |
| Frame(0), |
| "%s", |
| "unknown", |
| }, { |
| Frame(0), |
| "%+s", |
| "unknown", |
| }, { |
| Frame(initpc), |
| "%d", |
| "9", |
| }, { |
| Frame(0), |
| "%d", |
| "0", |
| }, { |
| Frame(initpc), |
| "%n", |
| "init", |
| }, { |
| func() Frame { |
| var x X |
| return x.ptr() |
| }(), |
| "%n", |
| "(*X).ptr", |
| }, { |
| func() Frame { |
| var x X |
| return x.val() |
| }(), |
| "%n", |
| "X.val", |
| }, { |
| Frame(0), |
| "%n", |
| "", |
| }, { |
| Frame(initpc), |
| "%v", |
| "stack_test.go:9", |
| }, { |
| Frame(initpc), |
| "%+v", |
| "github.com/pkg/errors/stack_test.go:9", |
| }, { |
| Frame(0), |
| "%v", |
| "unknown:0", |
| }} |
| |
| for _, tt := range tests { |
| got := fmt.Sprintf(tt.format, tt.Frame) |
| want := tt.want |
| if want != got { |
| t.Errorf("%v %q: want: %q, got: %q", tt.Frame, tt.format, want, got) |
| } |
| } |
| } |
| |
| func TestFuncname(t *testing.T) { |
| tests := []struct { |
| name, want string |
| }{ |
| {"", ""}, |
| {"runtime.main", "main"}, |
| {"github.com/pkg/errors.funcname", "funcname"}, |
| {"funcname", "funcname"}, |
| {"io.copyBuffer", "copyBuffer"}, |
| {"main.(*R).Write", "(*R).Write"}, |
| } |
| |
| for _, tt := range tests { |
| got := funcname(tt.name) |
| want := tt.want |
| if got != want { |
| t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) |
| } |
| } |
| } |
| |
| func TestTrimGOPATH(t *testing.T) { |
| var tests = []struct { |
| Frame |
| want string |
| }{{ |
| Frame(initpc), |
| "github.com/pkg/errors/stack_test.go", |
| }} |
| |
| for _, tt := range tests { |
| pc := tt.Frame.pc() |
| fn := runtime.FuncForPC(pc) |
| file, _ := fn.FileLine(pc) |
| got := trimGOPATH(fn.Name(), file) |
| want := tt.want |
| if want != got { |
| t.Errorf("%v: want %q, got %q", tt.Frame, want, got) |
| } |
| } |
| } |
| |
| func TestStacktrace(t *testing.T) { |
| tests := []struct { |
| err error |
| want []string |
| }{{ |
| New("ooh"), []string{ |
| "github.com/pkg/errors/stack_test.go:177", |
| }, |
| }, { |
| Wrap(New("ooh"), "ahh"), []string{ |
| "github.com/pkg/errors/stack_test.go:181", // this is the stack of Wrap, not New |
| }, |
| }, { |
| Cause(Wrap(New("ooh"), "ahh")), []string{ |
| "github.com/pkg/errors/stack_test.go:185", // this is the stack of New |
| }, |
| }, { |
| func() error { return New("ooh") }(), []string{ |
| "github.com/pkg/errors/stack_test.go:189", // this is the stack of New |
| "github.com/pkg/errors/stack_test.go:189", // this is the stack of New's caller |
| }, |
| }, { |
| Cause(func() error { |
| return func() error { |
| return Errorf("hello %s", fmt.Sprintf("world")) |
| }() |
| }()), []string{ |
| "github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf |
| "github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller |
| "github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller |
| }, |
| }} |
| for i, tt := range tests { |
| x, ok := tt.err.(interface { |
| Stacktrace() Stacktrace |
| }) |
| if !ok { |
| t.Errorf("expected %#v to implement Stacktrace() Stacktrace", tt.err) |
| continue |
| } |
| st := x.Stacktrace() |
| for j, want := range tt.want { |
| frame := st[j] |
| got := fmt.Sprintf("%+v", frame) |
| if got != want { |
| t.Errorf("test %d: frame %d: got %q, want %q", i, j, got, want) |
| } |
| } |
| } |
| } |
| |
| func stacktrace() Stacktrace { |
| const depth = 8 |
| var pcs [depth]uintptr |
| n := runtime.Callers(1, pcs[:]) |
| var st stack = pcs[0:n] |
| return st.Stacktrace() |
| } |
| |
| func TestStacktraceFormat(t *testing.T) { |
| tests := []struct { |
| Stacktrace |
| format string |
| want string |
| }{{ |
| nil, |
| "%s", |
| "[]", |
| }, { |
| nil, |
| "%v", |
| "[]", |
| }, { |
| nil, |
| "%+v", |
| "[]", |
| }, { |
| nil, |
| "%#v", |
| "[]errors.Frame(nil)", |
| }, { |
| make(Stacktrace, 0), |
| "%s", |
| "[]", |
| }, { |
| make(Stacktrace, 0), |
| "%v", |
| "[]", |
| }, { |
| make(Stacktrace, 0), |
| "%+v", |
| "[]", |
| }, { |
| make(Stacktrace, 0), |
| "%#v", |
| "[]errors.Frame{}", |
| }, { |
| stacktrace()[:2], |
| "%s", |
| "[stack_test.go stack_test.go]", |
| }, { |
| stacktrace()[:2], |
| "%v", |
| "[stack_test.go:226 stack_test.go:273]", |
| }, { |
| stacktrace()[:2], |
| "%+v", |
| "[github.com/pkg/errors/stack_test.go:226 github.com/pkg/errors/stack_test.go:277]", |
| }, { |
| stacktrace()[:2], |
| "%#v", |
| "[]errors.Frame{stack_test.go:226, stack_test.go:281}", |
| }} |
| |
| for i, tt := range tests { |
| got := fmt.Sprintf(tt.format, tt.Stacktrace) |
| if got != tt.want { |
| t.Errorf("test %d: got: %q, want: %q", i+1, got, tt.want) |
| } |
| } |
| } |