vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 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 | // |
| 54 | // TODO: |
| 55 | // - Handle different argument/return types (e.g. ..., chan, map, interface). |
| 56 | package gomock |
| 57 | |
| 58 | import ( |
| 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. |
| 68 | type 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. |
| 75 | type 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 | // }) |
| 104 | type 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. |
| 118 | func 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 | |
| 130 | type cancelReporter struct { |
| 131 | TestHelper |
| 132 | cancel func() |
| 133 | } |
| 134 | |
| 135 | func (r *cancelReporter) Errorf(format string, args ...interface{}) { |
| 136 | r.TestHelper.Errorf(format, args...) |
| 137 | } |
| 138 | func (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. |
| 145 | func 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 | |
| 155 | type nopTestHelper struct { |
| 156 | TestReporter |
| 157 | } |
| 158 | |
| 159 | func (h nopTestHelper) Helper() {} |
| 160 | |
| 161 | // RecordCall is called by a mock. It should not be called by user code. |
| 162 | func (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. |
| 176 | func (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. |
| 189 | func (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. |
| 232 | func (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 | |
| 259 | func 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 | } |