blob: d7c3c656ae0a3ba1fbad814b5a7b47dd6cc491fd [file] [log] [blame]
vinokumaf7605fc2023-06-02 18:08:01 +05301// 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//
54// TODO:
55// - Handle different argument/return types (e.g. ..., chan, map, interface).
56package gomock
57
58import (
59 "context"
60 "fmt"
61 "reflect"
62 "runtime"
63 "sync"
64)
65
66// A TestReporter is something that can be used to report test failures. It
67// is satisfied by the standard library's *testing.T.
68type TestReporter interface {
69 Errorf(format string, args ...interface{})
70 Fatalf(format string, args ...interface{})
71}
72
73// TestHelper is a TestReporter that has the Helper method. It is satisfied
74// by the standard library's *testing.T.
75type TestHelper interface {
76 TestReporter
77 Helper()
78}
79
80// A Controller represents the top-level control of a mock ecosystem. It
81// defines the scope and lifetime of mock objects, as well as their
82// expectations. It is safe to call Controller's methods from multiple
83// goroutines. Each test should create a new Controller and invoke Finish via
84// defer.
85//
86// func TestFoo(t *testing.T) {
87// ctrl := gomock.NewController(t)
88// defer ctrl.Finish()
89// // ..
90// }
91//
92// func TestBar(t *testing.T) {
93// t.Run("Sub-Test-1", st) {
94// ctrl := gomock.NewController(st)
95// defer ctrl.Finish()
96// // ..
97// })
98// t.Run("Sub-Test-2", st) {
99// ctrl := gomock.NewController(st)
100// defer ctrl.Finish()
101// // ..
102// })
103// })
104type Controller struct {
105 // T should only be called within a generated mock. It is not intended to
106 // be used in user code and may be changed in future versions. T is the
107 // TestReporter passed in when creating the Controller via NewController.
108 // If the TestReporter does not implement a TestHelper it will be wrapped
109 // with a nopTestHelper.
110 T TestHelper
111 mu sync.Mutex
112 expectedCalls *callSet
113 finished bool
114}
115
116// NewController returns a new Controller. It is the preferred way to create a
117// Controller.
118func NewController(t TestReporter) *Controller {
119 h, ok := t.(TestHelper)
120 if !ok {
121 h = nopTestHelper{t}
122 }
123
124 return &Controller{
125 T: h,
126 expectedCalls: newCallSet(),
127 }
128}
129
130type cancelReporter struct {
131 TestHelper
132 cancel func()
133}
134
135func (r *cancelReporter) Errorf(format string, args ...interface{}) {
136 r.TestHelper.Errorf(format, args...)
137}
138func (r *cancelReporter) Fatalf(format string, args ...interface{}) {
139 defer r.cancel()
140 r.TestHelper.Fatalf(format, args...)
141}
142
143// WithContext returns a new Controller and a Context, which is cancelled on any
144// fatal failure.
145func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {
146 h, ok := t.(TestHelper)
147 if !ok {
148 h = nopTestHelper{t}
149 }
150
151 ctx, cancel := context.WithCancel(ctx)
152 return NewController(&cancelReporter{h, cancel}), ctx
153}
154
155type nopTestHelper struct {
156 TestReporter
157}
158
159func (h nopTestHelper) Helper() {}
160
161// RecordCall is called by a mock. It should not be called by user code.
162func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {
163 ctrl.T.Helper()
164
165 recv := reflect.ValueOf(receiver)
166 for i := 0; i < recv.Type().NumMethod(); i++ {
167 if recv.Type().Method(i).Name == method {
168 return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
169 }
170 }
171 ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)
172 panic("unreachable")
173}
174
175// RecordCallWithMethodType is called by a mock. It should not be called by user code.
176func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
177 ctrl.T.Helper()
178
179 call := newCall(ctrl.T, receiver, method, methodType, args...)
180
181 ctrl.mu.Lock()
182 defer ctrl.mu.Unlock()
183 ctrl.expectedCalls.Add(call)
184
185 return call
186}
187
188// Call is called by a mock. It should not be called by user code.
189func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {
190 ctrl.T.Helper()
191
192 // Nest this code so we can use defer to make sure the lock is released.
193 actions := func() []func([]interface{}) []interface{} {
194 ctrl.T.Helper()
195 ctrl.mu.Lock()
196 defer ctrl.mu.Unlock()
197
198 expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
199 if err != nil {
200 origin := callerInfo(2)
201 ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err)
202 }
203
204 // Two things happen here:
205 // * the matching call no longer needs to check prerequite calls,
206 // * and the prerequite calls are no longer expected, so remove them.
207 preReqCalls := expected.dropPrereqs()
208 for _, preReqCall := range preReqCalls {
209 ctrl.expectedCalls.Remove(preReqCall)
210 }
211
212 actions := expected.call()
213 if expected.exhausted() {
214 ctrl.expectedCalls.Remove(expected)
215 }
216 return actions
217 }()
218
219 var rets []interface{}
220 for _, action := range actions {
221 if r := action(args); r != nil {
222 rets = r
223 }
224 }
225
226 return rets
227}
228
229// Finish checks to see if all the methods that were expected to be called
230// were called. It should be invoked for each Controller. It is not idempotent
231// and therefore can only be invoked once.
232func (ctrl *Controller) Finish() {
233 ctrl.T.Helper()
234
235 ctrl.mu.Lock()
236 defer ctrl.mu.Unlock()
237
238 if ctrl.finished {
239 ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
240 }
241 ctrl.finished = true
242
243 // If we're currently panicking, probably because this is a deferred call,
244 // pass through the panic.
245 if err := recover(); err != nil {
246 panic(err)
247 }
248
249 // Check that all remaining expected calls are satisfied.
250 failures := ctrl.expectedCalls.Failures()
251 for _, call := range failures {
252 ctrl.T.Errorf("missing call(s) to %v", call)
253 }
254 if len(failures) != 0 {
255 ctrl.T.Fatalf("aborting test due to missing call(s)")
256 }
257}
258
259func callerInfo(skip int) string {
260 if _, file, line, ok := runtime.Caller(skip + 1); ok {
261 return fmt.Sprintf("%s:%d", file, line)
262 }
263 return "unknown file"
264}