TUN-8861: Add session limiter to UDP session manager

## Summary
In order to make cloudflared behavior more predictable and
prevent an exhaustion of resources, we have decided to add
session limits that can be configured by the user. This first
commit introduces the session limiter and adds it to the UDP
handling path. For now the limiter is set to run only in
unlimited mode.
This commit is contained in:
João "Pisco" Fernandes
2025-01-20 02:52:32 -08:00
parent 8918b6729e
commit bf4954e96a
66 changed files with 3409 additions and 1184 deletions

506
vendor/go.uber.org/mock/gomock/call.go generated vendored Normal file
View File

@@ -0,0 +1,506 @@
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomock
import (
"fmt"
"reflect"
"strconv"
"strings"
)
// Call represents an expected call to a mock.
type Call struct {
t TestHelper // for triggering test failures on invalid call setup
receiver any // the receiver of the method call
method string // the name of the method
methodType reflect.Type // the type of the method
args []Matcher // the args
origin string // file and line number of call setup
preReqs []*Call // prerequisite calls
// Expectations
minCalls, maxCalls int
numCalls int // actual number made
// actions are called when this Call is called. Each action gets the args and
// can set the return values by returning a non-nil slice. Actions run in the
// order they are created.
actions []func([]any) []any
}
// newCall creates a *Call. It requires the method type in order to support
// unexported methods.
func newCall(t TestHelper, receiver any, method string, methodType reflect.Type, args ...any) *Call {
t.Helper()
// TODO: check arity, types.
mArgs := make([]Matcher, len(args))
for i, arg := range args {
if m, ok := arg.(Matcher); ok {
mArgs[i] = m
} else if arg == nil {
// Handle nil specially so that passing a nil interface value
// will match the typed nils of concrete args.
mArgs[i] = Nil()
} else {
mArgs[i] = Eq(arg)
}
}
// callerInfo's skip should be updated if the number of calls between the user's test
// and this line changes, i.e. this code is wrapped in another anonymous function.
// 0 is us, 1 is RecordCallWithMethodType(), 2 is the generated recorder, and 3 is the user's test.
origin := callerInfo(3)
actions := []func([]any) []any{func([]any) []any {
// Synthesize the zero value for each of the return args' types.
rets := make([]any, methodType.NumOut())
for i := 0; i < methodType.NumOut(); i++ {
rets[i] = reflect.Zero(methodType.Out(i)).Interface()
}
return rets
}}
return &Call{
t: t, receiver: receiver, method: method, methodType: methodType,
args: mArgs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions,
}
}
// AnyTimes allows the expectation to be called 0 or more times
func (c *Call) AnyTimes() *Call {
c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity
return c
}
// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called or if MaxTimes
// was previously called with 1, MinTimes also sets the maximum number of calls to infinity.
func (c *Call) MinTimes(n int) *Call {
c.minCalls = n
if c.maxCalls == 1 {
c.maxCalls = 1e8
}
return c
}
// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called or if MinTimes was
// previously called with 1, MaxTimes also sets the minimum number of calls to 0.
func (c *Call) MaxTimes(n int) *Call {
c.maxCalls = n
if c.minCalls == 1 {
c.minCalls = 0
}
return c
}
// DoAndReturn declares the action to run when the call is matched.
// The return values from this function are returned by the mocked function.
// It takes an any argument to support n-arity functions.
// The anonymous function must match the function signature mocked method.
func (c *Call) DoAndReturn(f any) *Call {
// TODO: Check arity and types here, rather than dying badly elsewhere.
v := reflect.ValueOf(f)
c.addAction(func(args []any) []any {
c.t.Helper()
ft := v.Type()
if c.methodType.NumIn() != ft.NumIn() {
if ft.IsVariadic() {
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.",
c.receiver, c.method)
} else {
c.t.Fatalf("wrong number of arguments in DoAndReturn func for %T.%v: got %d, want %d [%s]",
c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin)
}
return nil
}
vArgs := make([]reflect.Value, len(args))
for i := 0; i < len(args); i++ {
if args[i] != nil {
vArgs[i] = reflect.ValueOf(args[i])
} else {
// Use the zero value for the arg.
vArgs[i] = reflect.Zero(ft.In(i))
}
}
vRets := v.Call(vArgs)
rets := make([]any, len(vRets))
for i, ret := range vRets {
rets[i] = ret.Interface()
}
return rets
})
return c
}
// Do declares the action to run when the call is matched. The function's
// return values are ignored to retain backward compatibility. To use the
// return values call DoAndReturn.
// It takes an any argument to support n-arity functions.
// The anonymous function must match the function signature mocked method.
func (c *Call) Do(f any) *Call {
// TODO: Check arity and types here, rather than dying badly elsewhere.
v := reflect.ValueOf(f)
c.addAction(func(args []any) []any {
c.t.Helper()
ft := v.Type()
if c.methodType.NumIn() != ft.NumIn() {
if ft.IsVariadic() {
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.",
c.receiver, c.method)
} else {
c.t.Fatalf("wrong number of arguments in Do func for %T.%v: got %d, want %d [%s]",
c.receiver, c.method, ft.NumIn(), c.methodType.NumIn(), c.origin)
}
return nil
}
vArgs := make([]reflect.Value, len(args))
for i := 0; i < len(args); i++ {
if args[i] != nil {
vArgs[i] = reflect.ValueOf(args[i])
} else {
// Use the zero value for the arg.
vArgs[i] = reflect.Zero(ft.In(i))
}
}
v.Call(vArgs)
return nil
})
return c
}
// Return declares the values to be returned by the mocked function call.
func (c *Call) Return(rets ...any) *Call {
c.t.Helper()
mt := c.methodType
if len(rets) != mt.NumOut() {
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
}
for i, ret := range rets {
if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
// Identical types; nothing to do.
} else if got == nil {
// Nil needs special handling.
switch want.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
// ok
default:
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
i, c.receiver, c.method, want, c.origin)
}
} else if got.AssignableTo(want) {
// Assignable type relation. Make the assignment now so that the generated code
// can return the values with a type assertion.
v := reflect.New(want).Elem()
v.Set(reflect.ValueOf(ret))
rets[i] = v.Interface()
} else {
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
i, c.receiver, c.method, got, want, c.origin)
}
}
c.addAction(func([]any) []any {
return rets
})
return c
}
// Times declares the exact number of times a function call is expected to be executed.
func (c *Call) Times(n int) *Call {
c.minCalls, c.maxCalls = n, n
return c
}
// SetArg declares an action that will set the nth argument's value,
// indirected through a pointer. Or, in the case of a slice and map, SetArg
// will copy value's elements/key-value pairs into the nth argument.
func (c *Call) SetArg(n int, value any) *Call {
c.t.Helper()
mt := c.methodType
// TODO: This will break on variadic methods.
// We will need to check those at invocation time.
if n < 0 || n >= mt.NumIn() {
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",
n, mt.NumIn(), c.origin)
}
// Permit setting argument through an interface.
// In the interface case, we don't (nay, can't) check the type here.
at := mt.In(n)
switch at.Kind() {
case reflect.Ptr:
dt := at.Elem()
if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",
n, vt, dt, c.origin)
}
case reflect.Interface, reflect.Slice, reflect.Map:
// nothing to do
default:
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice non-map type %v [%s]",
n, at, c.origin)
}
c.addAction(func(args []any) []any {
v := reflect.ValueOf(value)
switch reflect.TypeOf(args[n]).Kind() {
case reflect.Slice:
setSlice(args[n], v)
case reflect.Map:
setMap(args[n], v)
default:
reflect.ValueOf(args[n]).Elem().Set(v)
}
return nil
})
return c
}
// isPreReq returns true if other is a direct or indirect prerequisite to c.
func (c *Call) isPreReq(other *Call) bool {
for _, preReq := range c.preReqs {
if other == preReq || preReq.isPreReq(other) {
return true
}
}
return false
}
// After declares that the call may only match after preReq has been exhausted.
func (c *Call) After(preReq *Call) *Call {
c.t.Helper()
if c == preReq {
c.t.Fatalf("A call isn't allowed to be its own prerequisite")
}
if preReq.isPreReq(c) {
c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq)
}
c.preReqs = append(c.preReqs, preReq)
return c
}
// Returns true if the minimum number of calls have been made.
func (c *Call) satisfied() bool {
return c.numCalls >= c.minCalls
}
// Returns true if the maximum number of calls have been made.
func (c *Call) exhausted() bool {
return c.numCalls >= c.maxCalls
}
func (c *Call) String() string {
args := make([]string, len(c.args))
for i, arg := range c.args {
args[i] = arg.String()
}
arguments := strings.Join(args, ", ")
return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin)
}
// Tests if the given call matches the expected call.
// If yes, returns nil. If no, returns error with message explaining why it does not match.
func (c *Call) matches(args []any) error {
if !c.methodType.IsVariadic() {
if len(args) != len(c.args) {
return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d",
c.origin, len(args), len(c.args))
}
for i, m := range c.args {
if !m.Matches(args[i]) {
return fmt.Errorf(
"expected call at %s doesn't match the argument at index %d.\nGot: %v\nWant: %v",
c.origin, i, formatGottenArg(m, args[i]), m,
)
}
}
} else {
if len(c.args) < c.methodType.NumIn()-1 {
return fmt.Errorf("expected call at %s has the wrong number of matchers. Got: %d, want: %d",
c.origin, len(c.args), c.methodType.NumIn()-1)
}
if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) {
return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: %d",
c.origin, len(args), len(c.args))
}
if len(args) < len(c.args)-1 {
return fmt.Errorf("expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d",
c.origin, len(args), len(c.args)-1)
}
for i, m := range c.args {
if i < c.methodType.NumIn()-1 {
// Non-variadic args
if !m.Matches(args[i]) {
return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
c.origin, strconv.Itoa(i), formatGottenArg(m, args[i]), m)
}
continue
}
// The last arg has a possibility of a variadic argument, so let it branch
// sample: Foo(a int, b int, c ...int)
if i < len(c.args) && i < len(args) {
if m.Matches(args[i]) {
// Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any())
// Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher)
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC)
// Got Foo(a, b) want Foo(matcherA, matcherB)
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD)
continue
}
}
// The number of actual args don't match the number of matchers,
// or the last matcher is a slice and the last arg is not.
// If this function still matches it is because the last matcher
// matches all the remaining arguments or the lack of any.
// Convert the remaining arguments, if any, into a slice of the
// expected type.
vArgsType := c.methodType.In(c.methodType.NumIn() - 1)
vArgs := reflect.MakeSlice(vArgsType, 0, len(args)-i)
for _, arg := range args[i:] {
vArgs = reflect.Append(vArgs, reflect.ValueOf(arg))
}
if m.Matches(vArgs.Interface()) {
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any())
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher)
// Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any())
// Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher)
break
}
// Wrong number of matchers or not match. Fail.
// Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD)
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD)
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE)
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD)
// Got Foo(a, b, c) want Foo(matcherA, matcherB)
return fmt.Errorf("expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
c.origin, strconv.Itoa(i), formatGottenArg(m, args[i:]), c.args[i])
}
}
// Check that all prerequisite calls have been satisfied.
for _, preReqCall := range c.preReqs {
if !preReqCall.satisfied() {
return fmt.Errorf("expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v",
c.origin, preReqCall, c)
}
}
// Check that the call is not exhausted.
if c.exhausted() {
return fmt.Errorf("expected call at %s has already been called the max number of times", c.origin)
}
return nil
}
// dropPrereqs tells the expected Call to not re-check prerequisite calls any
// longer, and to return its current set.
func (c *Call) dropPrereqs() (preReqs []*Call) {
preReqs = c.preReqs
c.preReqs = nil
return
}
func (c *Call) call() []func([]any) []any {
c.numCalls++
return c.actions
}
// InOrder declares that the given calls should occur in order.
// It panics if the type of any of the arguments isn't *Call or a generated
// mock with an embedded *Call.
func InOrder(args ...any) {
calls := make([]*Call, 0, len(args))
for i := 0; i < len(args); i++ {
if call := getCall(args[i]); call != nil {
calls = append(calls, call)
continue
}
panic(fmt.Sprintf(
"invalid argument at position %d of type %T, InOrder expects *gomock.Call or generated mock types with an embedded *gomock.Call",
i,
args[i],
))
}
for i := 1; i < len(calls); i++ {
calls[i].After(calls[i-1])
}
}
// getCall checks if the parameter is a *Call or a generated struct
// that wraps a *Call and returns the *Call pointer - if neither, it returns nil.
func getCall(arg any) *Call {
if call, ok := arg.(*Call); ok {
return call
}
t := reflect.ValueOf(arg)
if t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface {
return nil
}
t = t.Elem()
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if !f.CanInterface() {
continue
}
if call, ok := f.Interface().(*Call); ok {
return call
}
}
return nil
}
func setSlice(arg any, v reflect.Value) {
va := reflect.ValueOf(arg)
for i := 0; i < v.Len(); i++ {
va.Index(i).Set(v.Index(i))
}
}
func setMap(arg any, v reflect.Value) {
va := reflect.ValueOf(arg)
for _, e := range va.MapKeys() {
va.SetMapIndex(e, reflect.Value{})
}
for _, e := range v.MapKeys() {
va.SetMapIndex(e, v.MapIndex(e))
}
}
func (c *Call) addAction(action func([]any) []any) {
c.actions = append(c.actions, action)
}
func formatGottenArg(m Matcher, arg any) string {
got := fmt.Sprintf("%v (%T)", arg, arg)
if gs, ok := m.(GotFormatter); ok {
got = gs.Got(arg)
}
return got
}

