1package errors 2 3import ( 4 "fmt" 5 "runtime" 6 "testing" 7) 8 9var initpc, _, _, _ = runtime.Caller(0) 10 11func TestFrameLine(t *testing.T) { 12 var tests = []struct { 13 Frame 14 want int 15 }{{ 16 Frame(initpc), 17 9, 18 }, { 19 func() Frame { 20 var pc, _, _, _ = runtime.Caller(0) 21 return Frame(pc) 22 }(), 23 20, 24 }, { 25 func() Frame { 26 var pc, _, _, _ = runtime.Caller(1) 27 return Frame(pc) 28 }(), 29 28, 30 }, { 31 Frame(0), // invalid PC 32 0, 33 }} 34 35 for _, tt := range tests { 36 got := tt.Frame.line() 37 want := tt.want 38 if want != got { 39 t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got) 40 } 41 } 42} 43 44type X struct{} 45 46func (x X) val() Frame { 47 var pc, _, _, _ = runtime.Caller(0) 48 return Frame(pc) 49} 50 51func (x *X) ptr() Frame { 52 var pc, _, _, _ = runtime.Caller(0) 53 return Frame(pc) 54} 55 56func TestFrameFormat(t *testing.T) { 57 var tests = []struct { 58 Frame 59 format string 60 want string 61 }{{ 62 Frame(initpc), 63 "%s", 64 "stack_test.go", 65 }, { 66 Frame(initpc), 67 "%+s", 68 "github.com/pkg/errors.init\n" + 69 "\t.+/github.com/pkg/errors/stack_test.go", 70 }, { 71 Frame(0), 72 "%s", 73 "unknown", 74 }, { 75 Frame(0), 76 "%+s", 77 "unknown", 78 }, { 79 Frame(initpc), 80 "%d", 81 "9", 82 }, { 83 Frame(0), 84 "%d", 85 "0", 86 }, { 87 Frame(initpc), 88 "%n", 89 "init", 90 }, { 91 func() Frame { 92 var x X 93 return x.ptr() 94 }(), 95 "%n", 96 `\(\*X\).ptr`, 97 }, { 98 func() Frame { 99 var x X 100 return x.val() 101 }(), 102 "%n", 103 "X.val", 104 }, { 105 Frame(0), 106 "%n", 107 "", 108 }, { 109 Frame(initpc), 110 "%v", 111 "stack_test.go:9", 112 }, { 113 Frame(initpc), 114 "%+v", 115 "github.com/pkg/errors.init\n" + 116 "\t.+/github.com/pkg/errors/stack_test.go:9", 117 }, { 118 Frame(0), 119 "%v", 120 "unknown:0", 121 }} 122 123 for i, tt := range tests { 124 testFormatRegexp(t, i, tt.Frame, tt.format, tt.want) 125 } 126} 127 128func TestFuncname(t *testing.T) { 129 tests := []struct { 130 name, want string 131 }{ 132 {"", ""}, 133 {"runtime.main", "main"}, 134 {"github.com/pkg/errors.funcname", "funcname"}, 135 {"funcname", "funcname"}, 136 {"io.copyBuffer", "copyBuffer"}, 137 {"main.(*R).Write", "(*R).Write"}, 138 } 139 140 for _, tt := range tests { 141 got := funcname(tt.name) 142 want := tt.want 143 if got != want { 144 t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) 145 } 146 } 147} 148 149func TestStackTrace(t *testing.T) { 150 tests := []struct { 151 err error 152 want []string 153 }{{ 154 New("ooh"), []string{ 155 "github.com/pkg/errors.TestStackTrace\n" + 156 "\t.+/github.com/pkg/errors/stack_test.go:154", 157 }, 158 }, { 159 Wrap(New("ooh"), "ahh"), []string{ 160 "github.com/pkg/errors.TestStackTrace\n" + 161 "\t.+/github.com/pkg/errors/stack_test.go:159", // this is the stack of Wrap, not New 162 }, 163 }, { 164 Cause(Wrap(New("ooh"), "ahh")), []string{ 165 "github.com/pkg/errors.TestStackTrace\n" + 166 "\t.+/github.com/pkg/errors/stack_test.go:164", // this is the stack of New 167 }, 168 }, { 169 func() error { return New("ooh") }(), []string{ 170 `github.com/pkg/errors.(func·009|TestStackTrace.func1)` + 171 "\n\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New 172 "github.com/pkg/errors.TestStackTrace\n" + 173 "\t.+/github.com/pkg/errors/stack_test.go:169", // this is the stack of New's caller 174 }, 175 }, { 176 Cause(func() error { 177 return func() error { 178 return Errorf("hello %s", fmt.Sprintf("world")) 179 }() 180 }()), []string{ 181 `github.com/pkg/errors.(func·010|TestStackTrace.func2.1)` + 182 "\n\t.+/github.com/pkg/errors/stack_test.go:178", // this is the stack of Errorf 183 `github.com/pkg/errors.(func·011|TestStackTrace.func2)` + 184 "\n\t.+/github.com/pkg/errors/stack_test.go:179", // this is the stack of Errorf's caller 185 "github.com/pkg/errors.TestStackTrace\n" + 186 "\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Errorf's caller's caller 187 }, 188 }} 189 for i, tt := range tests { 190 x, ok := tt.err.(interface { 191 StackTrace() StackTrace 192 }) 193 if !ok { 194 t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err) 195 continue 196 } 197 st := x.StackTrace() 198 for j, want := range tt.want { 199 testFormatRegexp(t, i, st[j], "%+v", want) 200 } 201 } 202} 203 204func stackTrace() StackTrace { 205 const depth = 8 206 var pcs [depth]uintptr 207 n := runtime.Callers(1, pcs[:]) 208 var st stack = pcs[0:n] 209 return st.StackTrace() 210} 211 212func TestStackTraceFormat(t *testing.T) { 213 tests := []struct { 214 StackTrace 215 format string 216 want string 217 }{{ 218 nil, 219 "%s", 220 `\[\]`, 221 }, { 222 nil, 223 "%v", 224 `\[\]`, 225 }, { 226 nil, 227 "%+v", 228 "", 229 }, { 230 nil, 231 "%#v", 232 `\[\]errors.Frame\(nil\)`, 233 }, { 234 make(StackTrace, 0), 235 "%s", 236 `\[\]`, 237 }, { 238 make(StackTrace, 0), 239 "%v", 240 `\[\]`, 241 }, { 242 make(StackTrace, 0), 243 "%+v", 244 "", 245 }, { 246 make(StackTrace, 0), 247 "%#v", 248 `\[\]errors.Frame{}`, 249 }, { 250 stackTrace()[:2], 251 "%s", 252 `\[stack_test.go stack_test.go\]`, 253 }, { 254 stackTrace()[:2], 255 "%v", 256 `\[stack_test.go:207 stack_test.go:254\]`, 257 }, { 258 stackTrace()[:2], 259 "%+v", 260 "\n" + 261 "github.com/pkg/errors.stackTrace\n" + 262 "\t.+/github.com/pkg/errors/stack_test.go:207\n" + 263 "github.com/pkg/errors.TestStackTraceFormat\n" + 264 "\t.+/github.com/pkg/errors/stack_test.go:258", 265 }, { 266 stackTrace()[:2], 267 "%#v", 268 `\[\]errors.Frame{stack_test.go:207, stack_test.go:266}`, 269 }} 270 271 for i, tt := range tests { 272 testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want) 273 } 274} 275