| 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 |
| 16 | |
| 17 | import ( |
| 18 | "fmt" |
| 19 | "reflect" |
| 20 | "strconv" |
| 21 | "strings" |
| 22 | ) |
| 23 | |
| 24 | // Call represents an expected call to a mock. |
| 25 | type Call struct { |
| 26 | t TestHelper // for triggering test failures on invalid call setup |
| 27 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 28 | receiver any // the receiver of the method call |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 29 | method string // the name of the method |
| 30 | methodType reflect.Type // the type of the method |
| 31 | args []Matcher // the args |
| 32 | origin string // file and line number of call setup |
| 33 | |
| 34 | preReqs []*Call // prerequisite calls |
| 35 | |
| 36 | // Expectations |
| 37 | minCalls, maxCalls int |
| 38 | |
| 39 | numCalls int // actual number made |
| 40 | |
| 41 | // actions are called when this Call is called. Each action gets the args and |
| 42 | // can set the return values by returning a non-nil slice. Actions run in the |
| 43 | // order they are created. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 44 | actions []func([]any) []any |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | // newCall creates a *Call. It requires the method type in order to support |
| 48 | // unexported methods. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 49 | func newCall(t TestHelper, receiver any, method string, methodType reflect.Type, args ...any) *Call { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 50 | t.Helper() |
| 51 | |
| 52 | // TODO: check arity, types. |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 53 | mArgs := make([]Matcher, len(args)) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 54 | for i, arg := range args { |
| 55 | if m, ok := arg.(Matcher); ok { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 56 | mArgs[i] = m |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 57 | } else if arg == nil { |
| 58 | // Handle nil specially so that passing a nil interface value |
| 59 | // will match the typed nils of concrete args. |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 60 | mArgs[i] = Nil() |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 61 | } else { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 62 | mArgs[i] = Eq(arg) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 63 | } |
| 64 | } |
| 65 | |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 66 | // callerInfo's skip should be updated if the number of calls between the user's test |
| 67 | // and this line changes, i.e. this code is wrapped in another anonymous function. |
| 68 | // 0 is us, 1 is RecordCallWithMethodType(), 2 is the generated recorder, and 3 is the user's test. |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 69 | origin := callerInfo(3) |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 70 | actions := []func([]any) []any{func([]any) []any { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 71 | // Synthesize the zero value for each of the return args' types. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 72 | rets := make([]any, methodType.NumOut()) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 73 | for i := 0; i < methodType.NumOut(); i++ { |
| 74 | rets[i] = reflect.Zero(methodType.Out(i)).Interface() |
| 75 | } |
| 76 | return rets |
| 77 | }} |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 78 | return &Call{ |
| 79 | t: t, receiver: receiver, method: method, methodType: methodType, |
| 80 | args: mArgs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions, |
| 81 | } |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | // AnyTimes allows the expectation to be called 0 or more times |
| 85 | func (c *Call) AnyTimes() *Call { |
| 86 | c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity |
| 87 | return c |
| 88 | } |
| 89 | |
| 90 | // MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called or if MaxTimes |
| 91 | // was previously called with 1, MinTimes also sets the maximum number of calls to infinity. |
| 92 | func (c *Call) MinTimes(n int) *Call { |
| 93 | c.minCalls = n |
| 94 | if c.maxCalls == 1 { |
| 95 | c.maxCalls = 1e8 |
| 96 | } |
| 97 | return c |
| 98 | } |
| 99 | |
| 100 | // MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called or if MinTimes was |
| 101 | // previously called with 1, MaxTimes also sets the minimum number of calls to 0. |
| 102 | func (c *Call) MaxTimes(n int) *Call { |
| 103 | c.maxCalls = n |
| 104 | if c.minCalls == 1 { |
| 105 | c.minCalls = 0 |
| 106 | } |
| 107 | return c |
| 108 | } |
| 109 | |
| 110 | // DoAndReturn declares the action to run when the call is matched. |
| 111 | // The return values from this function are returned by the mocked function. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 112 | // It takes an any argument to support n-arity functions. |
| 113 | // The anonymous function must match the function signature mocked method. |
| 114 | func (c *Call) DoAndReturn(f any) *Call { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 115 | // TODO: Check arity and types here, rather than dying badly elsewhere. |
| 116 | v := reflect.ValueOf(f) |
| 117 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 118 | c.addAction(func(args []any) []any { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 119 | c.t.Helper() |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 120 | ft := v.Type() |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 121 | if c.methodType.NumIn() != ft.NumIn() { |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 122 | if ft.IsVariadic() { |
| 123 | c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v The function signature must match the mocked method, a variadic function cannot be used.", |
| 124 | c.receiver, c.method) |
| 125 | } else { |
| 126 | c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v: got %d, want %d [%s]", |
| 127 | c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin) |
| 128 | } |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 129 | return nil |
| 130 | } |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 131 | vArgs := make([]reflect.Value, len(args)) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 132 | for i := 0; i < len(args); i++ { |
| 133 | if args[i] != nil { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 134 | vArgs[i] = reflect.ValueOf(args[i]) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 135 | } else { |
| 136 | // Use the zero value for the arg. |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 137 | vArgs[i] = reflect.Zero(ft.In(i)) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 138 | } |
| 139 | } |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 140 | vRets := v.Call(vArgs) |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 141 | rets := make([]any, len(vRets)) |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 142 | for i, ret := range vRets { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 143 | rets[i] = ret.Interface() |
| 144 | } |
| 145 | return rets |
| 146 | }) |
| 147 | return c |
| 148 | } |
| 149 | |
| 150 | // Do declares the action to run when the call is matched. The function's |
| 151 | // return values are ignored to retain backward compatibility. To use the |
| 152 | // return values call DoAndReturn. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 153 | // It takes an any argument to support n-arity functions. |
| 154 | // The anonymous function must match the function signature mocked method. |
| 155 | func (c *Call) Do(f any) *Call { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 156 | // TODO: Check arity and types here, rather than dying badly elsewhere. |
| 157 | v := reflect.ValueOf(f) |
| 158 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 159 | c.addAction(func(args []any) []any { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 160 | c.t.Helper() |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 161 | ft := v.Type() |
| 162 | if c.methodType.NumIn() != ft.NumIn() { |
| 163 | if ft.IsVariadic() { |
| 164 | c.t.Fatalf("wrong number of arguments in Do func for %T.%v The function signature must match the mocked method, a variadic function cannot be used.", |
| 165 | c.receiver, c.method) |
| 166 | } else { |
| 167 | c.t.Fatalf("wrong number of arguments in Do func for %T.%v: got %d, want %d [%s]", |
| 168 | c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin) |
| 169 | } |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 170 | return nil |
| 171 | } |
| 172 | vArgs := make([]reflect.Value, len(args)) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 173 | for i := 0; i < len(args); i++ { |
| 174 | if args[i] != nil { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 175 | vArgs[i] = reflect.ValueOf(args[i]) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 176 | } else { |
| 177 | // Use the zero value for the arg. |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 178 | vArgs[i] = reflect.Zero(ft.In(i)) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 179 | } |
| 180 | } |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 181 | v.Call(vArgs) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 182 | return nil |
| 183 | }) |
| 184 | return c |
| 185 | } |
| 186 | |
| 187 | // Return declares the values to be returned by the mocked function call. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 188 | func (c *Call) Return(rets ...any) *Call { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 189 | c.t.Helper() |
| 190 | |
| 191 | mt := c.methodType |
| 192 | if len(rets) != mt.NumOut() { |
| 193 | c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]", |
| 194 | c.receiver, c.method, len(rets), mt.NumOut(), c.origin) |
| 195 | } |
| 196 | for i, ret := range rets { |
| 197 | if got, want := reflect.TypeOf(ret), mt.Out(i); got == want { |
| 198 | // Identical types; nothing to do. |
| 199 | } else if got == nil { |
| 200 | // Nil needs special handling. |
| 201 | switch want.Kind() { |
| 202 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: |
| 203 | // ok |
| 204 | default: |
| 205 | c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]", |
| 206 | i, c.receiver, c.method, want, c.origin) |
| 207 | } |
| 208 | } else if got.AssignableTo(want) { |
| 209 | // Assignable type relation. Make the assignment now so that the generated code |
| 210 | // can return the values with a type assertion. |
| 211 | v := reflect.New(want).Elem() |
| 212 | v.Set(reflect.ValueOf(ret)) |
| 213 | rets[i] = v.Interface() |
| 214 | } else { |
| 215 | c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]", |
| 216 | i, c.receiver, c.method, got, want, c.origin) |
| 217 | } |
| 218 | } |
| 219 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 220 | c.addAction(func([]any) []any { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 221 | return rets |
| 222 | }) |
| 223 | |
| 224 | return c |
| 225 | } |
| 226 | |
| 227 | // Times declares the exact number of times a function call is expected to be executed. |
| 228 | func (c *Call) Times(n int) *Call { |
| 229 | c.minCalls, c.maxCalls = n, n |
| 230 | return c |
| 231 | } |
| 232 | |
| 233 | // SetArg declares an action that will set the nth argument's value, |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 234 | // indirected through a pointer. Or, in the case of a slice and map, SetArg |
| 235 | // will copy value's elements/key-value pairs into the nth argument. |
| 236 | func (c *Call) SetArg(n int, value any) *Call { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 237 | c.t.Helper() |
| 238 | |
| 239 | mt := c.methodType |
| 240 | // TODO: This will break on variadic methods. |
| 241 | // We will need to check those at invocation time. |
| 242 | if n < 0 || n >= mt.NumIn() { |
| 243 | c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]", |
| 244 | n, mt.NumIn(), c.origin) |
| 245 | } |
| 246 | // Permit setting argument through an interface. |
| 247 | // In the interface case, we don't (nay, can't) check the type here. |
| 248 | at := mt.In(n) |
| 249 | switch at.Kind() { |
| 250 | case reflect.Ptr: |
| 251 | dt := at.Elem() |
| 252 | if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) { |
| 253 | c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]", |
| 254 | n, vt, dt, c.origin) |
| 255 | } |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 256 | case reflect.Interface, reflect.Slice, reflect.Map: |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 257 | // nothing to do |
| 258 | default: |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 259 | c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice non-map type %v [%s]", |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 260 | n, at, c.origin) |
| 261 | } |
| 262 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 263 | c.addAction(func(args []any) []any { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 264 | v := reflect.ValueOf(value) |
| 265 | switch reflect.TypeOf(args[n]).Kind() { |
| 266 | case reflect.Slice: |
| 267 | setSlice(args[n], v) |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 268 | case reflect.Map: |
| 269 | setMap(args[n], v) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 270 | default: |
| 271 | reflect.ValueOf(args[n]).Elem().Set(v) |
| 272 | } |
| 273 | return nil |
| 274 | }) |
| 275 | return c |
| 276 | } |
| 277 | |
| 278 | // isPreReq returns true if other is a direct or indirect prerequisite to c. |
| 279 | func (c *Call) isPreReq(other *Call) bool { |
| 280 | for _, preReq := range c.preReqs { |
| 281 | if other == preReq || preReq.isPreReq(other) { |
| 282 | return true |
| 283 | } |
| 284 | } |
| 285 | return false |
| 286 | } |
| 287 | |
| 288 | // After declares that the call may only match after preReq has been exhausted. |
| 289 | func (c *Call) After(preReq *Call) *Call { |
| 290 | c.t.Helper() |
| 291 | |
| 292 | if c == preReq { |
| 293 | c.t.Fatalf("A call isn't allowed to be its own prerequisite") |
| 294 | } |
| 295 | if preReq.isPreReq(c) { |
| 296 | c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq) |
| 297 | } |
| 298 | |
| 299 | c.preReqs = append(c.preReqs, preReq) |
| 300 | return c |
| 301 | } |
| 302 | |
| 303 | // Returns true if the minimum number of calls have been made. |
| 304 | func (c *Call) satisfied() bool { |
| 305 | return c.numCalls >= c.minCalls |
| 306 | } |
| 307 | |
| 308 | // Returns true if the maximum number of calls have been made. |
| 309 | func (c *Call) exhausted() bool { |
| 310 | return c.numCalls >= c.maxCalls |
| 311 | } |
| 312 | |
| 313 | func (c *Call) String() string { |
| 314 | args := make([]string, len(c.args)) |
| 315 | for i, arg := range c.args { |
| 316 | args[i] = arg.String() |
| 317 | } |
| 318 | arguments := strings.Join(args, ", ") |
| 319 | return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin) |
| 320 | } |
| 321 | |
| 322 | // Tests if the given call matches the expected call. |
| 323 | // If yes, returns nil. If no, returns error with message explaining why it does not match. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 324 | func (c *Call) matches(args []any) error { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 325 | if !c.methodType.IsVariadic() { |
| 326 | if len(args) != len(c.args) { |
| 327 | return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d", |
| 328 | c.origin, len(args), len(c.args)) |
| 329 | } |
| 330 | |
| 331 | for i, m := range c.args { |
| 332 | if !m.Matches(args[i]) { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 333 | return fmt.Errorf( |
| 334 | "expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v", |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 335 | c.origin, i, formatGottenArg(m, args[i]), m, |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 336 | ) |
| 337 | } |
| 338 | } |
| 339 | } else { |
| 340 | if len(c.args) < c.methodType.NumIn()-1 { |
| 341 | return fmt.Errorf("expected call at %s has the wrong number of matchers. Got: %d, want: %d", |
| 342 | c.origin, len(c.args), c.methodType.NumIn()-1) |
| 343 | } |
| 344 | if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) { |
| 345 | return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d", |
| 346 | c.origin, len(args), len(c.args)) |
| 347 | } |
| 348 | if len(args) < len(c.args)-1 { |
| 349 | return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d", |
| 350 | c.origin, len(args), len(c.args)-1) |
| 351 | } |
| 352 | |
| 353 | for i, m := range c.args { |
| 354 | if i < c.methodType.NumIn()-1 { |
| 355 | // Non-variadic args |
| 356 | if !m.Matches(args[i]) { |
| 357 | return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 358 | c.origin, strconv.Itoa(i), formatGottenArg(m, args[i]), m) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 359 | } |
| 360 | continue |
| 361 | } |
| 362 | // The last arg has a possibility of a variadic argument, so let it branch |
| 363 | |
| 364 | // sample: Foo(a int, b int, c ...int) |
| 365 | if i < len(c.args) && i < len(args) { |
| 366 | if m.Matches(args[i]) { |
| 367 | // Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any()) |
| 368 | // Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher) |
| 369 | // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC) |
| 370 | // Got Foo(a, b) want Foo(matcherA, matcherB) |
| 371 | // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD) |
| 372 | continue |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | // The number of actual args don't match the number of matchers, |
| 377 | // or the last matcher is a slice and the last arg is not. |
| 378 | // If this function still matches it is because the last matcher |
| 379 | // matches all the remaining arguments or the lack of any. |
| 380 | // Convert the remaining arguments, if any, into a slice of the |
| 381 | // expected type. |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 382 | vArgsType := c.methodType.In(c.methodType.NumIn() - 1) |
| 383 | vArgs := reflect.MakeSlice(vArgsType, 0, len(args)-i) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 384 | for _, arg := range args[i:] { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 385 | vArgs = reflect.Append(vArgs, reflect.ValueOf(arg)) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 386 | } |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 387 | if m.Matches(vArgs.Interface()) { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 388 | // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any()) |
| 389 | // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher) |
| 390 | // Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any()) |
| 391 | // Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher) |
| 392 | break |
| 393 | } |
| 394 | // Wrong number of matchers or not match. Fail. |
| 395 | // Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD) |
| 396 | // Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD) |
| 397 | // Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE) |
| 398 | // Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD) |
| 399 | // Got Foo(a, b, c) want Foo(matcherA, matcherB) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 400 | |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 401 | return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v", |
| 402 | c.origin, strconv.Itoa(i), formatGottenArg(m, args[i:]), c.args[i]) |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 403 | } |
| 404 | } |
| 405 | |
| 406 | // Check that all prerequisite calls have been satisfied. |
| 407 | for _, preReqCall := range c.preReqs { |
| 408 | if !preReqCall.satisfied() { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 409 | return fmt.Errorf("expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v", |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 410 | c.origin, preReqCall, c) |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | // Check that the call is not exhausted. |
| 415 | if c.exhausted() { |
| 416 | return fmt.Errorf("expected call at %s has already been called the max number of times", c.origin) |
| 417 | } |
| 418 | |
| 419 | return nil |
| 420 | } |
| 421 | |
| 422 | // dropPrereqs tells the expected Call to not re-check prerequisite calls any |
| 423 | // longer, and to return its current set. |
| 424 | func (c *Call) dropPrereqs() (preReqs []*Call) { |
| 425 | preReqs = c.preReqs |
| 426 | c.preReqs = nil |
| 427 | return |
| 428 | } |
| 429 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 430 | func (c *Call) call() []func([]any) []any { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 431 | c.numCalls++ |
| 432 | return c.actions |
| 433 | } |
| 434 | |
| 435 | // InOrder declares that the given calls should occur in order. |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 436 | // It panics if the type of any of the arguments isn't *Call or a generated |
| 437 | // mock with an embedded *Call. |
| 438 | func InOrder(args ...any) { |
| 439 | calls := make([]*Call, 0, len(args)) |
| 440 | for i := 0; i < len(args); i++ { |
| 441 | if call := getCall(args[i]); call != nil { |
| 442 | calls = append(calls, call) |
| 443 | continue |
| 444 | } |
| 445 | panic(fmt.Sprintf( |
| 446 | "invalid argument at position %d of type %T, InOrder expects *gomock.Call or generated mock types with an embedded *gomock.Call", |
| 447 | i, |
| 448 | args[i], |
| 449 | )) |
| 450 | } |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 451 | for i := 1; i < len(calls); i++ { |
| 452 | calls[i].After(calls[i-1]) |
| 453 | } |
| 454 | } |
| 455 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 456 | // getCall checks if the parameter is a *Call or a generated struct |
| 457 | // that wraps a *Call and returns the *Call pointer - if neither, it returns nil. |
| 458 | func getCall(arg any) *Call { |
| 459 | if call, ok := arg.(*Call); ok { |
| 460 | return call |
| 461 | } |
| 462 | t := reflect.ValueOf(arg) |
| 463 | if t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface { |
| 464 | return nil |
| 465 | } |
| 466 | t = t.Elem() |
| 467 | for i := 0; i < t.NumField(); i++ { |
| 468 | f := t.Field(i) |
| 469 | if !f.CanInterface() { |
| 470 | continue |
| 471 | } |
| 472 | if call, ok := f.Interface().(*Call); ok { |
| 473 | return call |
| 474 | } |
| 475 | } |
| 476 | return nil |
| 477 | } |
| 478 | |
| 479 | func setSlice(arg any, v reflect.Value) { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 480 | va := reflect.ValueOf(arg) |
| 481 | for i := 0; i < v.Len(); i++ { |
| 482 | va.Index(i).Set(v.Index(i)) |
| 483 | } |
| 484 | } |
| 485 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 486 | func setMap(arg any, v reflect.Value) { |
| 487 | va := reflect.ValueOf(arg) |
| 488 | for _, e := range va.MapKeys() { |
| 489 | va.SetMapIndex(e, reflect.Value{}) |
| 490 | } |
| 491 | for _, e := range v.MapKeys() { |
| 492 | va.SetMapIndex(e, v.MapIndex(e)) |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | func (c *Call) addAction(action func([]any) []any) { |
| vinokuma | f7605fc | 2023-06-02 18:08:01 +0530 | [diff] [blame] | 497 | c.actions = append(c.actions, action) |
| 498 | } |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 499 | |
| bseeniva | dd66c36 | 2026-02-12 19:13:26 +0530 | [diff] [blame] | 500 | func formatGottenArg(m Matcher, arg any) string { |
| Abhay Kumar | fe505f2 | 2025-11-10 14:16:31 +0000 | [diff] [blame] | 501 | got := fmt.Sprintf("%v (%T)", arg, arg) |
| 502 | if gs, ok := m.(GotFormatter); ok { |
| 503 | got = gs.Got(arg) |
| 504 | } |
| 505 | return got |
| 506 | } |