summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-stack/stack/stack_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-stack/stack/stack_test.go')
-rw-r--r--vendor/github.com/go-stack/stack/stack_test.go582
1 files changed, 582 insertions, 0 deletions
diff --git a/vendor/github.com/go-stack/stack/stack_test.go b/vendor/github.com/go-stack/stack/stack_test.go
new file mode 100644
index 0000000..44f3a7d
--- /dev/null
+++ b/vendor/github.com/go-stack/stack/stack_test.go
@@ -0,0 +1,582 @@
+package stack_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strings"
+ "testing"
+
+ "github.com/go-stack/stack"
+)
+
+func TestCaller(t *testing.T) {
+ t.Parallel()
+
+ c := stack.Caller(0)
+ _, file, line, ok := runtime.Caller(0)
+ line--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ if got, want := c.Frame().File, file; got != want {
+ t.Errorf("got file == %v, want file == %v", got, want)
+ }
+
+ if got, want := c.Frame().Line, line; got != want {
+ t.Errorf("got line == %v, want line == %v", got, want)
+ }
+}
+
+func f3(f1 func() stack.Call) stack.Call {
+ return f2(f1)
+}
+
+func f2(f1 func() stack.Call) stack.Call {
+ return f1()
+}
+
+func TestCallerMidstackInlined(t *testing.T) {
+ t.Parallel()
+
+ _, _, line, ok := runtime.Caller(0)
+ line -= 10 // adjust to return f1() line inside f2()
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ c := f3(func() stack.Call {
+ return stack.Caller(2)
+ })
+
+ if got, want := c.Frame().Line, line; got != want {
+ t.Errorf("got line == %v, want line == %v", got, want)
+ }
+ if got, want := c.Frame().Function, "github.com/go-stack/stack_test.f3"; got != want {
+ t.Errorf("got func name == %v, want func name == %v", got, want)
+ }
+}
+
+func TestCallerPanic(t *testing.T) {
+ t.Parallel()
+
+ var (
+ line int
+ ok bool
+ )
+
+ defer func() {
+ if recover() != nil {
+ var pcs [32]uintptr
+ n := runtime.Callers(1, pcs[:])
+ frames := runtime.CallersFrames(pcs[:n])
+ // count frames to runtime.sigpanic
+ panicIdx := 0
+ for {
+ f, more := frames.Next()
+ if f.Function == "runtime.sigpanic" {
+ break
+ }
+ panicIdx++
+ if !more {
+ t.Fatal("no runtime.sigpanic entry on the stack")
+ }
+ }
+ c := stack.Caller(panicIdx)
+ if got, want := c.Frame().Function, "runtime.sigpanic"; got != want {
+ t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want)
+ }
+ c1 := stack.Caller(panicIdx + 1)
+ if got, want := c1.Frame().Function, "github.com/go-stack/stack_test.TestCallerPanic"; got != want {
+ t.Errorf("TestCallerPanic frame: got name == %v, want name == %v", got, want)
+ }
+ if got, want := c1.Frame().Line, line; got != want {
+ t.Errorf("TestCallerPanic frame: got line == %v, want line == %v", got, want)
+ }
+ }
+ }()
+
+ _, _, line, ok = runtime.Caller(0)
+ line += 7 // adjust to match line of panic below
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ // Initiate a sigpanic.
+ var x *uintptr
+ _ = *x
+}
+
+type tholder struct {
+ trace func() stack.CallStack
+}
+
+func (th *tholder) traceLabyrinth() stack.CallStack {
+ for {
+ return th.trace()
+ }
+}
+
+func TestTrace(t *testing.T) {
+ t.Parallel()
+
+ _, _, line, ok := runtime.Caller(0)
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ fh := tholder{
+ trace: func() stack.CallStack {
+ cs := stack.Trace()
+ return cs
+ },
+ }
+
+ cs := fh.traceLabyrinth()
+
+ lines := []int{line + 7, line - 7, line + 12}
+
+ for i, line := range lines {
+ if got, want := cs[i].Frame().Line, line; got != want {
+ t.Errorf("got line[%d] == %v, want line[%d] == %v", i, got, i, want)
+ }
+ }
+}
+
+// Test stack handling originating from a sigpanic.
+func TestTracePanic(t *testing.T) {
+ t.Parallel()
+
+ var (
+ line int
+ ok bool
+ )
+
+ defer func() {
+ if recover() != nil {
+ trace := stack.Trace()
+
+ // find runtime.sigpanic
+ panicIdx := -1
+ for i, c := range trace {
+ if c.Frame().Function == "runtime.sigpanic" {
+ panicIdx = i
+ break
+ }
+ }
+ if panicIdx == -1 {
+ t.Fatal("no runtime.sigpanic entry on the stack")
+ }
+ if got, want := trace[panicIdx].Frame().Function, "runtime.sigpanic"; got != want {
+ t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want)
+ }
+ if got, want := trace[panicIdx+1].Frame().Function, "github.com/go-stack/stack_test.TestTracePanic"; got != want {
+ t.Errorf("TestTracePanic frame: got name == %v, want name == %v", got, want)
+ }
+ if got, want := trace[panicIdx+1].Frame().Line, line; got != want {
+ t.Errorf("TestTracePanic frame: got line == %v, want line == %v", got, want)
+ }
+ }
+ }()
+
+ _, _, line, ok = runtime.Caller(0)
+ line += 7 // adjust to match line of panic below
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ // Initiate a sigpanic.
+ var x *uintptr
+ _ = *x
+}
+
+const importPath = "github.com/go-stack/stack"
+
+type testType struct{}
+
+func (tt testType) testMethod() (c stack.Call, file string, line int, ok bool) {
+ c = stack.Caller(0)
+ _, file, line, ok = runtime.Caller(0)
+ line--
+ return
+}
+
+func TestCallFormat(t *testing.T) {
+ t.Parallel()
+
+ c := stack.Caller(0)
+ _, file, line, ok := runtime.Caller(0)
+ line--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ relFile := path.Join(importPath, filepath.Base(file))
+
+ c2, file2, line2, ok2 := testType{}.testMethod()
+ if !ok2 {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ relFile2 := path.Join(importPath, filepath.Base(file2))
+
+ data := []struct {
+ c stack.Call
+ desc string
+ fmt string
+ out string
+ }{
+ {stack.Call{}, "error", "%s", "%!s(NOFUNC)"},
+
+ {c, "func", "%s", path.Base(file)},
+ {c, "func", "%+s", relFile},
+ {c, "func", "%#s", file},
+ {c, "func", "%d", fmt.Sprint(line)},
+ {c, "func", "%n", "TestCallFormat"},
+ {c, "func", "%+n", "github.com/go-stack/stack_test.TestCallFormat"},
+ {c, "func", "%k", "stack_test"},
+ {c, "func", "%+k", "github.com/go-stack/stack_test"},
+ {c, "func", "%v", fmt.Sprint(path.Base(file), ":", line)},
+ {c, "func", "%+v", fmt.Sprint(relFile, ":", line)},
+ {c, "func", "%#v", fmt.Sprint(file, ":", line)},
+
+ {c2, "meth", "%s", path.Base(file2)},
+ {c2, "meth", "%+s", relFile2},
+ {c2, "meth", "%#s", file2},
+ {c2, "meth", "%d", fmt.Sprint(line2)},
+ {c2, "meth", "%n", "testType.testMethod"},
+ {c2, "meth", "%+n", "github.com/go-stack/stack_test.testType.testMethod"},
+ {c2, "meth", "%k", "stack_test"},
+ {c2, "meth", "%+k", "github.com/go-stack/stack_test"},
+ {c2, "meth", "%v", fmt.Sprint(path.Base(file2), ":", line2)},
+ {c2, "meth", "%+v", fmt.Sprint(relFile2, ":", line2)},
+ {c2, "meth", "%#v", fmt.Sprint(file2, ":", line2)},
+ }
+
+ for _, d := range data {
+ got := fmt.Sprintf(d.fmt, d.c)
+ if got != d.out {
+ t.Errorf("fmt.Sprintf(%q, Call(%s)) = %s, want %s", d.fmt, d.desc, got, d.out)
+ }
+ }
+}
+
+func TestCallString(t *testing.T) {
+ t.Parallel()
+
+ c := stack.Caller(0)
+ _, file, line, ok := runtime.Caller(0)
+ line--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ c2, file2, line2, ok2 := testType{}.testMethod()
+ if !ok2 {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ data := []struct {
+ c stack.Call
+ desc string
+ out string
+ }{
+ {stack.Call{}, "error", "%!v(NOFUNC)"},
+ {c, "func", fmt.Sprint(path.Base(file), ":", line)},
+ {c2, "meth", fmt.Sprint(path.Base(file2), ":", line2)},
+ }
+
+ for _, d := range data {
+ got := d.c.String()
+ if got != d.out {
+ t.Errorf("got %s, want %s", got, d.out)
+ }
+ }
+}
+
+func TestCallMarshalText(t *testing.T) {
+ t.Parallel()
+
+ c := stack.Caller(0)
+ _, file, line, ok := runtime.Caller(0)
+ line--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ c2, file2, line2, ok2 := testType{}.testMethod()
+ if !ok2 {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+
+ data := []struct {
+ c stack.Call
+ desc string
+ out []byte
+ err error
+ }{
+ {stack.Call{}, "error", nil, stack.ErrNoFunc},
+ {c, "func", []byte(fmt.Sprint(path.Base(file), ":", line)), nil},
+ {c2, "meth", []byte(fmt.Sprint(path.Base(file2), ":", line2)), nil},
+ }
+
+ for _, d := range data {
+ text, err := d.c.MarshalText()
+ if got, want := err, d.err; got != want {
+ t.Errorf("%s: got err %v, want err %v", d.desc, got, want)
+ }
+ if got, want := text, d.out; !reflect.DeepEqual(got, want) {
+ t.Errorf("%s: got %s, want %s", d.desc, got, want)
+ }
+ }
+}
+
+func TestCallStackString(t *testing.T) {
+ cs, line0 := getTrace(t)
+ _, file, line1, ok := runtime.Caller(0)
+ line1--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ file = path.Base(file)
+ if got, want := cs.String(), fmt.Sprintf("[%s:%d %s:%d]", file, line0, file, line1); got != want {
+ t.Errorf("\n got %v\nwant %v", got, want)
+ }
+}
+
+func TestCallStackMarshalText(t *testing.T) {
+ cs, line0 := getTrace(t)
+ _, file, line1, ok := runtime.Caller(0)
+ line1--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ file = path.Base(file)
+ text, _ := cs.MarshalText()
+ if got, want := text, []byte(fmt.Sprintf("[%s:%d %s:%d]", file, line0, file, line1)); !reflect.DeepEqual(got, want) {
+ t.Errorf("\n got %v\nwant %v", got, want)
+ }
+}
+
+func getTrace(t *testing.T) (stack.CallStack, int) {
+ cs := stack.Trace().TrimRuntime()
+ _, _, line, ok := runtime.Caller(0)
+ line--
+ if !ok {
+ t.Fatal("runtime.Caller(0) failed")
+ }
+ return cs, line
+}
+
+func TestTrimAbove(t *testing.T) {
+ trace := trimAbove()
+ if got, want := len(trace), 2; got != want {
+ t.Fatalf("got len(trace) == %v, want %v, trace: %n", got, want, trace)
+ }
+ if got, want := fmt.Sprintf("%n", trace[1]), "TestTrimAbove"; got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
+
+func trimAbove() stack.CallStack {
+ call := stack.Caller(1)
+ trace := stack.Trace()
+ return trace.TrimAbove(call)
+}
+
+func TestTrimBelow(t *testing.T) {
+ trace := trimBelow()
+ if got, want := fmt.Sprintf("%n", trace[0]), "TestTrimBelow"; got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
+
+func trimBelow() stack.CallStack {
+ call := stack.Caller(1)
+ trace := stack.Trace()
+ return trace.TrimBelow(call)
+}
+
+func TestTrimRuntime(t *testing.T) {
+ trace := stack.Trace().TrimRuntime()
+ if got, want := len(trace), 1; got != want {
+ t.Errorf("got len(trace) == %v, want %v, goroot: %q, trace: %#v", got, want, runtime.GOROOT(), trace)
+ }
+}
+
+func BenchmarkCallVFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprint(ioutil.Discard, c)
+ }
+}
+
+func BenchmarkCallPlusVFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%+v", c)
+ }
+}
+
+func BenchmarkCallSharpVFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%#v", c)
+ }
+}
+
+func BenchmarkCallSFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%s", c)
+ }
+}
+
+func BenchmarkCallPlusSFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%+s", c)
+ }
+}
+
+func BenchmarkCallSharpSFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%#s", c)
+ }
+}
+
+func BenchmarkCallDFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%d", c)
+ }
+}
+
+func BenchmarkCallNFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%n", c)
+ }
+}
+
+func BenchmarkCallPlusNFmt(b *testing.B) {
+ c := stack.Caller(0)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fmt.Fprintf(ioutil.Discard, "%+n", c)
+ }
+}
+
+func BenchmarkCaller(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ stack.Caller(0)
+ }
+}
+
+func BenchmarkTrace(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ stack.Trace()
+ }
+}
+
+func deepStack(depth int, b *testing.B) stack.CallStack {
+ if depth > 0 {
+ return deepStack(depth-1, b)
+ }
+ b.StartTimer()
+ s := stack.Trace()
+ return s
+}
+
+func BenchmarkTrace10(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ deepStack(10, b)
+ }
+}
+
+func BenchmarkTrace50(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ deepStack(50, b)
+ }
+}
+
+func BenchmarkTrace100(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ deepStack(100, b)
+ }
+}
+
+////////////////
+// Benchmark functions followed by formatting
+////////////////
+
+func BenchmarkCallerAndVFmt(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ fmt.Fprint(ioutil.Discard, stack.Caller(0))
+ }
+}
+
+func BenchmarkTraceAndVFmt(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ fmt.Fprint(ioutil.Discard, stack.Trace())
+ }
+}
+
+func BenchmarkTrace10AndVFmt(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ b.StopTimer()
+ fmt.Fprint(ioutil.Discard, deepStack(10, b))
+ }
+}
+
+////////////////
+// Baseline against package runtime.
+////////////////
+
+func BenchmarkRuntimeCaller(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ runtime.Caller(0)
+ }
+}
+
+func BenchmarkRuntimeCallerAndFmt(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _, file, line, _ := runtime.Caller(0)
+ const sep = "/"
+ if i := strings.LastIndex(file, sep); i != -1 {
+ file = file[i+len(sep):]
+ }
+ fmt.Fprint(ioutil.Discard, file, ":", line)
+ }
+}
+
+func BenchmarkFuncForPC(b *testing.B) {
+ pc, _, _, _ := runtime.Caller(0)
+ pc--
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ runtime.FuncForPC(pc)
+ }
+}
+
+func BenchmarkFuncFileLine(b *testing.B) {
+ pc, _, _, _ := runtime.Caller(0)
+ pc--
+ fn := runtime.FuncForPC(pc)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ fn.FileLine(pc)
+ }
+}