1
2
3
4 package stackdump_test
5
6 import (
7 "bytes"
8 "fmt"
9 "regexp"
10 "runtime"
11 "testing"
12
13 "github.com/golang/glog/internal/stackdump"
14 )
15
16 var file string
17
18 func init() {
19 _, file, _, _ = runtime.Caller(0)
20 }
21
22 func TestCallerText(t *testing.T) {
23 stack := stackdump.CallerText(0)
24 _, _, line, _ := runtime.Caller(0)
25 line--
26
27 wantRE := regexp.MustCompile(fmt.Sprintf(
28 `^goroutine \d+ \[running\]:
29 github.com/golang/glog/internal/stackdump_test\.TestCallerText(\([^)]*\))?
30 %v:%v.*
31 `, file, line))
32 if !wantRE.Match(stack) {
33 t.Errorf("Stack dump:\n%s\nwant matching regexp:\n%s", stack, wantRE.String())
34
35 buf := make([]byte, len(stack)*2)
36 origStack := buf[:runtime.Stack(buf, false)]
37 t.Logf("Unpruned stack:\n%s", origStack)
38 }
39 }
40
41 func callerAt(calls int, depth int) (stack []byte) {
42 if calls == 1 {
43 return stackdump.CallerText(depth)
44 }
45 return callerAt(calls-1, depth)
46 }
47
48 func TestCallerTextSkip(t *testing.T) {
49 const calls = 3
50 cases := []struct {
51 depth int
52 callerAtFrames int
53 wantEndOfStack bool
54 }{
55 {depth: 0, callerAtFrames: calls},
56 {depth: calls - 1, callerAtFrames: 1},
57 {depth: calls, callerAtFrames: 0},
58 {depth: calls + 1, callerAtFrames: 0},
59 {depth: calls + 100, wantEndOfStack: true},
60 }
61
62 for _, tc := range cases {
63 stack := callerAt(calls, tc.depth)
64
65 wantREBuf := bytes.NewBuffer(nil)
66 fmt.Fprintf(wantREBuf, `^goroutine \d+ \[running\]:
67 `)
68 if tc.wantEndOfStack {
69 fmt.Fprintf(wantREBuf, "\n|$")
70 } else {
71 for n := tc.callerAtFrames; n > 0; n-- {
72 fmt.Fprintf(wantREBuf, `github.com/golang/glog/internal/stackdump_test\.callerAt(\([^)]*\))?
73 %v:\d+.*
74 `, file)
75 }
76
77 if tc.depth <= calls {
78 fmt.Fprintf(wantREBuf, `github.com/golang/glog/internal/stackdump_test\.TestCallerTextSkip(\([^)]*\))?
79 %v:\d+.*
80 `, file)
81 }
82 }
83
84 wantRE := regexp.MustCompile(wantREBuf.String())
85
86 if !wantRE.Match(stack) {
87 t.Errorf("for %v calls, stackdump.CallerText(%v) =\n%s\n\nwant matching regexp:\n%s", calls, tc.depth, stack, wantRE.String())
88 }
89 }
90 }
91
92 func pcAt(calls int, depth int) (stack []uintptr) {
93 if calls == 1 {
94 return stackdump.CallerPC(depth)
95 }
96 stack = pcAt(calls-1, depth)
97 runtime.Gosched()
98 return stack
99 }
100
101 func TestCallerPC(t *testing.T) {
102 const calls = 3
103 cases := []struct {
104 depth int
105 pcAtFrames int
106 wantEndOfStack bool
107 }{
108 {depth: 0, pcAtFrames: calls},
109 {depth: calls - 1, pcAtFrames: 1},
110 {depth: calls, pcAtFrames: 0},
111 {depth: calls + 1, pcAtFrames: 0},
112 {depth: calls + 100, wantEndOfStack: true},
113 }
114
115 for _, tc := range cases {
116 stack := pcAt(calls, tc.depth)
117 if tc.wantEndOfStack {
118 if len(stack) != 0 {
119 t.Errorf("for %v calls, stackdump.CallerPC(%v) =\n%q\nwant []", calls, tc.depth, stack)
120 }
121 continue
122 }
123
124 wantFuncs := []string{}
125 for n := tc.pcAtFrames; n > 0; n-- {
126 wantFuncs = append(wantFuncs, `github.com/golang/glog/internal/stackdump_test\.pcAt$`)
127 }
128 if tc.depth <= calls {
129 wantFuncs = append(wantFuncs, `^github.com/golang/glog/internal/stackdump_test\.TestCallerPC$`)
130 }
131
132 gotFuncs := []string{}
133 for _, pc := range stack {
134 gotFuncs = append(gotFuncs, runtime.FuncForPC(pc).Name())
135 }
136 if len(gotFuncs) > len(wantFuncs) {
137 gotFuncs = gotFuncs[:len(wantFuncs)]
138 }
139
140 ok := true
141 for i, want := range wantFuncs {
142 re := regexp.MustCompile(want)
143 if i >= len(gotFuncs) || !re.MatchString(gotFuncs[i]) {
144 ok = false
145 break
146 }
147 }
148 if !ok {
149 t.Errorf("for %v calls, stackdump.CallerPC(%v) =\n%q\nwant %q", calls, tc.depth, gotFuncs, wantFuncs)
150 }
151 }
152 }
153
View as plain text