263 lines
4.8 KiB
Go
263 lines
4.8 KiB
Go
package errors
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var initpc = caller()
|
|
|
|
func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
|
|
t.Helper()
|
|
got := fmt.Sprintf(format, arg)
|
|
gotLines := strings.SplitN(got, "\n", -1)
|
|
wantLines := strings.SplitN(want, "\n", -1)
|
|
|
|
if len(wantLines) > len(gotLines) {
|
|
t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want)
|
|
return
|
|
}
|
|
|
|
for i, w := range wantLines {
|
|
match, err := regexp.MatchString(w, gotLines[i])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !match {
|
|
t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type X struct{}
|
|
|
|
// val returns a Frame pointing to itself.
|
|
func (x X) val() Frame {
|
|
return caller()
|
|
}
|
|
|
|
// ptr returns a Frame pointing to itself.
|
|
func (x *X) ptr() Frame {
|
|
return caller()
|
|
}
|
|
|
|
func TestFrameFormat(t *testing.T) {
|
|
var tests = []struct {
|
|
Frame
|
|
format string
|
|
want string
|
|
}{{
|
|
initpc,
|
|
"%s",
|
|
"stack_test.go",
|
|
}, {
|
|
initpc,
|
|
"%+s",
|
|
"github.com/go-ap/errors.init\n" +
|
|
"\t.+/errors/stack_test.go",
|
|
}, {
|
|
0,
|
|
"%s",
|
|
"unknown",
|
|
}, {
|
|
0,
|
|
"%+s",
|
|
"unknown",
|
|
}, {
|
|
initpc,
|
|
"%d",
|
|
"11",
|
|
}, {
|
|
0,
|
|
"%d",
|
|
"0",
|
|
}, {
|
|
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",
|
|
}, {
|
|
0,
|
|
"%n",
|
|
"",
|
|
}, {
|
|
initpc,
|
|
"%v",
|
|
"stack_test.go:11",
|
|
}, {
|
|
initpc,
|
|
"%+v",
|
|
"github.com/go-ap/errors.init\n" +
|
|
"\t.+/errors/stack_test.go:11",
|
|
}, {
|
|
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/go-ap/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 TestStackTrace(t *testing.T) {
|
|
tests := []struct {
|
|
err error
|
|
want []string
|
|
}{{
|
|
Newf("ooh"), []string{
|
|
"github.com/go-ap/errors.TestStackTrace\n" +
|
|
"\t.+/errors/stack_test.go:145",
|
|
},
|
|
}, {
|
|
wrap(Newf("ooh"), "ahh"), []string{
|
|
"github.com/go-ap/errors.TestStackTrace\n" +
|
|
"\t.+/errors/stack_test.go:150", // this is the stack of Wrap, not New
|
|
},
|
|
}, {
|
|
func() error { return Newf("ooh") }(), []string{
|
|
`github.com/go-ap/errors.TestStackTrace.func1` +
|
|
"\n\t.+/errors/stack_test.go:155", // this is the stack of New
|
|
"github.com/go-ap/errors.TestStackTrace\n" +
|
|
"\t.+/errors/stack_test.go:155", // this is the stack of New's caller
|
|
},
|
|
}}
|
|
t.Skipf(`TODO(marius): This needs some more work
|
|
As going one level up the stack in stack.callers() removes meaningful information from the tests`)
|
|
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()
|
|
if len(st) == 0 {
|
|
continue
|
|
}
|
|
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:183 stack_test.go:230\]`,
|
|
}, {
|
|
stackTrace()[:2],
|
|
"%+v",
|
|
"\n" +
|
|
"github.com/go-ap/errors.stackTrace\n" +
|
|
"\t.+/errors/stack_test.go:183\n" +
|
|
"github.com/go-ap/errors.TestStackTraceFormat\n" +
|
|
"\t.+/errors/stack_test.go:234",
|
|
}, {
|
|
stackTrace()[:2],
|
|
"%#v",
|
|
`\[\]errors.Frame{stack_test.go:183, stack_test.go:242}`,
|
|
}}
|
|
|
|
t.Skipf(`TODO(marius): This needs some more work
|
|
As going one level up the stack in stack.callers() removes meaningful information from the tests`)
|
|
for i, tt := range tests {
|
|
testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want)
|
|
}
|
|
}
|
|
|
|
// a version of runtime.Caller that returns a Frame, not a uintptr.
|
|
func caller() Frame {
|
|
var pcs [3]uintptr
|
|
n := runtime.Callers(2, pcs[:])
|
|
frames := runtime.CallersFrames(pcs[:n])
|
|
frame, _ := frames.Next()
|
|
return Frame(frame.PC)
|
|
}
|