164
vendor/go.uber.org/mock/gomock/callset.go generated vendored Normal file
View File

@@ -0,0 +1,164 @@
// Copyright 2011 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomock
import (
"bytes"
"errors"
"fmt"
"sync"
)
// callSet represents a set of expected calls, indexed by receiver and method
// name.
type callSet struct {
// Calls that are still expected.
expected map[callSetKey][]*Call
expectedMu *sync.Mutex
// Calls that have been exhausted.
exhausted map[callSetKey][]*Call
// when set to true, existing call expectations are overridden when new call expectations are made
allowOverride bool
}
// callSetKey is the key in the maps in callSet
type callSetKey struct {
receiver any
fname string
}
func newCallSet() *callSet {
return &callSet{
expected: make(map[callSetKey][]*Call),
expectedMu: &sync.Mutex{},
exhausted: make(map[callSetKey][]*Call),
}
}
func newOverridableCallSet() *callSet {
return &callSet{
expected: make(map[callSetKey][]*Call),
expectedMu: &sync.Mutex{},
exhausted: make(map[callSetKey][]*Call),
allowOverride: true,
}
}
// Add adds a new expected call.
func (cs callSet) Add(call *Call) {
key := callSetKey{call.receiver, call.method}
cs.expectedMu.Lock()
defer cs.expectedMu.Unlock()
m := cs.expected
if call.exhausted() {
m = cs.exhausted
}
if cs.allowOverride {
m[key] = make([]*Call, 0)
}
m[key] = append(m[key], call)
}
// Remove removes an expected call.
func (cs callSet) Remove(call *Call) {
key := callSetKey{call.receiver, call.method}
cs.expectedMu.Lock()
defer cs.expectedMu.Unlock()
calls := cs.expected[key]
for i, c := range calls {
if c == call {
// maintain order for remaining calls
cs.expected[key] = append(calls[:i], calls[i+1:]...)
cs.exhausted[key] = append(cs.exhausted[key], call)
break
}
}
}
// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
func (cs callSet) FindMatch(receiver any, method string, args []any) (*Call, error) {
key := callSetKey{receiver, method}
cs.expectedMu.Lock()
defer cs.expectedMu.Unlock()
// Search through the expected calls.
expected := cs.expected[key]
var callsErrors bytes.Buffer
for _, call := range expected {
err := call.matches(args)
if err != nil {
_, _ = fmt.Fprintf(&callsErrors, "\n%v", err)
} else {
return call, nil
}
}
// If we haven't found a match then search through the exhausted calls so we
// get useful error messages.
exhausted := cs.exhausted[key]
for _, call := range exhausted {
if err := call.matches(args); err != nil {
_, _ = fmt.Fprintf(&callsErrors, "\n%v", err)
continue
}
_, _ = fmt.Fprintf(
&callsErrors, "all expected calls for method %q have been exhausted", method,
)
}
if len(expected)+len(exhausted) == 0 {
_, _ = fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method)
}
return nil, errors.New(callsErrors.String())
}
// Failures returns the calls that are not satisfied.
func (cs callSet) Failures() []*Call {
cs.expectedMu.Lock()
defer cs.expectedMu.Unlock()
failures := make([]*Call, 0, len(cs.expected))
for _, calls := range cs.expected {
for _, call := range calls {
if !call.satisfied() {
failures = append(failures, call)
}
}
}
return failures
}
// Satisfied returns true in case all expected calls in this callSet are satisfied.
func (cs callSet) Satisfied() bool {
cs.expectedMu.Lock()
defer cs.expectedMu.Unlock()
for _, calls := range cs.expected {
for _, call := range calls {
if !call.satisfied() {
return false
}
}
}
return true
}

