Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/golang/mock/gomock/controller.go
2880 views
1
// Copyright 2010 Google Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
// Package gomock is a mock framework for Go.
16
//
17
// Standard usage:
18
// (1) Define an interface that you wish to mock.
19
// type MyInterface interface {
20
// SomeMethod(x int64, y string)
21
// }
22
// (2) Use mockgen to generate a mock from the interface.
23
// (3) Use the mock in a test:
24
// func TestMyThing(t *testing.T) {
25
// mockCtrl := gomock.NewController(t)
26
// defer mockCtrl.Finish()
27
//
28
// mockObj := something.NewMockMyInterface(mockCtrl)
29
// mockObj.EXPECT().SomeMethod(4, "blah")
30
// // pass mockObj to a real object and play with it.
31
// }
32
//
33
// By default, expected calls are not enforced to run in any particular order.
34
// Call order dependency can be enforced by use of InOrder and/or Call.After.
35
// Call.After can create more varied call order dependencies, but InOrder is
36
// often more convenient.
37
//
38
// The following examples create equivalent call order dependencies.
39
//
40
// Example of using Call.After to chain expected call order:
41
//
42
// firstCall := mockObj.EXPECT().SomeMethod(1, "first")
43
// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
44
// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
45
//
46
// Example of using InOrder to declare expected call order:
47
//
48
// gomock.InOrder(
49
// mockObj.EXPECT().SomeMethod(1, "first"),
50
// mockObj.EXPECT().SomeMethod(2, "second"),
51
// mockObj.EXPECT().SomeMethod(3, "third"),
52
// )
53
package gomock
54
55
import (
56
"context"
57
"fmt"
58
"reflect"
59
"runtime"
60
"sync"
61
)
62
63
// A TestReporter is something that can be used to report test failures. It
64
// is satisfied by the standard library's *testing.T.
65
type TestReporter interface {
66
Errorf(format string, args ...interface{})
67
Fatalf(format string, args ...interface{})
68
}
69
70
// TestHelper is a TestReporter that has the Helper method. It is satisfied
71
// by the standard library's *testing.T.
72
type TestHelper interface {
73
TestReporter
74
Helper()
75
}
76
77
// cleanuper is used to check if TestHelper also has the `Cleanup` method. A
78
// common pattern is to pass in a `*testing.T` to
79
// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup
80
// method. This can be utilized to call `Finish()` so the caller of this library
81
// does not have to.
82
type cleanuper interface {
83
Cleanup(func())
84
}
85
86
// A Controller represents the top-level control of a mock ecosystem. It
87
// defines the scope and lifetime of mock objects, as well as their
88
// expectations. It is safe to call Controller's methods from multiple
89
// goroutines. Each test should create a new Controller and invoke Finish via
90
// defer.
91
//
92
// func TestFoo(t *testing.T) {
93
// ctrl := gomock.NewController(t)
94
// defer ctrl.Finish()
95
// // ..
96
// }
97
//
98
// func TestBar(t *testing.T) {
99
// t.Run("Sub-Test-1", st) {
100
// ctrl := gomock.NewController(st)
101
// defer ctrl.Finish()
102
// // ..
103
// })
104
// t.Run("Sub-Test-2", st) {
105
// ctrl := gomock.NewController(st)
106
// defer ctrl.Finish()
107
// // ..
108
// })
109
// })
110
type Controller struct {
111
// T should only be called within a generated mock. It is not intended to
112
// be used in user code and may be changed in future versions. T is the
113
// TestReporter passed in when creating the Controller via NewController.
114
// If the TestReporter does not implement a TestHelper it will be wrapped
115
// with a nopTestHelper.
116
T TestHelper
117
mu sync.Mutex
118
expectedCalls *callSet
119
finished bool
120
}
121
122
// NewController returns a new Controller. It is the preferred way to create a
123
// Controller.
124
//
125
// New in go1.14+, if you are passing a *testing.T into this function you no
126
// longer need to call ctrl.Finish() in your test methods.
127
func NewController(t TestReporter) *Controller {
128
h, ok := t.(TestHelper)
129
if !ok {
130
h = &nopTestHelper{t}
131
}
132
ctrl := &Controller{
133
T: h,
134
expectedCalls: newCallSet(),
135
}
136
if c, ok := isCleanuper(ctrl.T); ok {
137
c.Cleanup(func() {
138
ctrl.T.Helper()
139
ctrl.finish(true, nil)
140
})
141
}
142
143
return ctrl
144
}
145
146
type cancelReporter struct {
147
t TestHelper
148
cancel func()
149
}
150
151
func (r *cancelReporter) Errorf(format string, args ...interface{}) {
152
r.t.Errorf(format, args...)
153
}
154
func (r *cancelReporter) Fatalf(format string, args ...interface{}) {
155
defer r.cancel()
156
r.t.Fatalf(format, args...)
157
}
158
159
func (r *cancelReporter) Helper() {
160
r.t.Helper()
161
}
162
163
// WithContext returns a new Controller and a Context, which is cancelled on any
164
// fatal failure.
165
func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {
166
h, ok := t.(TestHelper)
167
if !ok {
168
h = &nopTestHelper{t: t}
169
}
170
171
ctx, cancel := context.WithCancel(ctx)
172
return NewController(&cancelReporter{t: h, cancel: cancel}), ctx
173
}
174
175
type nopTestHelper struct {
176
t TestReporter
177
}
178
179
func (h *nopTestHelper) Errorf(format string, args ...interface{}) {
180
h.t.Errorf(format, args...)
181
}
182
func (h *nopTestHelper) Fatalf(format string, args ...interface{}) {
183
h.t.Fatalf(format, args...)
184
}
185
186
func (h nopTestHelper) Helper() {}
187
188
// RecordCall is called by a mock. It should not be called by user code.
189
func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {
190
ctrl.T.Helper()
191
192
recv := reflect.ValueOf(receiver)
193
for i := 0; i < recv.Type().NumMethod(); i++ {
194
if recv.Type().Method(i).Name == method {
195
return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
196
}
197
}
198
ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)
199
panic("unreachable")
200
}
201
202
// RecordCallWithMethodType is called by a mock. It should not be called by user code.
203
func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
204
ctrl.T.Helper()
205
206
call := newCall(ctrl.T, receiver, method, methodType, args...)
207
208
ctrl.mu.Lock()
209
defer ctrl.mu.Unlock()
210
ctrl.expectedCalls.Add(call)
211
212
return call
213
}
214
215
// Call is called by a mock. It should not be called by user code.
216
func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {
217
ctrl.T.Helper()
218
219
// Nest this code so we can use defer to make sure the lock is released.
220
actions := func() []func([]interface{}) []interface{} {
221
ctrl.T.Helper()
222
ctrl.mu.Lock()
223
defer ctrl.mu.Unlock()
224
225
expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
226
if err != nil {
227
// callerInfo's skip should be updated if the number of calls between the user's test
228
// and this line changes, i.e. this code is wrapped in another anonymous function.
229
// 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test.
230
origin := callerInfo(3)
231
ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err)
232
}
233
234
// Two things happen here:
235
// * the matching call no longer needs to check prerequite calls,
236
// * and the prerequite calls are no longer expected, so remove them.
237
preReqCalls := expected.dropPrereqs()
238
for _, preReqCall := range preReqCalls {
239
ctrl.expectedCalls.Remove(preReqCall)
240
}
241
242
actions := expected.call()
243
if expected.exhausted() {
244
ctrl.expectedCalls.Remove(expected)
245
}
246
return actions
247
}()
248
249
var rets []interface{}
250
for _, action := range actions {
251
if r := action(args); r != nil {
252
rets = r
253
}
254
}
255
256
return rets
257
}
258
259
// Finish checks to see if all the methods that were expected to be called
260
// were called. It should be invoked for each Controller. It is not idempotent
261
// and therefore can only be invoked once.
262
//
263
// New in go1.14+, if you are passing a *testing.T into NewController function you no
264
// longer need to call ctrl.Finish() in your test methods.
265
func (ctrl *Controller) Finish() {
266
// If we're currently panicking, probably because this is a deferred call.
267
// This must be recovered in the deferred function.
268
err := recover()
269
ctrl.finish(false, err)
270
}
271
272
func (ctrl *Controller) finish(cleanup bool, panicErr interface{}) {
273
ctrl.T.Helper()
274
275
ctrl.mu.Lock()
276
defer ctrl.mu.Unlock()
277
278
if ctrl.finished {
279
if _, ok := isCleanuper(ctrl.T); !ok {
280
ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
281
}
282
return
283
}
284
ctrl.finished = true
285
286
// Short-circuit, pass through the panic.
287
if panicErr != nil {
288
panic(panicErr)
289
}
290
291
// Check that all remaining expected calls are satisfied.
292
failures := ctrl.expectedCalls.Failures()
293
for _, call := range failures {
294
ctrl.T.Errorf("missing call(s) to %v", call)
295
}
296
if len(failures) != 0 {
297
if !cleanup {
298
ctrl.T.Fatalf("aborting test due to missing call(s)")
299
return
300
}
301
ctrl.T.Errorf("aborting test due to missing call(s)")
302
}
303
}
304
305
// callerInfo returns the file:line of the call site. skip is the number
306
// of stack frames to skip when reporting. 0 is callerInfo's call site.
307
func callerInfo(skip int) string {
308
if _, file, line, ok := runtime.Caller(skip + 1); ok {
309
return fmt.Sprintf("%s:%d", file, line)
310
}
311
return "unknown file"
312
}
313
314
// isCleanuper checks it if t's base TestReporter has a Cleanup method.
315
func isCleanuper(t TestReporter) (cleanuper, bool) {
316
tr := unwrapTestReporter(t)
317
c, ok := tr.(cleanuper)
318
return c, ok
319
}
320
321
// unwrapTestReporter unwraps TestReporter to the base implementation.
322
func unwrapTestReporter(t TestReporter) TestReporter {
323
tr := t
324
switch nt := t.(type) {
325
case *cancelReporter:
326
tr = nt.t
327
if h, check := tr.(*nopTestHelper); check {
328
tr = h.t
329
}
330
case *nopTestHelper:
331
tr = nt.t
332
default:
333
// not wrapped
334
}
335
return tr
336
}
337
338