Skip to content

Commit

Permalink
Merge pull request #484 from LouisBrunner/fix_generics_with_expecter
Browse files Browse the repository at this point in the history
fix: add support for with-expecter when using generics
  • Loading branch information
LandonTClipp committed Jun 30, 2022
2 parents de0cade + e4954a2 commit 4d1f925
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 22 deletions.
54 changes: 32 additions & 22 deletions pkg/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ func (g *Generator) Generate(ctx context.Context) error {
)

if g.WithExpecter {
g.generateExpecterStruct()
g.generateExpecterStruct(ctx)
}

for _, method := range g.iface.Methods() {
Expand Down Expand Up @@ -705,7 +705,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)
}
}

Expand All @@ -714,23 +714,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
Expand All @@ -740,13 +746,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
Expand All @@ -758,16 +768,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 }}
Expand All @@ -780,7 +790,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}})
Expand All @@ -802,7 +812,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
}
Expand Down
184 changes: 184 additions & 0 deletions pkg/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 4d1f925

Please sign in to comment.