326
vendor/go.uber.org/mock/gomock/controller.go generated vendored Normal file
View File

@@ -0,0 +1,326 @@
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomock
import (
"context"
"fmt"
"reflect"
"runtime"
"sync"
)
// A TestReporter is something that can be used to report test failures. It
// is satisfied by the standard library's *testing.T.
type TestReporter interface {
Errorf(format string, args ...any)
Fatalf(format string, args ...any)
}
// TestHelper is a TestReporter that has the Helper method. It is satisfied
// by the standard library's *testing.T.
type TestHelper interface {
TestReporter
Helper()
}
// cleanuper is used to check if TestHelper also has the `Cleanup` method. A
// common pattern is to pass in a `*testing.T` to
// `NewController(t TestReporter)`. In Go 1.14+, `*testing.T` has a cleanup
// method. This can be utilized to call `Finish()` so the caller of this library
// does not have to.
type cleanuper interface {
Cleanup(func())
}
// A Controller represents the top-level control of a mock ecosystem. It
// defines the scope and lifetime of mock objects, as well as their
// expectations. It is safe to call Controller's methods from multiple
// goroutines. Each test should create a new Controller.
//
// func TestFoo(t *testing.T) {
// ctrl := gomock.NewController(t)
// // ..
// }
//
// func TestBar(t *testing.T) {
// t.Run("Sub-Test-1", st) {
// ctrl := gomock.NewController(st)
// // ..
// })
// t.Run("Sub-Test-2", st) {
// ctrl := gomock.NewController(st)
// // ..
// })
// })
type Controller struct {
// T should only be called within a generated mock. It is not intended to
// be used in user code and may be changed in future versions. T is the
// TestReporter passed in when creating the Controller via NewController.
// If the TestReporter does not implement a TestHelper it will be wrapped
// with a nopTestHelper.
T TestHelper
mu sync.Mutex
expectedCalls *callSet
finished bool
}
// NewController returns a new Controller. It is the preferred way to create a Controller.
//
// Passing [*testing.T] registers cleanup function to automatically call [Controller.Finish]
// when the test and all its subtests complete.
func NewController(t TestReporter, opts ...ControllerOption) *Controller {
h, ok := t.(TestHelper)
if !ok {
h = &nopTestHelper{t}
}
ctrl := &Controller{
T: h,
expectedCalls: newCallSet(),
}
for _, opt := range opts {
opt.apply(ctrl)
}
if c, ok := isCleanuper(ctrl.T); ok {
c.Cleanup(func() {
ctrl.T.Helper()
ctrl.finish(true, nil)
})
}
return ctrl
}
// ControllerOption configures how a Controller should behave.
type ControllerOption interface {
apply(*Controller)
}
type overridableExpectationsOption struct{}
// WithOverridableExpectations allows for overridable call expectations
// i.e., subsequent call expectations override existing call expectations
func WithOverridableExpectations() overridableExpectationsOption {
return overridableExpectationsOption{}
}
func (o overridableExpectationsOption) apply(ctrl *Controller) {
ctrl.expectedCalls = newOverridableCallSet()
}
type cancelReporter struct {
t TestHelper
cancel func()
}
func (r *cancelReporter) Errorf(format string, args ...any) {
r.t.Errorf(format, args...)
}
func (r *cancelReporter) Fatalf(format string, args ...any) {
defer r.cancel()
r.t.Fatalf(format, args...)
}
func (r *cancelReporter) Helper() {
r.t.Helper()
}
// WithContext returns a new Controller and a Context, which is cancelled on any
// fatal failure.
func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {
h, ok := t.(TestHelper)
if !ok {
h = &nopTestHelper{t: t}
}
ctx, cancel := context.WithCancel(ctx)
return NewController(&cancelReporter{t: h, cancel: cancel}), ctx
}
type nopTestHelper struct {
t TestReporter
}
func (h *nopTestHelper) Errorf(format string, args ...any) {
h.t.Errorf(format, args...)
}
func (h *nopTestHelper) Fatalf(format string, args ...any) {
h.t.Fatalf(format, args...)
}
func (h nopTestHelper) Helper() {}
// RecordCall is called by a mock. It should not be called by user code.
func (ctrl *Controller) RecordCall(receiver any, method string, args ...any) *Call {
ctrl.T.Helper()
recv := reflect.ValueOf(receiver)
for i := 0; i < recv.Type().NumMethod(); i++ {
if recv.Type().Method(i).Name == method {
return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
}
}
ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)
panic("unreachable")
}
// RecordCallWithMethodType is called by a mock. It should not be called by user code.
func (ctrl *Controller) RecordCallWithMethodType(receiver any, method string, methodType reflect.Type, args ...any) *Call {
ctrl.T.Helper()
call := newCall(ctrl.T, receiver, method, methodType, args...)
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
ctrl.expectedCalls.Add(call)
return call
}
// Call is called by a mock. It should not be called by user code.
func (ctrl *Controller) Call(receiver any, method string, args ...any) []any {
ctrl.T.Helper()
// Nest this code so we can use defer to make sure the lock is released.
actions := func() []func([]any) []any {
ctrl.T.Helper()
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
if err != nil {
// callerInfo's skip should be updated if the number of calls between the user's test
// and this line changes, i.e. this code is wrapped in another anonymous function.
// 0 is us, 1 is controller.Call(), 2 is the generated mock, and 3 is the user's test.
origin := callerInfo(3)
stringArgs := make([]string, len(args))
for i, arg := range args {
stringArgs[i] = getString(arg)
}
ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, stringArgs, origin, err)
}
// Two things happen here:
// * the matching call no longer needs to check prerequisite calls,
// * and the prerequisite calls are no longer expected, so remove them.
preReqCalls := expected.dropPrereqs()
for _, preReqCall := range preReqCalls {
ctrl.expectedCalls.Remove(preReqCall)
}
actions := expected.call()
if expected.exhausted() {
ctrl.expectedCalls.Remove(expected)
}
return actions
}()
var rets []any
for _, action := range actions {
if r := action(args); r != nil {
rets = r
}
}
return rets
}
// Finish checks to see if all the methods that were expected to be called were called.
// It is not idempotent and therefore can only be invoked once.
//
// Note: If you pass a *testing.T into [NewController], you no longer
// need to call ctrl.Finish() in your test methods.
func (ctrl *Controller) Finish() {
// If we're currently panicking, probably because this is a deferred call.
// This must be recovered in the deferred function.
err := recover()
ctrl.finish(false, err)
}
// Satisfied returns whether all expected calls bound to this Controller have been satisfied.
// Calling Finish is then guaranteed to not fail due to missing calls.
func (ctrl *Controller) Satisfied() bool {
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
return ctrl.expectedCalls.Satisfied()
}
func (ctrl *Controller) finish(cleanup bool, panicErr any) {
ctrl.T.Helper()
ctrl.mu.Lock()
defer ctrl.mu.Unlock()
if ctrl.finished {
if _, ok := isCleanuper(ctrl.T); !ok {
ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
}
return
}
ctrl.finished = true
// Short-circuit, pass through the panic.
if panicErr != nil {
panic(panicErr)
}
// Check that all remaining expected calls are satisfied.
failures := ctrl.expectedCalls.Failures()
for _, call := range failures {
ctrl.T.Errorf("missing call(s) to %v", call)
}
if len(failures) != 0 {
if !cleanup {
ctrl.T.Fatalf("aborting test due to missing call(s)")
return
}
ctrl.T.Errorf("aborting test due to missing call(s)")
}
}
// callerInfo returns the file:line of the call site. skip is the number
// of stack frames to skip when reporting. 0 is callerInfo's call site.
func callerInfo(skip int) string {
if _, file, line, ok := runtime.Caller(skip + 1); ok {
return fmt.Sprintf("%s:%d", file, line)
}
return "unknown file"
}
// isCleanuper checks it if t's base TestReporter has a Cleanup method.
func isCleanuper(t TestReporter) (cleanuper, bool) {
tr := unwrapTestReporter(t)
c, ok := tr.(cleanuper)
return c, ok
}
// unwrapTestReporter unwraps TestReporter to the base implementation.
func unwrapTestReporter(t TestReporter) TestReporter {
tr := t
switch nt := t.(type) {
case *cancelReporter:
tr = nt.t
if h, check := tr.(*nopTestHelper); check {
tr = h.t
}
case *nopTestHelper:
tr = nt.t
default:
// not wrapped
}
return tr
}

60
vendor/go.uber.org/mock/gomock/doc.go generated vendored Normal file
View File

@@ -0,0 +1,60 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package gomock is a mock framework for Go.
//
// Standard usage:
//
// (1) Define an interface that you wish to mock.
// type MyInterface interface {
// SomeMethod(x int64, y string)
// }
// (2) Use mockgen to generate a mock from the interface.
// (3) Use the mock in a test:
// func TestMyThing(t *testing.T) {
// mockCtrl := gomock.NewController(t)
// mockObj := something.NewMockMyInterface(mockCtrl)
// mockObj.EXPECT().SomeMethod(4, "blah")
// // pass mockObj to a real object and play with it.
// }
//
// By default, expected calls are not enforced to run in any particular order.
// Call order dependency can be enforced by use of InOrder and/or Call.After.
// Call.After can create more varied call order dependencies, but InOrder is
// often more convenient.
//
// The following examples create equivalent call order dependencies.
//
// Example of using Call.After to chain expected call order:
//
// firstCall := mockObj.EXPECT().SomeMethod(1, "first")
// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
//
// Example of using InOrder to declare expected call order:
//
// gomock.InOrder(
// mockObj.EXPECT().SomeMethod(1, "first"),
// mockObj.EXPECT().SomeMethod(2, "second"),
// mockObj.EXPECT().SomeMethod(3, "third"),
// )
//
// The standard TestReporter most users will pass to `NewController` is a
// `*testing.T` from the context of the test. Note that this will use the
// standard `t.Error` and `t.Fatal` methods to report what happened in the test.
// In some cases this can leave your testing package in a weird state if global
// state is used since `t.Fatal` is like calling panic in the middle of a
// function. In these cases it is recommended that you pass in your own
// `TestReporter`.
package gomock

447
vendor/go.uber.org/mock/gomock/matchers.go generated vendored Normal file
View File

@@ -0,0 +1,447 @@
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomock
import (
"fmt"
"reflect"
"regexp"
"strings"
)
// A Matcher is a representation of a class of values.
// It is used to represent the valid or expected arguments to a mocked method.
type Matcher interface {
// Matches returns whether x is a match.
Matches(x any) bool
// String describes what the matcher matches.
String() string
}
// WantFormatter modifies the given Matcher's String() method to the given
// Stringer. This allows for control on how the "Want" is formatted when
// printing .
func WantFormatter(s fmt.Stringer, m Matcher) Matcher {
type matcher interface {
Matches(x any) bool
}
return struct {
matcher
fmt.Stringer
}{
matcher: m,
Stringer: s,
}
}
// StringerFunc type is an adapter to allow the use of ordinary functions as
// a Stringer. If f is a function with the appropriate signature,
// StringerFunc(f) is a Stringer that calls f.
type StringerFunc func() string
// String implements fmt.Stringer.
func (f StringerFunc) String() string {
return f()
}
// GotFormatter is used to better print failure messages. If a matcher
// implements GotFormatter, it will use the result from Got when printing
// the failure message.
type GotFormatter interface {
// Got is invoked with the received value. The result is used when
// printing the failure message.
Got(got any) string
}
// GotFormatterFunc type is an adapter to allow the use of ordinary
// functions as a GotFormatter. If f is a function with the appropriate
// signature, GotFormatterFunc(f) is a GotFormatter that calls f.
type GotFormatterFunc func(got any) string
// Got implements GotFormatter.
func (f GotFormatterFunc) Got(got any) string {
return f(got)
}
// GotFormatterAdapter attaches a GotFormatter to a Matcher.
func GotFormatterAdapter(s GotFormatter, m Matcher) Matcher {
return struct {
GotFormatter
Matcher
}{
GotFormatter: s,
Matcher: m,
}
}
type anyMatcher struct{}
func (anyMatcher) Matches(any) bool {
return true
}
func (anyMatcher) String() string {
return "is anything"
}
type condMatcher[T any] struct {
fn func(x T) bool
}
func (c condMatcher[T]) Matches(x any) bool {
typed, ok := x.(T)
if !ok {
return false
}
return c.fn(typed)
}
func (c condMatcher[T]) String() string {
return "adheres to a custom condition"
}
type eqMatcher struct {
x any
}
func (e eqMatcher) Matches(x any) bool {
// In case, some value is nil
if e.x == nil || x == nil {
return reflect.DeepEqual(e.x, x)
}
// Check if types assignable and convert them to common type
x1Val := reflect.ValueOf(e.x)
x2Val := reflect.ValueOf(x)
if x1Val.Type().AssignableTo(x2Val.Type()) {
x1ValConverted := x1Val.Convert(x2Val.Type())
return reflect.DeepEqual(x1ValConverted.Interface(), x2Val.Interface())
}
return false
}
func (e eqMatcher) String() string {
return fmt.Sprintf("is equal to %s (%T)", getString(e.x), e.x)
}
type nilMatcher struct{}
func (nilMatcher) Matches(x any) bool {
if x == nil {
return true
}
v := reflect.ValueOf(x)
switch v.Kind() {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Slice:
return v.IsNil()
}
return false
}
func (nilMatcher) String() string {
return "is nil"
}
type notMatcher struct {
m Matcher
}
func (n notMatcher) Matches(x any) bool {
return !n.m.Matches(x)
}
func (n notMatcher) String() string {
return "not(" + n.m.String() + ")"
}
type regexMatcher struct {
regex *regexp.Regexp
}
func (m regexMatcher) Matches(x any) bool {
switch t := x.(type) {
case string:
return m.regex.MatchString(t)
case []byte:
return m.regex.Match(t)
default:
return false
}
}
func (m regexMatcher) String() string {
return "matches regex " + m.regex.String()
}
type assignableToTypeOfMatcher struct {
targetType reflect.Type
}
func (m assignableToTypeOfMatcher) Matches(x any) bool {
return reflect.TypeOf(x).AssignableTo(m.targetType)
}
func (m assignableToTypeOfMatcher) String() string {
return "is assignable to " + m.targetType.Name()
}
type anyOfMatcher struct {
matchers []Matcher
}
func (am anyOfMatcher) Matches(x any) bool {
for _, m := range am.matchers {
if m.Matches(x) {
return true
}
}
return false
}
func (am anyOfMatcher) String() string {
ss := make([]string, 0, len(am.matchers))
for _, matcher := range am.matchers {
ss = append(ss, matcher.String())
}
return strings.Join(ss, " | ")
}
type allMatcher struct {
matchers []Matcher
}
func (am allMatcher) Matches(x any) bool {
for _, m := range am.matchers {
if !m.Matches(x) {
return false
}
}
return true
}
func (am allMatcher) String() string {
ss := make([]string, 0, len(am.matchers))
for _, matcher := range am.matchers {
ss = append(ss, matcher.String())
}
return strings.Join(ss, "; ")
}
type lenMatcher struct {
i int
}
func (m lenMatcher) Matches(x any) bool {
v := reflect.ValueOf(x)
switch v.Kind() {
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == m.i
default:
return false
}
}
func (m lenMatcher) String() string {
return fmt.Sprintf("has length %d", m.i)
}
type inAnyOrderMatcher struct {
x any
}
func (m inAnyOrderMatcher) Matches(x any) bool {
given, ok := m.prepareValue(x)
if !ok {
return false
}
wanted, ok := m.prepareValue(m.x)
if !ok {
return false
}
if given.Len() != wanted.Len() {
return false
}
usedFromGiven := make([]bool, given.Len())
foundFromWanted := make([]bool, wanted.Len())
for i := 0; i < wanted.Len(); i++ {
wantedMatcher := Eq(wanted.Index(i).Interface())
for j := 0; j < given.Len(); j++ {
if usedFromGiven[j] {
continue
}
if wantedMatcher.Matches(given.Index(j).Interface()) {
foundFromWanted[i] = true
usedFromGiven[j] = true
break
}
}
}
missingFromWanted := 0
for _, found := range foundFromWanted {
if !found {
missingFromWanted++
}
}
extraInGiven := 0
for _, used := range usedFromGiven {
if !used {
extraInGiven++
}
}
return extraInGiven == 0 && missingFromWanted == 0
}
func (m inAnyOrderMatcher) prepareValue(x any) (reflect.Value, bool) {
xValue := reflect.ValueOf(x)
switch xValue.Kind() {
case reflect.Slice, reflect.Array:
return xValue, true
default:
return reflect.Value{}, false
}
}
func (m inAnyOrderMatcher) String() string {
return fmt.Sprintf("has the same elements as %v", m.x)
}
// Constructors
// All returns a composite Matcher that returns true if and only all of the
// matchers return true.
func All(ms ...Matcher) Matcher { return allMatcher{ms} }
// Any returns a matcher that always matches.
func Any() Matcher { return anyMatcher{} }
// Cond returns a matcher that matches when the given function returns true
// after passing it the parameter to the mock function.
// This is particularly useful in case you want to match over a field of a custom struct, or dynamic logic.
//
// Example usage:
//
// Cond(func(x int){return x == 1}).Matches(1) // returns true
// Cond(func(x int){return x == 2}).Matches(1) // returns false
func Cond[T any](fn func(x T) bool) Matcher { return condMatcher[T]{fn} }
// AnyOf returns a composite Matcher that returns true if at least one of the
// matchers returns true.
//
// Example usage:
//
// AnyOf(1, 2, 3).Matches(2) // returns true
// AnyOf(1, 2, 3).Matches(10) // returns false
// AnyOf(Nil(), Len(2)).Matches(nil) // returns true
// AnyOf(Nil(), Len(2)).Matches("hi") // returns true
// AnyOf(Nil(), Len(2)).Matches("hello") // returns false
func AnyOf(xs ...any) Matcher {
ms := make([]Matcher, 0, len(xs))
for _, x := range xs {
if m, ok := x.(Matcher); ok {
ms = append(ms, m)
} else {
ms = append(ms, Eq(x))
}
}
return anyOfMatcher{ms}
}
// Eq returns a matcher that matches on equality.
//
// Example usage:
//
// Eq(5).Matches(5) // returns true
// Eq(5).Matches(4) // returns false
func Eq(x any) Matcher { return eqMatcher{x} }
// Len returns a matcher that matches on length. This matcher returns false if
// is compared to a type that is not an array, chan, map, slice, or string.
func Len(i int) Matcher {
return lenMatcher{i}
}
// Nil returns a matcher that matches if the received value is nil.
//
// Example usage:
//
// var x *bytes.Buffer
// Nil().Matches(x) // returns true
// x = &bytes.Buffer{}
// Nil().Matches(x) // returns false
func Nil() Matcher { return nilMatcher{} }
// Not reverses the results of its given child matcher.
//
// Example usage:
//
// Not(Eq(5)).Matches(4) // returns true
// Not(Eq(5)).Matches(5) // returns false
func Not(x any) Matcher {
if m, ok := x.(Matcher); ok {
return notMatcher{m}
}
return notMatcher{Eq(x)}
}
// Regex checks whether parameter matches the associated regex.
//
// Example usage:
//
// Regex("[0-9]{2}:[0-9]{2}").Matches("23:02") // returns true
// Regex("[0-9]{2}:[0-9]{2}").Matches([]byte{'2', '3', ':', '0', '2'}) // returns true
// Regex("[0-9]{2}:[0-9]{2}").Matches("hello world") // returns false
// Regex("[0-9]{2}").Matches(21) // returns false as it's not a valid type
func Regex(regexStr string) Matcher {
return regexMatcher{regex: regexp.MustCompile(regexStr)}
}
// AssignableToTypeOf is a Matcher that matches if the parameter to the mock
// function is assignable to the type of the parameter to this function.
//
// Example usage:
//
// var s fmt.Stringer = &bytes.Buffer{}
// AssignableToTypeOf(s).Matches(time.Second) // returns true
// AssignableToTypeOf(s).Matches(99) // returns false
//
// var ctx = reflect.TypeOf((*context.Context)(nil)).Elem()
// AssignableToTypeOf(ctx).Matches(context.Background()) // returns true
func AssignableToTypeOf(x any) Matcher {
if xt, ok := x.(reflect.Type); ok {
return assignableToTypeOfMatcher{xt}
}
return assignableToTypeOfMatcher{reflect.TypeOf(x)}
}
// InAnyOrder is a Matcher that returns true for collections of the same elements ignoring the order.
//
// Example usage:
//
// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 3, 2}) // returns true
// InAnyOrder([]int{1, 2, 3}).Matches([]int{1, 2}) // returns false
func InAnyOrder(x any) Matcher {
return inAnyOrderMatcher{x}
}

