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.init\n" + "\t.+/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.init\n" + "\t.+/github.com/pkg/errors/stack_test.go:9", }, { Frame(0), "%v", "unknown:0", }} for i, tt := range tests { testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) }} 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 i, tt := range tests { pc := tt.Frame.pc() fn := runtime.FuncForPC(pc) file, _ := fn.FileLine(pc) got := trimGOPATH(fn.Name(), file) testFormatRegexp(t, i, got, "%s", tt.want) }} func TestStackTrace(t *testing.T) { tests := []struct { err error want []string }{{ New("ooh"), []string{ "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:172", }, }, { Wrap(New("ooh"), "ahh"), []string{ "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New }, }, { Cause(Wrap(New("ooh"), "ahh")), []string{ "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New }, }, { func() error { return New("ooh") }(), []string{ `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + "\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New "github.com/pkg/errors.TestStackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:187", // 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.(func·010|TestStackTrace.func2.1)` + "\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + "\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller "github.com/pkg/errors.TestStackTrace\n" + "\t.+/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 { testFormatRegexp(t, i, st[j], "%+v", 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:225 stack_test.go:272\]`, }, { stackTrace()[:2], "%+v", "\n" + "github.com/pkg/errors.stackTrace\n" + "\t.+/github.com/pkg/errors/stack_test.go:225\n" + "github.com/pkg/errors.TestStackTraceFormat\n" + "\t.+/github.com/pkg/errors/stack_test.go:276", }, { stackTrace()[:2], "%#v", `\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`, }} for i, tt := range tests { testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) }}
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.init\n" +
"\t.+/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.init\n" +
"\t.+/github.com/pkg/errors/stack_test.go:9",
}, {
Frame(0),
"%v",
"unknown:0",
}}
for i, tt := range tests {
testFormatRegexp(t, i, tt.Frame, tt.format, tt.want)
}
}
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 i, tt := range tests {
pc := tt.Frame.pc()
fn := runtime.FuncForPC(pc)
file, _ := fn.FileLine(pc)
got := trimGOPATH(fn.Name(), file)
testFormatRegexp(t, i, got, "%s", tt.want)
}
}
func TestStackTrace(t *testing.T) {
tests := []struct {
err error
want []string
}{{
New("ooh"), []string{
"github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:172",
},
}, {
Wrap(New("ooh"), "ahh"), []string{
"github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New
},
}, {
Cause(Wrap(New("ooh"), "ahh")), []string{
"github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New
},
}, {
func() error { return New("ooh") }(), []string{
`github.com/pkg/errors.(func·009|TestStackTrace.func1)` +
"\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New
"github.com/pkg/errors.TestStackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:187", // 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.(func·010|TestStackTrace.func2.1)` +
"\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf
`github.com/pkg/errors.(func·011|TestStackTrace.func2)` +
"\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller
"github.com/pkg/errors.TestStackTrace\n" +
"\t.+/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 {
testFormatRegexp(t, i, st[j], "%+v", 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:225 stack_test.go:272\]`,
}, {
stackTrace()[:2],
"%+v",
"\n" +
"github.com/pkg/errors.stackTrace\n" +
"\t.+/github.com/pkg/errors/stack_test.go:225\n" +
"github.com/pkg/errors.TestStackTraceFormat\n" +
"\t.+/github.com/pkg/errors/stack_test.go:276",
}, {
stackTrace()[:2],
"%#v",
`\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`,
}}
for i, tt := range tests {
testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want)
}
}