trpc.group/trpc-go/trpc-go@v1.0.3/errs/stack_test.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package errs 15 16 import ( 17 "fmt" 18 "regexp" 19 "runtime" 20 "strings" 21 "testing" 22 ) 23 24 var initpc = caller() 25 26 type X struct{} 27 28 // val returns a Frame pointing to itself. 29 func (x X) val() frame { 30 return caller() 31 } 32 33 // ptr returns a Frame pointing to itself. 34 func (x *X) ptr() frame { 35 return caller() 36 } 37 38 func TestFrameFormat(t *testing.T) { 39 var tests = []struct { 40 frame 41 format string 42 want string 43 }{{ 44 initpc, 45 "%s", 46 "stack_test.go", 47 }, { 48 initpc, 49 "%+s", 50 "trpc.group/trpc-go/trpc-go/errs.init\n" + 51 "\t.+errs/stack_test.go", 52 }, { 53 0, 54 "%s", 55 "unknown", 56 }, { 57 0, 58 "%+s", 59 "unknown", 60 }, { 61 initpc, 62 "%d", 63 "24", 64 }, { 65 0, 66 "%d", 67 "0", 68 }, { 69 initpc, 70 "%n", 71 "init", 72 }, { 73 func() frame { 74 var x X 75 return x.ptr() 76 }(), 77 "%n", 78 `\(\*X\).ptr`, 79 }, { 80 func() frame { 81 var x X 82 return x.val() 83 }(), 84 "%n", 85 "X.val", 86 }, { 87 0, 88 "%n", 89 "", 90 }, { 91 initpc, 92 "%v", 93 "stack_test.go:24", 94 }, { 95 initpc, 96 "%+v", 97 "trpc.group/trpc-go/trpc-go/errs.init\n" + 98 "\t.+errs/stack_test.go", 99 }, { 100 0, 101 "%v", 102 "unknown:0", 103 }} 104 105 for i, tt := range tests { 106 testFormatRegexp(t, i, tt.frame, tt.format, tt.want) 107 } 108 } 109 110 func TestFuncname(t *testing.T) { 111 tests := []struct { 112 name, want string 113 }{ 114 {"", ""}, 115 {"runtime.main", "main"}, 116 {"github.com/pkg/errors.funcname", "funcname"}, 117 {"funcname", "funcname"}, 118 {"io.copyBuffer", "copyBuffer"}, 119 {"main.(*R).Write", "(*R).Write"}, 120 } 121 122 for _, tt := range tests { 123 got := funcName(tt.name) 124 want := tt.want 125 if got != want { 126 t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got) 127 } 128 } 129 } 130 131 func getStackTrace() stackTrace { 132 const depth = 8 133 var pcs [depth]uintptr 134 n := runtime.Callers(1, pcs[:]) 135 stack := pcs[0:n] 136 // convert to errors.StackTrace 137 st := make([]frame, len(stack)) 138 for i := 0; i < len(st); i++ { 139 st[i] = frame((stack)[i]) 140 } 141 return st 142 } 143 144 func TestStackTraceFormat(t *testing.T) { 145 tests := []struct { 146 stackTrace 147 format string 148 want string 149 }{{ 150 nil, 151 "%s", 152 `\[\]`, 153 }, { 154 nil, 155 "%v", 156 `\[\]`, 157 }, { 158 nil, 159 "%+v", 160 "", 161 }, { 162 nil, 163 "%#v", 164 `\[\]errs\.frame\(nil\)`, 165 }, { 166 make(stackTrace, 0), 167 "%s", 168 `\[\]`, 169 }, { 170 make(stackTrace, 0), 171 "%v", 172 `\[\]`, 173 }, { 174 make(stackTrace, 0), 175 "%+v", 176 "", 177 }, { 178 make(stackTrace, 0), 179 "%#v", 180 `\[\]errs\.frame{}`, 181 }, { 182 getStackTrace()[:2], 183 "%s", 184 `\[stack_test.go stack_test.go\]`, 185 }, { 186 getStackTrace()[:2], 187 "%v", 188 `\[stack_test.go:134 stack_test.go:186\]`, 189 }, { 190 getStackTrace()[:2], 191 "%+v", 192 "\n" + 193 "trpc.group/trpc-go/trpc-go/errs.getStackTrace\n" + 194 "\t.+errs/stack_test.go:134\n" + 195 "trpc.group/trpc-go/trpc-go/errs.TestStackTraceFormat\n" + 196 "\t.+errs/stack_test.go:190", 197 }, { 198 getStackTrace()[:2], 199 "%#v", 200 `\[\]errs\.frame{stack_test.go:134, stack_test.go:198}`, 201 }} 202 203 for i, tt := range tests { 204 testFormatRegexp(t, i, tt.stackTrace, tt.format, tt.want) 205 } 206 } 207 208 // a version of runtime.Caller that returns a Frame, not a uintptr. 209 func caller() frame { 210 var pcs [3]uintptr 211 n := runtime.Callers(2, pcs[:]) 212 frames := runtime.CallersFrames(pcs[:n]) 213 nextFrame, _ := frames.Next() 214 return frame(nextFrame.PC) 215 } 216 217 func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) { 218 t.Helper() 219 got := fmt.Sprintf(format, arg) 220 gotLines := strings.SplitN(got, "\n", -1) 221 wantLines := strings.SplitN(want, "\n", -1) 222 223 if len(wantLines) > len(gotLines) { 224 t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want) 225 return 226 } 227 228 for i, w := range wantLines { 229 match, err := regexp.MatchString(w, gotLines[i]) 230 if err != nil { 231 t.Fatal(err) 232 } 233 if !match { 234 t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want) 235 } 236 } 237 }