36
vendor/go.uber.org/mock/gomock/string.go generated vendored Normal file
View File

@@ -0,0 +1,36 @@
package gomock
import (
"fmt"
"reflect"
)
// getString is a safe way to convert a value to a string for printing results
// If the value is a a mock, getString avoids calling the mocked String() method,
// which avoids potential deadlocks
func getString(x any) string {
if isGeneratedMock(x) {
return fmt.Sprintf("%T", x)
}
if s, ok := x.(fmt.Stringer); ok {
return s.String()
}
return fmt.Sprintf("%v", x)
}
// isGeneratedMock checks if the given type has a "isgomock" field,
// indicating it is a generated mock.
func isGeneratedMock(x any) bool {
typ := reflect.TypeOf(x)
if typ == nil {
return false
}
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
if typ.Kind() != reflect.Struct {
return false
}
_, isgomock := typ.FieldByName("isgomock")
return isgomock
}

41
vendor/go.uber.org/mock/mockgen/deprecated.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package main
import (
"flag"
"log"
"os"
)
const (
deprecatedFlagProgOnly = "prog_only"
deprecatedFlagExecOnly = "exec_only"
)
var (
_ = flag.Bool("prog_only", false, "DEPRECATED (reflect mode) Only generate the reflection program; write it to stdout and exit.")
_ = flag.String("exec_only", "", "DEPRECATED (reflect mode) If set, execute this reflection program.")
)
// notifyAboutDeprecatedFlags prints a warning message for a deprecated flags if they are set.
func notifyAboutDeprecatedFlags() {
const resetColorPostfix = "\033[0m"
logger := initWarningLogger()
flag.Visit(func(f *flag.Flag) {
switch f.Name {
case deprecatedFlagProgOnly:
logger.Println("The -prog_only flag is deprecated and has no effect.", resetColorPostfix)
case deprecatedFlagExecOnly:
logger.Println("The -exec_only flag is deprecated and has no effect.", resetColorPostfix)
}
})
}
func initWarningLogger() *log.Logger {
const (
yellowColor = "\033[33m"
warningPrefix = yellowColor + "WARNING: "
)
return log.New(os.Stdout, warningPrefix, log.Ldate|log.Ltime)
}

View File

