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) } } }