Path: blob/main/vendor/github.com/golang/mock/gomock/controller.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.1314// Package gomock is a mock framework for Go.15//16// Standard usage:17// (1) Define an interface that you wish to mock.18// type MyInterface interface {19// SomeMethod(x int64, y string)20// }21// (2) Use mockgen to generate a mock from the interface.22// (3) Use the mock in a test:23// func TestMyThing(t *testing.T) {24// mockCtrl := gomock.NewController(t)25// defer mockCtrl.Finish()26//27// mockObj := something.NewMockMyInterface(mockCtrl)28// mockObj.EXPECT().SomeMethod(4, "blah")29// // pass mockObj to a real object and play with it.30// }31//32// By default, expected calls are not enforced to run in any particular order.33// Call order dependency can be enforced by use of InOrder and/or Call.After.34// Call.After can create more varied call order dependencies, but InOrder is35// often more convenient.36//37// The following examples create equivalent call order dependencies.38//39// Example of using Call.After to chain expected call order:40//41// firstCall := mockObj.EXPECT().SomeMethod(1, "first")42// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)43// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)44//45// Example of using InOrder to declare expected call order:46//47// gomock.InOrder(48// mockObj.EXPECT().SomeMethod(1, "first"),49// mockObj.EXPECT().SomeMethod(2, "second"),50// mockObj.EXPECT().SomeMethod(3, "third"),51// )52package gomock5354import (55"context"56"fmt"57"reflect"58"runtime"59"sync"60)6162// A TestReporter is something that can be used to report test failures. It63// is satisfied by the standard library's *testing.T.64type TestReporter interface {65Errorf(format string, args ...interface{})66Fatalf(format string, args ...interface{})67}6869// TestHelper is a TestReporter that has the Helper method. It is satisfied70// by the standard library's *testing.T.71type TestHelper interface {72TestReporter73Helper()74}7576// cleanuper is used to check if TestHelper also has the `Cleanup` method. A77// common pattern is to pass in a `*testing.T` to78// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup79// method. This can be utilized to call `Finish()` so the caller of this library80// does not have to.81type cleanuper interface {82Cleanup(func())83}8485// A Controller represents the top-level control of a mock ecosystem. It86// defines the scope and lifetime of mock objects, as well as their87// expectations. It is safe to call Controller's methods from multiple88// goroutines. Each test should create a new Controller and invoke Finish via89// defer.90//91// func TestFoo(t *testing.T) {92// ctrl := gomock.NewController(t)93// defer ctrl.Finish()94// // ..95// }96//97// func TestBar(t *testing.T) {98// t.Run("Sub-Test-1", st) {99// ctrl := gomock.NewController(st)100// defer ctrl.Finish()101// // ..102// })103// t.Run("Sub-Test-2", st) {104// ctrl := gomock.NewController(st)105// defer ctrl.Finish()106// // ..107// })108// })109type Controller struct {110// T should only be called within a generated mock. It is not intended to111// be used in user code and may be changed in future versions. T is the112// TestReporter passed in when creating the Controller via NewController.113// If the TestReporter does not implement a TestHelper it will be wrapped114// with a nopTestHelper.115T TestHelper116mu sync.Mutex117expectedCalls *callSet118finished bool119}120121// NewController returns a new Controller. It is the preferred way to create a122// Controller.123//124// New in go1.14+, if you are passing a *testing.T into this function you no125// longer need to call ctrl.Finish() in your test methods.126func NewController(t TestReporter) *Controller {127h, ok := t.(TestHelper)128if !ok {129h = &nopTestHelper{t}130}131ctrl := &Controller{132T: h,133expectedCalls: newCallSet(),134}135if c, ok := isCleanuper(ctrl.T); ok {136c.Cleanup(func() {137ctrl.T.Helper()138ctrl.finish(true, nil)139})140}141142return ctrl143}144145type cancelReporter struct {146t TestHelper147cancel func()148}149150func (r *cancelReporter) Errorf(format string, args ...interface{}) {151r.t.Errorf(format, args...)152}153func (r *cancelReporter) Fatalf(format string, args ...interface{}) {154defer r.cancel()155r.t.Fatalf(format, args...)156}157158func (r *cancelReporter) Helper() {159r.t.Helper()160}161162// WithContext returns a new Controller and a Context, which is cancelled on any163// fatal failure.164func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {165h, ok := t.(TestHelper)166if !ok {167h = &nopTestHelper{t: t}168}169170ctx, cancel := context.WithCancel(ctx)171return NewController(&cancelReporter{t: h, cancel: cancel}), ctx172}173174type nopTestHelper struct {175t TestReporter176}177178func (h *nopTestHelper) Errorf(format string, args ...interface{}) {179h.t.Errorf(format, args...)180}181func (h *nopTestHelper) Fatalf(format string, args ...interface{}) {182h.t.Fatalf(format, args...)183}184185func (h nopTestHelper) Helper() {}186187// RecordCall is called by a mock. It should not be called by user code.188func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {189ctrl.T.Helper()190191recv := reflect.ValueOf(receiver)192for i := 0; i < recv.Type().NumMethod(); i++ {193if recv.Type().Method(i).Name == method {194return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)195}196}197ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)198panic("unreachable")199}200201// RecordCallWithMethodType is called by a mock. It should not be called by user code.202func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {203ctrl.T.Helper()204205call := newCall(ctrl.T, receiver, method, methodType, args...)206207ctrl.mu.Lock()208defer ctrl.mu.Unlock()209ctrl.expectedCalls.Add(call)210211return call212}213214// Call is called by a mock. It should not be called by user code.215func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {216ctrl.T.Helper()217218// Nest this code so we can use defer to make sure the lock is released.219actions := func() []func([]interface{}) []interface{} {220ctrl.T.Helper()221ctrl.mu.Lock()222defer ctrl.mu.Unlock()223224expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)225if err != nil {226// callerInfo's skip should be updated if the number of calls between the user's test227// and this line changes, i.e. this code is wrapped in another anonymous function.228// 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test.229origin := callerInfo(3)230ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err)231}232233// Two things happen here:234// * the matching call no longer needs to check prerequite calls,235// * and the prerequite calls are no longer expected, so remove them.236preReqCalls := expected.dropPrereqs()237for _, preReqCall := range preReqCalls {238ctrl.expectedCalls.Remove(preReqCall)239}240241actions := expected.call()242if expected.exhausted() {243ctrl.expectedCalls.Remove(expected)244}245return actions246}()247248var rets []interface{}249for _, action := range actions {250if r := action(args); r != nil {251rets = r252}253}254255return rets256}257258// Finish checks to see if all the methods that were expected to be called259// were called. It should be invoked for each Controller. It is not idempotent260// and therefore can only be invoked once.261//262// New in go1.14+, if you are passing a *testing.T into NewController function you no263// longer need to call ctrl.Finish() in your test methods.264func (ctrl *Controller) Finish() {265// If we're currently panicking, probably because this is a deferred call.266// This must be recovered in the deferred function.267err := recover()268ctrl.finish(false, err)269}270271func (ctrl *Controller) finish(cleanup bool, panicErr interface{}) {272ctrl.T.Helper()273274ctrl.mu.Lock()275defer ctrl.mu.Unlock()276277if ctrl.finished {278if _, ok := isCleanuper(ctrl.T); !ok {279ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")280}281return282}283ctrl.finished = true284285// Short-circuit, pass through the panic.286if panicErr != nil {287panic(panicErr)288}289290// Check that all remaining expected calls are satisfied.291failures := ctrl.expectedCalls.Failures()292for _, call := range failures {293ctrl.T.Errorf("missing call(s) to %v", call)294}295if len(failures) != 0 {296if !cleanup {297ctrl.T.Fatalf("aborting test due to missing call(s)")298return299}300ctrl.T.Errorf("aborting test due to missing call(s)")301}302}303304// callerInfo returns the file:line of the call site. skip is the number305// of stack frames to skip when reporting. 0 is callerInfo's call site.306func callerInfo(skip int) string {307if _, file, line, ok := runtime.Caller(skip + 1); ok {308return fmt.Sprintf("%s:%d", file, line)309}310return "unknown file"311}312313// isCleanuper checks it if t's base TestReporter has a Cleanup method.314func isCleanuper(t TestReporter) (cleanuper, bool) {315tr := unwrapTestReporter(t)316c, ok := tr.(cleanuper)317return c, ok318}319320// unwrapTestReporter unwraps TestReporter to the base implementation.321func unwrapTestReporter(t TestReporter) TestReporter {322tr := t323switch nt := t.(type) {324case *cancelReporter:325tr = nt.t326if h, check := tr.(*nopTestHelper); check {327tr = h.t328}329case *nopTestHelper:330tr = nt.t331default:332// not wrapped333}334return tr335}336337338