@@ -5,9 +5,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build go1.18
// +build go1.18
package main
import (
@@ -15,7 +12,6 @@ import (
"fmt"
"go/ast"
"go/token"
"strings"
"go.uber.org/mock/mockgen/model"
)
@@ -67,29 +63,6 @@ func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]m
return nil, nil
}
func getIdentTypeParams(decl any) string {
if decl == nil {
return ""
}
ts, ok := decl.(*ast.TypeSpec)
if !ok {
return ""
}
if ts.TypeParams == nil || len(ts.TypeParams.List) == 0 {
return ""
}
var sb strings.Builder
sb.WriteString("[")
for i, v := range ts.TypeParams.List {
if i != 0 {
sb.WriteString(", ")
}
sb.WriteString(v.Names[0].Name)
}
sb.WriteString("]")
return sb.String()
}
func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) {
var indices []ast.Expr
var typ ast.Expr

View File

@@ -1,41 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !go1.18
// +build !go1.18
package main
import (
"fmt"
"go/ast"
"go.uber.org/mock/mockgen/model"
)
func getTypeSpecTypeParams(ts *ast.TypeSpec) []*ast.Field {
return nil
}
func (p *fileParser) parseGenericType(pkg string, typ ast.Expr, tps map[string]model.Type) (model.Type, error) {
return nil, nil
}
func getIdentTypeParams(decl any) string {
return ""
}
func (p *fileParser) parseGenericMethod(field *ast.Field, it *namedInterface, iface *model.Interface, pkg string, tps map[string]model.Type) ([]*model.Method, error) {
return nil, fmt.Errorf("don't know how to mock method of type %T", field.Type)
}

21
vendor/go.uber.org/mock/mockgen/gob.go generated vendored Normal file
View File

@@ -0,0 +1,21 @@
package main
import (
"encoding/gob"
"os"
"go.uber.org/mock/mockgen/model"
)
func gobMode(path string) (*model.Package, error) {
in, err := os.Open(path)
if err != nil {
return nil, err
}
defer in.Close()
var pkg model.Package
if err := gob.NewDecoder(in).Decode(&pkg); err != nil {
return nil, err
}
return &pkg, nil
}

View File

@@ -59,14 +59,17 @@ var (
mockNames = flag.String("mock_names", "", "Comma-separated interfaceName=mockName pairs of explicit mock names to use. Mock names default to 'Mock'+ interfaceName suffix.")
packageOut = flag.String("package", "", "Package of the generated code; defaults to the package of the input with a 'mock_' prefix.")
selfPackage = flag.String("self_package", "", "The full package import path for the generated code. The purpose of this flag is to prevent import cycles in the generated code by trying to include its own package. This can happen if the mock's package is set to one of its inputs (usually the main one) and the output is stdio so mockgen cannot detect the final output package. Setting this flag will then tell mockgen which import to exclude.")
writeCmdComment = flag.Bool("write_command_comment", true, "Writes the command used as a comment if true.")
writePkgComment = flag.Bool("write_package_comment", true, "Writes package documentation comment (godoc) if true.")
writeSourceComment = flag.Bool("write_source_comment", true, "Writes original file (source mode) or interface names (reflect mode) comment if true.")
writeSourceComment = flag.Bool("write_source_comment", true, "Writes original file (source mode) or interface names (package mode) comment if true.")
writeGenerateDirective = flag.Bool("write_generate_directive", false, "Add //go:generate directive to regenerate the mock")
copyrightFile = flag.String("copyright_file", "", "Copyright file used to add copyright header")
buildConstraint = flag.String("build_constraint", "", "If non-empty, added as //go:build <constraint>")
typed = flag.Bool("typed", false, "Generate Type-safe 'Return', 'Do', 'DoAndReturn' function")
imports = flag.String("imports", "", "(source mode) Comma-separated name=path pairs of explicit imports to use.")
auxFiles = flag.String("aux_files", "", "(source mode) Comma-separated pkg=path pairs of auxiliary Go source files.")
excludeInterfaces = flag.String("exclude_interfaces", "", "Comma-separated names of interfaces to be excluded")
excludeInterfaces = flag.String("exclude_interfaces", "", "(source mode) Comma-separated names of interfaces to be excluded")
modelGob = flag.String("model_gob", "", "Skip package/source loading entirely and use the gob encoded model.Package at the given path")
debugParser = flag.Bool("debug_parser", false, "Print out parser results only.")
showVersion = flag.Bool("version", false, "Print version.")
@@ -76,6 +79,8 @@ func main() {
flag.Usage = usage
flag.Parse()
notifyAboutDeprecatedFlags()
if *showVersion {
printVersion()
return
@@ -84,7 +89,9 @@ func main() {
var pkg *model.Package
var err error
var packageName string
if *source != "" {
if *modelGob != "" {
pkg, err = gobMode(*modelGob)
} else if *source != "" {
pkg, err = sourceMode(*source)
} else {
if flag.NArg() != 2 {
@@ -103,7 +110,8 @@ func main() {
log.Fatalf("Parse package name failed: %v", err)
}
}
pkg, err = reflectMode(packageName, interfaces)
parser := packageModeParser{}
pkg, err = parser.parsePackage(packageName, interfaces)
}
if err != nil {
log.Fatalf("Loading input failed: %v", err)
@@ -116,7 +124,7 @@ func main() {
outputPackageName := *packageOut
if outputPackageName == "" {
// pkg.Name in reflect mode is the base name of the import path,
// pkg.Name in package mode is the base name of the import path,
// which might have characters that are illegal to have in package names.
outputPackageName = "mock_" + sanitize(pkg.Name)
}
@@ -142,7 +150,9 @@ func main() {
}
}
g := new(generator)
g := &generator{
buildConstraint: *buildConstraint,
}
if *source != "" {
g.filename = *source
} else {
@@ -225,20 +235,21 @@ func usage() {
flag.PrintDefaults()
}
const usageText = `mockgen has two modes of operation: source and reflect.
const usageText = `mockgen has two modes of operation: source and package.
Source mode generates mock interfaces from a source file.
It is enabled by using the -source flag. Other flags that
may be useful in this mode are -imports and -aux_files.
may be useful in this mode are -imports, -aux_files and -exclude_interfaces.
Example:
mockgen -source=foo.go [other options]
Reflect mode generates mock interfaces by building a program
that uses reflection to understand interfaces. It is enabled
by passing two non-flag arguments: an import path, and a
comma-separated list of symbols.
Package mode works by specifying the package and interface names.
It is enabled by passing two non-flag arguments: an import path, and a
comma-separated list of symbols.
You can use "." to refer to the current path's package.
Example:
mockgen database/sql/driver Conn,Driver
mockgen . SomeInterface
`
@@ -250,12 +261,13 @@ type generator struct {
destination string // may be empty
srcPackage, srcInterfaces string // may be empty
copyrightHeader string
buildConstraint string // may be empty
packageMap map[string]string // map from import path to package name
}
func (g *generator) p(format string, args ...any) {
fmt.Fprintf(&g.buf, g.indent+format+"\n", args...)
_, _ = fmt.Fprintf(&g.buf, g.indent+format+"\n", args...)
}
func (g *generator) in() {
@@ -305,6 +317,12 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac
g.p("")
}
if g.buildConstraint != "" {
g.p("//go:build %s", g.buildConstraint)
// https://pkg.go.dev/cmd/go#hdr-Build_constraints:~:text=a%20build%20constraint%20should%20be%20followed%20by%20a%20blank%20line
g.p("")
}
g.p("// Code generated by MockGen. DO NOT EDIT.")
if *writeSourceComment {
if g.filename != "" {
@@ -313,16 +331,18 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac
g.p("// Source: %v (interfaces: %v)", g.srcPackage, g.srcInterfaces)
}
}
g.p("//")
g.p("// Generated by this command:")
g.p("//")
// only log the name of the executable, not the full path
name := filepath.Base(os.Args[0])
if runtime.GOOS == "windows" {
name = strings.TrimSuffix(name, ".exe")
if *writeCmdComment {
g.p("//")
g.p("// Generated by this command:")
g.p("//")
// only log the name of the executable, not the full path
name := filepath.Base(os.Args[0])
if runtime.GOOS == "windows" {
name = strings.TrimSuffix(name, ".exe")
}
g.p("//\t%v", strings.Join(append([]string{name}, os.Args[1:]...), " "))
g.p("//")
}
g.p("//\t%v", strings.Join(append([]string{name}, os.Args[1:]...), " "))
g.p("//")
// Get all required imports, and generate unique names for them all.
im := pkg.Imports()
@@ -392,11 +412,13 @@ func (g *generator) Generate(pkg *model.Package, outputPkgName string, outputPac
localNames[pkgName] = true
}
if *writePkgComment {
// Ensure there's an empty line before the package to follow the recommendations:
// https://github.com/golang/go/wiki/CodeReviewComments#package-comments
g.p("")
// Ensure there is an empty line between “generated by” block and
// package documentation comments to follow the recommendations:
// https://go.dev/wiki/CodeReviewComments#package-comments
// That is, “generated by” should not be a package comment.
g.p("")
if *writePkgComment {
g.p("// Package %v is a generated GoMock package.", outputPkgName)
}
g.p("package %v", outputPkgName)
@@ -472,6 +494,7 @@ func (g *generator) GenerateMockInterface(intf *model.Interface, outputPackagePa
g.in()
g.p("ctrl *gomock.Controller")
g.p("recorder *%vMockRecorder%v", mockType, shortTp)
g.p("isgomock struct{}")
g.out()
g.p("}")
g.p("")
@@ -816,7 +839,7 @@ func createPackageMap(importPaths []string) map[string]string {
}
pkgMap := make(map[string]string)
b := bytes.NewBuffer(nil)
args := []string{"list", "-json"}
args := []string{"list", "-json=ImportPath,Name"}
args = append(args, importPaths...)
cmd := exec.Command("go", args...)
cmd.Stdout = b

358
vendor/go.uber.org/mock/mockgen/package_mode.go generated vendored Normal file
View File

@@ -0,0 +1,358 @@
package main
import (
"errors"
"flag"
"fmt"
"go/types"
"strings"
"go.uber.org/mock/mockgen/model"
"golang.org/x/tools/go/packages"
)
var (
buildFlags = flag.String("build_flags", "", "(package mode) Additional flags for go build.")
)
type packageModeParser struct {
pkgName string
}
func (p *packageModeParser) parsePackage(packageName string, ifaces []string) (*model.Package, error) {
p.pkgName = packageName
pkg, err := p.loadPackage(packageName)
if err != nil {
return nil, fmt.Errorf("load package: %w", err)
}
interfaces, err := p.extractInterfacesFromPackage(pkg, ifaces)
if err != nil {
return nil, fmt.Errorf("extract interfaces from package: %w", err)
}
return &model.Package{
Name: pkg.Types.Name(),
PkgPath: packageName,
Interfaces: interfaces,
}, nil
}
func (p *packageModeParser) loadPackage(packageName string) (*packages.Package, error) {
var buildFlagsSet []string
if *buildFlags != "" {
buildFlagsSet = strings.Split(*buildFlags, " ")
}
cfg := &packages.Config{
Mode: packages.NeedDeps | packages.NeedImports | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedEmbedFiles,
BuildFlags: buildFlagsSet,
}
pkgs, err := packages.Load(cfg, packageName)
if err != nil {
return nil, fmt.Errorf("load packages: %w", err)
}
if len(pkgs) != 1 {
return nil, fmt.Errorf("packages length must be 1: %d", len(pkgs))
}
if len(pkgs[0].Errors) > 0 {
errs := make([]error, len(pkgs[0].Errors))
for i, err := range pkgs[0].Errors {
errs[i] = err
}
return nil, errors.Join(errs...)
}
return pkgs[0], nil
}
func (p *packageModeParser) extractInterfacesFromPackage(pkg *packages.Package, ifaces []string) ([]*model.Interface, error) {
interfaces := make([]*model.Interface, len(ifaces))
for i, iface := range ifaces {
obj := pkg.Types.Scope().Lookup(iface)
if obj == nil {
return nil, fmt.Errorf("interface %s does not exist", iface)
}
modelIface, err := p.parseInterface(obj)
if err != nil {
return nil, newParseTypeError("parse interface", obj.Name(), err)
}
interfaces[i] = modelIface
}
return interfaces, nil
}
func (p *packageModeParser) parseInterface(obj types.Object) (*model.Interface, error) {
named, ok := types.Unalias(obj.Type()).(*types.Named)
if !ok {
return nil, fmt.Errorf("%s is not an interface. it is a %T", obj.Name(), obj.Type().Underlying())
}
iface, ok := named.Underlying().(*types.Interface)
if !ok {
return nil, fmt.Errorf("%s is not an interface. it is a %T", obj.Name(), obj.Type().Underlying())
}
if p.isConstraint(iface) {
return nil, fmt.Errorf("interface %s is a constraint", obj.Name())
}
methods := make([]*model.Method, iface.NumMethods())
for i := range iface.NumMethods() {
method := iface.Method(i)
typedMethod, ok := method.Type().(*types.Signature)
if !ok {
return nil, fmt.Errorf("method %s is not a signature", method.Name())
}
modelFunc, err := p.parseFunc(typedMethod)
if err != nil {
return nil, newParseTypeError("parse method", typedMethod.String(), err)
}
methods[i] = &model.Method{
Name: method.Name(),
In: modelFunc.In,
Out: modelFunc.Out,
Variadic: modelFunc.Variadic,
}
}
if named.TypeParams() == nil {
return &model.Interface{Name: obj.Name(), Methods: methods}, nil
}
typeParams := make([]*model.Parameter, named.TypeParams().Len())
for i := range named.TypeParams().Len() {
param := named.TypeParams().At(i)
typeParam, err := p.parseConstraint(param)
if err != nil {
return nil, newParseTypeError("parse type parameter", param.String(), err)
}
typeParams[i] = &model.Parameter{Name: param.Obj().Name(), Type: typeParam}
}
return &model.Interface{Name: obj.Name(), Methods: methods, TypeParams: typeParams}, nil
}
func (o *packageModeParser) isConstraint(t *types.Interface) bool {
for i := range t.NumEmbeddeds() {
embed := t.EmbeddedType(i)
if _, ok := embed.Underlying().(*types.Interface); !ok {
return true
}
}
return false
}
func (p *packageModeParser) parseType(t types.Type) (model.Type, error) {
switch t := t.(type) {
case *types.Array:
elementType, err := p.parseType(t.Elem())
if err != nil {
return nil, newParseTypeError("parse array type", t.Elem().String(), err)
}
return &model.ArrayType{Len: int(t.Len()), Type: elementType}, nil
case *types.Slice:
elementType, err := p.parseType(t.Elem())
if err != nil {
return nil, newParseTypeError("parse slice type", t.Elem().String(), err)
}
return &model.ArrayType{Len: -1, Type: elementType}, nil
case *types.Chan:
var dir model.ChanDir
switch t.Dir() {
case types.RecvOnly:
dir = model.RecvDir
case types.SendOnly:
dir = model.SendDir
}
chanType, err := p.parseType(t.Elem())
if err != nil {
return nil, newParseTypeError("parse chan type", t.Elem().String(), err)
}
return &model.ChanType{Dir: dir, Type: chanType}, nil
case *types.Signature:
sig, err := p.parseFunc(t)
if err != nil {
return nil, newParseTypeError("parse signature", t.String(), err)
}
return sig, nil
case *types.Named, *types.Alias:
object := t.(interface{ Obj() *types.TypeName })
var pkg string
if object.Obj().Pkg() != nil {
pkg = object.Obj().Pkg().Path()
}
// TypeArgs method not available for aliases in go1.22
genericType, ok := t.(interface{ TypeArgs() *types.TypeList })
if !ok || genericType.TypeArgs() == nil {
return &model.NamedType{
Package: pkg,
Type: object.Obj().Name(),
}, nil
}
typeParams := &model.TypeParametersType{TypeParameters: make([]model.Type, genericType.TypeArgs().Len())}
for i := range genericType.TypeArgs().Len() {
typeParam := genericType.TypeArgs().At(i)
typedParam, err := p.parseType(typeParam)
if err != nil {
return nil, newParseTypeError("parse type parameter", typeParam.String(), err)
}
typeParams.TypeParameters[i] = typedParam
}
return &model.NamedType{
Package: pkg,
Type: object.Obj().Name(),
TypeParams: typeParams,
}, nil
case *types.Interface:
if t.Empty() {
return model.PredeclaredType("any"), nil
}
return nil, fmt.Errorf("cannot handle non-empty unnamed interfaces")
case *types.Map:
key, err := p.parseType(t.Key())
if err != nil {
return nil, newParseTypeError("parse map key", t.Key().String(), err)
}
value, err := p.parseType(t.Elem())
if err != nil {
return nil, newParseTypeError("parse map value", t.Elem().String(), err)
}
return &model.MapType{Key: key, Value: value}, nil
case *types.Pointer:
valueType, err := p.parseType(t.Elem())
if err != nil {
return nil, newParseTypeError("parse pointer type", t.Elem().String(), err)
}
return &model.PointerType{Type: valueType}, nil
case *types.Struct:
if t.NumFields() > 0 {
return nil, fmt.Errorf("cannot handle non-empty unnamed structs")
}
return model.PredeclaredType("struct{}"), nil
case *types.Basic:
return model.PredeclaredType(t.Name()), nil
case *types.Tuple:
panic("tuple field") // TODO
case *types.TypeParam:
return &model.NamedType{Type: t.Obj().Name()}, nil
default:
panic("unknown type") // TODO
}
}
func (p *packageModeParser) parseFunc(sig *types.Signature) (*model.FuncType, error) {
var variadic *model.Parameter
params := make([]*model.Parameter, 0, sig.Params().Len())
for i := range sig.Params().Len() {
param := sig.Params().At(i)
isVariadicParam := i == sig.Params().Len()-1 && sig.Variadic()
parseType := param.Type()
if isVariadicParam {
sliceType, ok := param.Type().(*types.Slice)
if !ok {
return nil, newParseTypeError("variadic parameter is not a slice", param.String(), nil)
}
parseType = sliceType.Elem()
}
paramType, err := p.parseType(parseType)
if err != nil {
return nil, newParseTypeError("parse parameter type", parseType.String(), err)
}
modelParameter := &model.Parameter{Type: paramType, Name: param.Name()}
if isVariadicParam {
variadic = modelParameter
} else {
params = append(params, modelParameter)
}
}
if len(params) == 0 {
params = nil
}
results := make([]*model.Parameter, sig.Results().Len())
for i := range sig.Results().Len() {
result := sig.Results().At(i)
resultType, err := p.parseType(result.Type())
if err != nil {
return nil, newParseTypeError("parse result type", result.Type().String(), err)
}
results[i] = &model.Parameter{Type: resultType, Name: result.Name()}
}
if len(results) == 0 {
results = nil
}
return &model.FuncType{
In: params,
Out: results,
Variadic: variadic,
}, nil
}
func (p *packageModeParser) parseConstraint(t *types.TypeParam) (model.Type, error) {
if t == nil {
return nil, fmt.Errorf("nil type param")
}
typeParam, err := p.parseType(t.Constraint())
if err != nil {
return nil, newParseTypeError("parse constraint type", t.Constraint().String(), err)
}
return typeParam, nil
}
type parseTypeError struct {
message string
typeString string
error error
}
func newParseTypeError(message string, typeString string, error error) *parseTypeError {
return &parseTypeError{typeString: typeString, error: error, message: message}
}
func (p parseTypeError) Error() string {
if p.error != nil {
return fmt.Sprintf("%s: error parsing %s: %s", p.message, p.typeString, p.error)
}
return fmt.Sprintf("%s: error parsing type %s", p.message, p.typeString)
}
func (p parseTypeError) Unwrap() error {
return p.error
}

View File

@@ -1,256 +0,0 @@
// Copyright 2012 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
// This file contains the model construction by reflection.
import (
"bytes"
"encoding/gob"
"flag"
"fmt"
"go/build"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"text/template"
"go.uber.org/mock/mockgen/model"
)
var (
progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.")
execOnly = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.")
buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.")
)
// reflectMode generates mocks via reflection on an interface.
func reflectMode(importPath string, symbols []string) (*model.Package, error) {
if *execOnly != "" {
return run(*execOnly)
}
program, err := writeProgram(importPath, symbols)
if err != nil {
return nil, err
}
if *progOnly {
if _, err := os.Stdout.Write(program); err != nil {
return nil, err
}
os.Exit(0)
}
wd, _ := os.Getwd()
// Try to run the reflection program in the current working directory.
if p, err := runInDir(program, wd); err == nil {
return p, nil
}
// Try to run the program in the same directory as the input package.
if p, err := build.Import(importPath, wd, build.FindOnly); err == nil {
dir := p.Dir
if p, err := runInDir(program, dir); err == nil {
return p, nil
}
}
// Try to run it in a standard temp directory.
return runInDir(program, "")
}
func writeProgram(importPath string, symbols []string) ([]byte, error) {
var program bytes.Buffer
data := reflectData{
ImportPath: importPath,
Symbols: symbols,
}
if err := reflectProgram.Execute(&program, &data); err != nil {
return nil, err
}
return program.Bytes(), nil
}
// run the given program and parse the output as a model.Package.
func run(program string) (*model.Package, error) {
f, err := os.CreateTemp("", "")
if err != nil {
return nil, err
}
filename := f.Name()
defer os.Remove(filename)
if err := f.Close(); err != nil {
return nil, err
}
// Run the program.
cmd := exec.Command(program, "-output", filename)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return nil, err
}
f, err = os.Open(filename)
if err != nil {
return nil, err
}
// Process output.
var pkg model.Package
if err := gob.NewDecoder(f).Decode(&pkg); err != nil {
return nil, err
}
if err := f.Close(); err != nil {
return nil, err
}
return &pkg, nil
}
// runInDir writes the given program into the given dir, runs it there, and
// parses the output as a model.Package.
func runInDir(program []byte, dir string) (*model.Package, error) {
// We use TempDir instead of TempFile so we can control the filename.
tmpDir, err := os.MkdirTemp(dir, "gomock_reflect_")
if err != nil {
return nil, err
}
defer func() {
if err := os.RemoveAll(tmpDir); err != nil {
log.Printf("failed to remove temp directory: %s", err)
}
}()
const progSource = "prog.go"
var progBinary = "prog.bin"
if runtime.GOOS == "windows" {
// Windows won't execute a program unless it has a ".exe" suffix.
progBinary += ".exe"
}
if err := os.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil {
return nil, err
}
cmdArgs := []string{}
cmdArgs = append(cmdArgs, "build")
if *buildFlags != "" {
cmdArgs = append(cmdArgs, strings.Split(*buildFlags, " ")...)
}
cmdArgs = append(cmdArgs, "-o", progBinary, progSource)
// Build the program.
buf := bytes.NewBuffer(nil)
cmd := exec.Command("go", cmdArgs...)
cmd.Dir = tmpDir
cmd.Stdout = os.Stdout
cmd.Stderr = io.MultiWriter(os.Stderr, buf)
if err := cmd.Run(); err != nil {
sErr := buf.String()
if strings.Contains(sErr, `cannot find package "."`) &&
strings.Contains(sErr, "go.uber.org/mock/mockgen/model") {
fmt.Fprint(os.Stderr, "Please reference the steps in the README to fix this error:\n\thttps://go.uber.org/mock#reflect-vendoring-error.\n")
return nil, err
}
return nil, err
}
return run(filepath.Join(tmpDir, progBinary))
}
type reflectData struct {
ImportPath string
Symbols []string
}
// This program reflects on an interface value, and prints the
// gob encoding of a model.Package to standard output.
// JSON doesn't work because of the model.Type interface.
var reflectProgram = template.Must(template.New("program").Parse(`
// Code generated by MockGen. DO NOT EDIT.
package main
import (
"encoding/gob"
"flag"
"fmt"
"os"
"path"
"reflect"
"go.uber.org/mock/mockgen/model"
pkg_ {{printf "%q" .ImportPath}}
)
var output = flag.String("output", "", "The output file name, or empty to use stdout.")
func main() {
flag.Parse()
its := []struct{
sym string
typ reflect.Type
}{
{{range .Symbols}}
{ {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()},
{{end}}
}
pkg := &model.Package{
// NOTE: This behaves contrary to documented behaviour if the
// package name is not the final component of the import path.
// The reflect package doesn't expose the package name, though.
Name: path.Base({{printf "%q" .ImportPath}}),
}
for _, it := range its {
intf, err := model.InterfaceFromInterfaceType(it.typ)
if err != nil {
fmt.Fprintf(os.Stderr, "Reflection: %v\n", err)
os.Exit(1)
}
intf.Name = it.sym
pkg.Interfaces = append(pkg.Interfaces, intf)
}
outfile := os.Stdout
if len(*output) != 0 {
var err error
outfile, err = os.Create(*output)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to open output file %q", *output)
}
defer func() {
if err := outfile.Close(); err != nil {
fmt.Fprintf(os.Stderr, "failed to close output file %q", *output)
os.Exit(1)
}
}()
}
if err := gob.NewEncoder(outfile).Encode(pkg); err != nil {
fmt.Fprintf(os.Stderr, "gob encode: %v\n", err)
os.Exit(1)
}
}
`))