1
18
19 package grpctest
20
21 import (
22 "errors"
23 "fmt"
24 "os"
25 "path"
26 "regexp"
27 "runtime"
28 "strconv"
29 "sync"
30 "testing"
31 "time"
32
33 "google.golang.org/grpc/grpclog"
34 )
35
36
37
38 var TLogger *tLogger
39
40 const callingFrame = 4
41
42 type logType int
43
44 func (l logType) String() string {
45 switch l {
46 case infoLog:
47 return "INFO"
48 case warningLog:
49 return "WARNING"
50 case errorLog:
51 return "ERROR"
52 case fatalLog:
53 return "FATAL"
54 }
55 return "UNKNOWN"
56 }
57
58 const (
59 infoLog logType = iota
60 warningLog
61 errorLog
62 fatalLog
63 )
64
65 type tLogger struct {
66 v int
67 initialized bool
68
69 mu sync.Mutex
70 t *testing.T
71 start time.Time
72 errors map[*regexp.Regexp]int
73 }
74
75 func init() {
76 TLogger = &tLogger{errors: map[*regexp.Regexp]int{}}
77 vLevel := os.Getenv("GRPC_GO_LOG_VERBOSITY_LEVEL")
78 if vl, err := strconv.Atoi(vLevel); err == nil {
79 TLogger.v = vl
80 }
81 }
82
83
84 func getCallingPrefix(depth int) (string, error) {
85 _, file, line, ok := runtime.Caller(depth)
86 if !ok {
87 return "", errors.New("frame request out-of-bounds")
88 }
89 return fmt.Sprintf("%s:%d", path.Base(file), line), nil
90 }
91
92
93 func (g *tLogger) log(ltype logType, depth int, format string, args ...any) {
94 g.mu.Lock()
95 defer g.mu.Unlock()
96 prefix, err := getCallingPrefix(callingFrame + depth)
97 if err != nil {
98 g.t.Error(err)
99 return
100 }
101 args = append([]any{ltype.String() + " " + prefix}, args...)
102 args = append(args, fmt.Sprintf(" (t=+%s)", time.Since(g.start)))
103
104 if format == "" {
105 switch ltype {
106 case errorLog:
107
108 if g.expected(fmt.Sprintln(args...)) {
109 g.t.Log(args...)
110 } else {
111 g.t.Error(args...)
112 }
113 case fatalLog:
114 panic(fmt.Sprint(args...))
115 default:
116 g.t.Log(args...)
117 }
118 } else {
119
120 format = "%v " + format + "%s"
121 switch ltype {
122 case errorLog:
123 if g.expected(fmt.Sprintf(format, args...)) {
124 g.t.Logf(format, args...)
125 } else {
126 g.t.Errorf(format, args...)
127 }
128 case fatalLog:
129 panic(fmt.Sprintf(format, args...))
130 default:
131 g.t.Logf(format, args...)
132 }
133 }
134 }
135
136
137
138 func (g *tLogger) Update(t *testing.T) {
139 g.mu.Lock()
140 defer g.mu.Unlock()
141 if !g.initialized {
142 grpclog.SetLoggerV2(TLogger)
143 g.initialized = true
144 }
145 g.t = t
146 g.start = time.Now()
147 g.errors = map[*regexp.Regexp]int{}
148 }
149
150
151
152
153
154
155 func (g *tLogger) ExpectError(expr string) {
156 g.ExpectErrorN(expr, 1)
157 }
158
159
160 func (g *tLogger) ExpectErrorN(expr string, n int) {
161 g.mu.Lock()
162 defer g.mu.Unlock()
163 re, err := regexp.Compile(expr)
164 if err != nil {
165 g.t.Error(err)
166 return
167 }
168 g.errors[re] += n
169 }
170
171
172 func (g *tLogger) EndTest(t *testing.T) {
173 g.mu.Lock()
174 defer g.mu.Unlock()
175 for re, count := range g.errors {
176 if count > 0 {
177 t.Errorf("Expected error '%v' not encountered", re.String())
178 }
179 }
180 g.errors = map[*regexp.Regexp]int{}
181 }
182
183
184 func (g *tLogger) expected(s string) bool {
185 for re, count := range g.errors {
186 if re.FindStringIndex(s) != nil {
187 g.errors[re]--
188 if count <= 1 {
189 delete(g.errors, re)
190 }
191 return true
192 }
193 }
194 return false
195 }
196
197 func (g *tLogger) Info(args ...any) {
198 g.log(infoLog, 0, "", args...)
199 }
200
201 func (g *tLogger) Infoln(args ...any) {
202 g.log(infoLog, 0, "", args...)
203 }
204
205 func (g *tLogger) Infof(format string, args ...any) {
206 g.log(infoLog, 0, format, args...)
207 }
208
209 func (g *tLogger) InfoDepth(depth int, args ...any) {
210 g.log(infoLog, depth, "", args...)
211 }
212
213 func (g *tLogger) Warning(args ...any) {
214 g.log(warningLog, 0, "", args...)
215 }
216
217 func (g *tLogger) Warningln(args ...any) {
218 g.log(warningLog, 0, "", args...)
219 }
220
221 func (g *tLogger) Warningf(format string, args ...any) {
222 g.log(warningLog, 0, format, args...)
223 }
224
225 func (g *tLogger) WarningDepth(depth int, args ...any) {
226 g.log(warningLog, depth, "", args...)
227 }
228
229 func (g *tLogger) Error(args ...any) {
230 g.log(errorLog, 0, "", args...)
231 }
232
233 func (g *tLogger) Errorln(args ...any) {
234 g.log(errorLog, 0, "", args...)
235 }
236
237 func (g *tLogger) Errorf(format string, args ...any) {
238 g.log(errorLog, 0, format, args...)
239 }
240
241 func (g *tLogger) ErrorDepth(depth int, args ...any) {
242 g.log(errorLog, depth, "", args...)
243 }
244
245 func (g *tLogger) Fatal(args ...any) {
246 g.log(fatalLog, 0, "", args...)
247 }
248
249 func (g *tLogger) Fatalln(args ...any) {
250 g.log(fatalLog, 0, "", args...)
251 }
252
253 func (g *tLogger) Fatalf(format string, args ...any) {
254 g.log(fatalLog, 0, format, args...)
255 }
256
257 func (g *tLogger) FatalDepth(depth int, args ...any) {
258 g.log(fatalLog, depth, "", args...)
259 }
260
261 func (g *tLogger) V(l int) bool {
262 return l <= g.v
263 }
264
View as plain text