Path: blob/main/vendor/github.com/golang/mock/gomock/call.go
2880 views
// Copyright 2010 Google Inc.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314package gomock1516import (17"fmt"18"reflect"19"strconv"20"strings"21)2223// Call represents an expected call to a mock.24type Call struct {25t TestHelper // for triggering test failures on invalid call setup2627receiver interface{} // the receiver of the method call28method string // the name of the method29methodType reflect.Type // the type of the method30args []Matcher // the args31origin string // file and line number of call setup3233preReqs []*Call // prerequisite calls3435// Expectations36minCalls, maxCalls int3738numCalls int // actual number made3940// actions are called when this Call is called. Each action gets the args and41// can set the return values by returning a non-nil slice. Actions run in the42// order they are created.43actions []func([]interface{}) []interface{}44}4546// newCall creates a *Call. It requires the method type in order to support47// unexported methods.48func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {49t.Helper()5051// TODO: check arity, types.52mArgs := make([]Matcher, len(args))53for i, arg := range args {54if m, ok := arg.(Matcher); ok {55mArgs[i] = m56} else if arg == nil {57// Handle nil specially so that passing a nil interface value58// will match the typed nils of concrete args.59mArgs[i] = Nil()60} else {61mArgs[i] = Eq(arg)62}63}6465// callerInfo's skip should be updated if the number of calls between the user's test66// and this line changes, i.e. this code is wrapped in another anonymous function.67// 0 is us, 1 is RecordCallWithMethodType(), 2 is the generated recorder, and 3 is the user's test.68origin := callerInfo(3)69actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} {70// Synthesize the zero value for each of the return args' types.71rets := make([]interface{}, methodType.NumOut())72for i := 0; i < methodType.NumOut(); i++ {73rets[i] = reflect.Zero(methodType.Out(i)).Interface()74}75return rets76}}77return &Call{t: t, receiver: receiver, method: method, methodType: methodType,78args: mArgs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions}79}8081// AnyTimes allows the expectation to be called 0 or more times82func (c *Call) AnyTimes() *Call {83c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity84return c85}8687// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called or if MaxTimes88// was previously called with 1, MinTimes also sets the maximum number of calls to infinity.89func (c *Call) MinTimes(n int) *Call {90c.minCalls = n91if c.maxCalls == 1 {92c.maxCalls = 1e893}94return c95}9697// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called or if MinTimes was98// previously called with 1, MaxTimes also sets the minimum number of calls to 0.99func (c *Call) MaxTimes(n int) *Call {100c.maxCalls = n101if c.minCalls == 1 {102c.minCalls = 0103}104return c105}106107// DoAndReturn declares the action to run when the call is matched.108// The return values from this function are returned by the mocked function.109// It takes an interface{} argument to support n-arity functions.110func (c *Call) DoAndReturn(f interface{}) *Call {111// TODO: Check arity and types here, rather than dying badly elsewhere.112v := reflect.ValueOf(f)113114c.addAction(func(args []interface{}) []interface{} {115c.t.Helper()116vArgs := make([]reflect.Value, len(args))117ft := v.Type()118if c.methodType.NumIn() != ft.NumIn() {119c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v: got %d, want %d [%s]",120c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin)121return nil122}123for i := 0; i < len(args); i++ {124if args[i] != nil {125vArgs[i] = reflect.ValueOf(args[i])126} else {127// Use the zero value for the arg.128vArgs[i] = reflect.Zero(ft.In(i))129}130}131vRets := v.Call(vArgs)132rets := make([]interface{}, len(vRets))133for i, ret := range vRets {134rets[i] = ret.Interface()135}136return rets137})138return c139}140141// Do declares the action to run when the call is matched. The function's142// return values are ignored to retain backward compatibility. To use the143// return values call DoAndReturn.144// It takes an interface{} argument to support n-arity functions.145func (c *Call) Do(f interface{}) *Call {146// TODO: Check arity and types here, rather than dying badly elsewhere.147v := reflect.ValueOf(f)148149c.addAction(func(args []interface{}) []interface{} {150c.t.Helper()151if c.methodType.NumIn() != v.Type().NumIn() {152c.t.Fatalf("wrong number of arguments in Do func for %T.%v: got %d, want %d [%s]",153c.receiver, c.method, v.Type().NumIn(), c.methodType.NumIn(), c.origin)154return nil155}156vArgs := make([]reflect.Value, len(args))157ft := v.Type()158for i := 0; i < len(args); i++ {159if args[i] != nil {160vArgs[i] = reflect.ValueOf(args[i])161} else {162// Use the zero value for the arg.163vArgs[i] = reflect.Zero(ft.In(i))164}165}166v.Call(vArgs)167return nil168})169return c170}171172// Return declares the values to be returned by the mocked function call.173func (c *Call) Return(rets ...interface{}) *Call {174c.t.Helper()175176mt := c.methodType177if len(rets) != mt.NumOut() {178c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",179c.receiver, c.method, len(rets), mt.NumOut(), c.origin)180}181for i, ret := range rets {182if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {183// Identical types; nothing to do.184} else if got == nil {185// Nil needs special handling.186switch want.Kind() {187case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:188// ok189default:190c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",191i, c.receiver, c.method, want, c.origin)192}193} else if got.AssignableTo(want) {194// Assignable type relation. Make the assignment now so that the generated code195// can return the values with a type assertion.196v := reflect.New(want).Elem()197v.Set(reflect.ValueOf(ret))198rets[i] = v.Interface()199} else {200c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",201i, c.receiver, c.method, got, want, c.origin)202}203}204205c.addAction(func([]interface{}) []interface{} {206return rets207})208209return c210}211212// Times declares the exact number of times a function call is expected to be executed.213func (c *Call) Times(n int) *Call {214c.minCalls, c.maxCalls = n, n215return c216}217218// SetArg declares an action that will set the nth argument's value,219// indirected through a pointer. Or, in the case of a slice, SetArg220// will copy value's elements into the nth argument.221func (c *Call) SetArg(n int, value interface{}) *Call {222c.t.Helper()223224mt := c.methodType225// TODO: This will break on variadic methods.226// We will need to check those at invocation time.227if n < 0 || n >= mt.NumIn() {228c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",229n, mt.NumIn(), c.origin)230}231// Permit setting argument through an interface.232// In the interface case, we don't (nay, can't) check the type here.233at := mt.In(n)234switch at.Kind() {235case reflect.Ptr:236dt := at.Elem()237if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {238c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",239n, vt, dt, c.origin)240}241case reflect.Interface:242// nothing to do243case reflect.Slice:244// nothing to do245default:246c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]",247n, at, c.origin)248}249250c.addAction(func(args []interface{}) []interface{} {251v := reflect.ValueOf(value)252switch reflect.TypeOf(args[n]).Kind() {253case reflect.Slice:254setSlice(args[n], v)255default:256reflect.ValueOf(args[n]).Elem().Set(v)257}258return nil259})260return c261}262263// isPreReq returns true if other is a direct or indirect prerequisite to c.264func (c *Call) isPreReq(other *Call) bool {265for _, preReq := range c.preReqs {266if other == preReq || preReq.isPreReq(other) {267return true268}269}270return false271}272273// After declares that the call may only match after preReq has been exhausted.274func (c *Call) After(preReq *Call) *Call {275c.t.Helper()276277if c == preReq {278c.t.Fatalf("A call isn't allowed to be its own prerequisite")279}280if preReq.isPreReq(c) {281c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq)282}283284c.preReqs = append(c.preReqs, preReq)285return c286}287288// Returns true if the minimum number of calls have been made.289func (c *Call) satisfied() bool {290return c.numCalls >= c.minCalls291}292293// Returns true if the maximum number of calls have been made.294func (c *Call) exhausted() bool {295return c.numCalls >= c.maxCalls296}297298func (c *Call) String() string {299args := make([]string, len(c.args))300for i, arg := range c.args {301args[i] = arg.String()302}303arguments := strings.Join(args, ", ")304return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin)305}306307// Tests if the given call matches the expected call.308// If yes, returns nil. If no, returns error with message explaining why it does not match.309func (c *Call) matches(args []interface{}) error {310if !c.methodType.IsVariadic() {311if len(args) != len(c.args) {312return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d",313c.origin, len(args), len(c.args))314}315316for i, m := range c.args {317if !m.Matches(args[i]) {318return fmt.Errorf(319"expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v",320c.origin, i, formatGottenArg(m, args[i]), m,321)322}323}324} else {325if len(c.args) < c.methodType.NumIn()-1 {326return fmt.Errorf("expected call at %s has the wrong number of matchers. Got: %d, want: %d",327c.origin, len(c.args), c.methodType.NumIn()-1)328}329if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) {330return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d",331c.origin, len(args), len(c.args))332}333if len(args) < len(c.args)-1 {334return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d",335c.origin, len(args), len(c.args)-1)336}337338for i, m := range c.args {339if i < c.methodType.NumIn()-1 {340// Non-variadic args341if !m.Matches(args[i]) {342return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",343c.origin, strconv.Itoa(i), formatGottenArg(m, args[i]), m)344}345continue346}347// The last arg has a possibility of a variadic argument, so let it branch348349// sample: Foo(a int, b int, c ...int)350if i < len(c.args) && i < len(args) {351if m.Matches(args[i]) {352// Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any())353// Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher)354// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC)355// Got Foo(a, b) want Foo(matcherA, matcherB)356// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD)357continue358}359}360361// The number of actual args don't match the number of matchers,362// or the last matcher is a slice and the last arg is not.363// If this function still matches it is because the last matcher364// matches all the remaining arguments or the lack of any.365// Convert the remaining arguments, if any, into a slice of the366// expected type.367vArgsType := c.methodType.In(c.methodType.NumIn() - 1)368vArgs := reflect.MakeSlice(vArgsType, 0, len(args)-i)369for _, arg := range args[i:] {370vArgs = reflect.Append(vArgs, reflect.ValueOf(arg))371}372if m.Matches(vArgs.Interface()) {373// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any())374// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher)375// Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any())376// Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher)377break378}379// Wrong number of matchers or not match. Fail.380// Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD)381// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD)382// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE)383// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD)384// Got Foo(a, b, c) want Foo(matcherA, matcherB)385386return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",387c.origin, strconv.Itoa(i), formatGottenArg(m, args[i:]), c.args[i])388}389}390391// Check that all prerequisite calls have been satisfied.392for _, preReqCall := range c.preReqs {393if !preReqCall.satisfied() {394return fmt.Errorf("expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v",395c.origin, preReqCall, c)396}397}398399// Check that the call is not exhausted.400if c.exhausted() {401return fmt.Errorf("expected call at %s has already been called the max number of times", c.origin)402}403404return nil405}406407// dropPrereqs tells the expected Call to not re-check prerequisite calls any408// longer, and to return its current set.409func (c *Call) dropPrereqs() (preReqs []*Call) {410preReqs = c.preReqs411c.preReqs = nil412return413}414415func (c *Call) call() []func([]interface{}) []interface{} {416c.numCalls++417return c.actions418}419420// InOrder declares that the given calls should occur in order.421func InOrder(calls ...*Call) {422for i := 1; i < len(calls); i++ {423calls[i].After(calls[i-1])424}425}426427func setSlice(arg interface{}, v reflect.Value) {428va := reflect.ValueOf(arg)429for i := 0; i < v.Len(); i++ {430va.Index(i).Set(v.Index(i))431}432}433434func (c *Call) addAction(action func([]interface{}) []interface{}) {435c.actions = append(c.actions, action)436}437438func formatGottenArg(m Matcher, arg interface{}) string {439got := fmt.Sprintf("%v (%T)", arg, arg)440if gs, ok := m.(GotFormatter); ok {441got = gs.Got(arg)442}443return got444}445446447