1
0
Fork 0
golang-github-go-ap-errors/stack_test.go
Daniel Baumann d8f2a7c92a
Adding upstream version 0.0~git20250501.cd50c6a.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-18 22:31:57 +02:00

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