From e4954a2411500baf406d5716d03d26a23a5576c0 Mon Sep 17 00:00:00 2001 From: Louis Brunner Date: Wed, 22 Jun 2022 19:34:46 +0100 Subject: [PATCH] fix: add support for with-expecter when using generics --- pkg/generator.go | 54 ++++++++----- pkg/generator_test.go | 184 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+), 22 deletions(-) diff --git a/pkg/generator.go b/pkg/generator.go index cafbc1fc2..25e03bf31 100644 --- a/pkg/generator.go +++ b/pkg/generator.go @@ -612,7 +612,7 @@ func (g *Generator) Generate(ctx context.Context) error { ) if g.WithExpecter { - g.generateExpecterStruct() + g.generateExpecterStruct(ctx) } for _, method := range g.iface.Methods() { @@ -704,7 +704,7 @@ func (g *Generator) Generate(ctx context.Context) error { // Construct expecter helper functions if g.WithExpecter { - g.generateExpecterMethodCall(method, params, returns) + g.generateExpecterMethodCall(ctx, method, params, returns) } } @@ -713,23 +713,29 @@ func (g *Generator) Generate(ctx context.Context) error { return nil } -func (g *Generator) generateExpecterStruct() { - data := struct{ MockName, ExpecterName string }{ - MockName: g.mockName(), - ExpecterName: g.expecterName(), +func (g *Generator) generateExpecterStruct(ctx context.Context) { + data := struct { + MockName, ExpecterName string + InstantiatedTypeString string + TypeConstraint string + }{ + MockName: g.mockName(), + ExpecterName: g.expecterName(), + InstantiatedTypeString: g.getInstantiatedTypeString(), + TypeConstraint: g.getTypeConstraintString(ctx), } g.printTemplate(data, ` -type {{.ExpecterName}} struct { +type {{.ExpecterName}}{{ .TypeConstraint }} struct { mock *mock.Mock } -func (_m *{{.MockName}}) EXPECT() *{{.ExpecterName}} { - return &{{.ExpecterName}}{mock: &_m.Mock} +func (_m *{{.MockName}}{{ .InstantiatedTypeString }}) EXPECT() *{{.ExpecterName}}{{ .InstantiatedTypeString }} { + return &{{.ExpecterName}}{{ .InstantiatedTypeString }}{mock: &_m.Mock} } `) } -func (g *Generator) generateExpecterMethodCall(method *Method, params, returns *paramList) { +func (g *Generator) generateExpecterMethodCall(ctx context.Context, method *Method, params, returns *paramList) { data := struct { MockName, ExpecterName string @@ -739,13 +745,17 @@ func (g *Generator) generateExpecterMethodCall(method *Method, params, returns * LastParamName string LastParamType string NbNonVariadic int + InstantiatedTypeString string + TypeConstraint string }{ - MockName: g.mockName(), - ExpecterName: g.expecterName(), - CallStruct: fmt.Sprintf("%s_%s_Call", g.mockName(), method.Name), - MethodName: method.Name, - Params: params, - Returns: returns, + MockName: g.mockName(), + ExpecterName: g.expecterName(), + CallStruct: fmt.Sprintf("%s_%s_Call", g.mockName(), method.Name), + MethodName: method.Name, + Params: params, + Returns: returns, + InstantiatedTypeString: g.getInstantiatedTypeString(), + TypeConstraint: g.getTypeConstraintString(ctx), } // Get some info about parameters for variadic methods, way easier than doing it in golang template directly @@ -757,16 +767,16 @@ func (g *Generator) generateExpecterMethodCall(method *Method, params, returns * g.printTemplate(data, ` // {{.CallStruct}} is a *mock.Call that shadows Run/Return methods with type explicit version for method '{{.MethodName}}' -type {{.CallStruct}} struct { +type {{.CallStruct}}{{ .TypeConstraint }} struct { *mock.Call } // {{.MethodName}} is a helper method to define mock.On call {{- range .Params.Params}} -// - {{.}} +// - {{.}} {{- end}} -func (_e *{{.ExpecterName}}) {{.MethodName}}({{range .Params.ParamsIntf}}{{.}},{{end}}) *{{.CallStruct}} { - return &{{.CallStruct}}{Call: _e.mock.On("{{.MethodName}}", +func (_e *{{.ExpecterName}}{{ .InstantiatedTypeString }}) {{.MethodName}}({{range .Params.ParamsIntf}}{{.}},{{end}}) *{{.CallStruct}}{{ .InstantiatedTypeString }} { + return &{{.CallStruct}}{{ .InstantiatedTypeString }}{Call: _e.mock.On("{{.MethodName}}", {{- if not .Params.Variadic }} {{- range .Params.Names}}{{.}},{{end}} {{- else }} @@ -779,7 +789,7 @@ func (_e *{{.ExpecterName}}) {{.MethodName}}({{range .Params.ParamsIntf}}{{.}},{ {{- end }} )} } -func (_c *{{.CallStruct}}) Run(run func({{range .Params.Params}}{{.}},{{end}})) *{{.CallStruct}} { +func (_c *{{.CallStruct}}{{ .InstantiatedTypeString }}) Run(run func({{range .Params.Params}}{{.}},{{end}})) *{{.CallStruct}}{{ .InstantiatedTypeString }} { _c.Call.Run(func(args mock.Arguments) { {{- if not .Params.Variadic }} run({{range $i, $type := .Params.Types }}args[{{$i}}].({{$type}}),{{end}}) @@ -801,7 +811,7 @@ func (_c *{{.CallStruct}}) Run(run func({{range .Params.Params}}{{.}},{{end}})) return _c } -func (_c *{{.CallStruct}}) Return({{range .Returns.Params}}{{.}},{{end}}) *{{.CallStruct}} { +func (_c *{{.CallStruct}}{{ .InstantiatedTypeString }}) Return({{range .Returns.Params}}{{.}},{{end}}) *{{.CallStruct}}{{ .InstantiatedTypeString }} { _c.Call.Return({{range .Returns.Names}}{{.}},{{end}}) return _c } diff --git a/pkg/generator_test.go b/pkg/generator_test.go index 98313d1c6..d64f8dc27 100644 --- a/pkg/generator_test.go +++ b/pkg/generator_test.go @@ -2346,6 +2346,190 @@ func NewRequesterGenerics[TAny interface{}, TComparable comparable, TSigned cons s.checkGeneration("generic.go", "RequesterGenerics", false, "", expected) } +func (s *GeneratorSuite) TestGenericExpecterGenerator() { + expected := `// RequesterGenerics is an autogenerated mock type for the RequesterGenerics type +type RequesterGenerics[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { + ~int | test.GenericType[int, test.GetInt] + comparable +}] struct { + mock.Mock +} + +type RequesterGenerics_Expecter[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { + ~int | test.GenericType[int, test.GetInt] + comparable +}] struct { + mock *mock.Mock +} + +func (_m *RequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) EXPECT() *RequesterGenerics_Expecter[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + return &RequesterGenerics_Expecter[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]{mock: &_m.Mock} +} + +// GenericAnonymousStructs provides a mock function with given fields: _a0 +func (_m *RequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericAnonymousStructs(_a0 struct{ Type1 TExternalIntf }) struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] +} { + ret := _m.Called(_a0) + + var r0 struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] + } + if rf, ok := ret.Get(0).(func(struct{ Type1 TExternalIntf }) struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] + }); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] + }) + } + + return r0 +} + +// RequesterGenerics_GenericAnonymousStructs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenericAnonymousStructs' +type RequesterGenerics_GenericAnonymousStructs_Call[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { + ~int | test.GenericType[int, test.GetInt] + comparable +}] struct { + *mock.Call +} + +// GenericAnonymousStructs is a helper method to define mock.On call +// - _a0 struct{Type1 TExternalIntf} +func (_e *RequesterGenerics_Expecter[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericAnonymousStructs(_a0 interface{}) *RequesterGenerics_GenericAnonymousStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + return &RequesterGenerics_GenericAnonymousStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]{Call: _e.mock.On("GenericAnonymousStructs", _a0)} +} + +func (_c *RequesterGenerics_GenericAnonymousStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) Run(run func(_a0 struct{ Type1 TExternalIntf })) *RequesterGenerics_GenericAnonymousStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(struct{ Type1 TExternalIntf })) + }) + return _c +} + +func (_c *RequesterGenerics_GenericAnonymousStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) Return(_a0 struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] +}) *RequesterGenerics_GenericAnonymousStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + _c.Call.Return(_a0) + return _c +} + +// GenericArguments provides a mock function with given fields: _a0, _a1 +func (_m *RequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericArguments(_a0 TAny, _a1 TComparable) (TSigned, TIntf) { + ret := _m.Called(_a0, _a1) + + var r0 TSigned + if rf, ok := ret.Get(0).(func(TAny, TComparable) TSigned); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Get(0).(TSigned) + } + + var r1 TIntf + if rf, ok := ret.Get(1).(func(TAny, TComparable) TIntf); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Get(1).(TIntf) + } + + return r0, r1 +} + +// RequesterGenerics_GenericArguments_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenericArguments' +type RequesterGenerics_GenericArguments_Call[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { + ~int | test.GenericType[int, test.GetInt] + comparable +}] struct { + *mock.Call +} + +// GenericArguments is a helper method to define mock.On call +// - _a0 TAny +// - _a1 TComparable +func (_e *RequesterGenerics_Expecter[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericArguments(_a0 interface{}, _a1 interface{}) *RequesterGenerics_GenericArguments_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + return &RequesterGenerics_GenericArguments_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]{Call: _e.mock.On("GenericArguments", _a0, _a1)} +} + +func (_c *RequesterGenerics_GenericArguments_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) Run(run func(_a0 TAny, _a1 TComparable)) *RequesterGenerics_GenericArguments_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(TAny), args[1].(TComparable)) + }) + return _c +} + +func (_c *RequesterGenerics_GenericArguments_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) Return(_a0 TSigned, _a1 TIntf) *RequesterGenerics_GenericArguments_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + _c.Call.Return(_a0, _a1) + return _c +} + +// GenericStructs provides a mock function with given fields: _a0 +func (_m *RequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericStructs(_a0 test.GenericType[TAny, TIntf]) test.GenericType[TSigned, TIntf] { + ret := _m.Called(_a0) + + var r0 test.GenericType[TSigned, TIntf] + if rf, ok := ret.Get(0).(func(test.GenericType[TAny, TIntf]) test.GenericType[TSigned, TIntf]); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(test.GenericType[TSigned, TIntf]) + } + + return r0 +} + +// RequesterGenerics_GenericStructs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenericStructs' +type RequesterGenerics_GenericStructs_Call[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { + ~int | test.GenericType[int, test.GetInt] + comparable +}] struct { + *mock.Call +} + +// GenericStructs is a helper method to define mock.On call +// - _a0 test.GenericType[TAny,TIntf] +func (_e *RequesterGenerics_Expecter[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericStructs(_a0 interface{}) *RequesterGenerics_GenericStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + return &RequesterGenerics_GenericStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]{Call: _e.mock.On("GenericStructs", _a0)} +} + +func (_c *RequesterGenerics_GenericStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) Run(run func(_a0 test.GenericType[TAny, TIntf])) *RequesterGenerics_GenericStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(test.GenericType[TAny, TIntf])) + }) + return _c +} + +func (_c *RequesterGenerics_GenericStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) Return(_a0 test.GenericType[TSigned, TIntf]) *RequesterGenerics_GenericStructs_Call[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + _c.Call.Return(_a0) + return _c +} + +type mockConstructorTestingTNewRequesterGenerics interface { + mock.TestingT + Cleanup(func()) +} + +// NewRequesterGenerics creates a new instance of RequesterGenerics. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewRequesterGenerics[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { + ~int | test.GenericType[int, test.GetInt] + comparable +}](t mockConstructorTestingTNewRequesterGenerics) *RequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric] { + mock := &RequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} +` + cfg := config.Config{ + StructName: "RequesterGenerics", + WithExpecter: true, + UnrollVariadic: true, + } + s.checkGenerationWithConfig("generic.go", "RequesterGenerics", cfg, expected) +} + func (s *GeneratorSuite) TestGenericInpkgGenerator() { expected := `// MockRequesterGenerics is an autogenerated mock type for the RequesterGenerics type type MockRequesterGenerics[TAny interface{}, TComparable comparable, TSigned constraints.Signed, TIntf GetInt, TExternalIntf io.Writer, TGenIntf GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface {