mirror of
https://github.com/pkg/errors.git
synced 2025-05-02 21:49:41 +00:00
* Introduce errors.Stacktrace Introduce a new type to replace the unnamed return value from `Stacktrace()`. `Stacktrace` implements `fmt.Formatter` and currently replicates the default behaviour of printing a `[]Frame`. In a future PR the values of `%+v` and `%#v` will be extended to implement a more readable stacktrace.
293 lines
5.1 KiB
Go
293 lines
5.1 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|