diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 551f3ce..6808fbd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - main pull_request: jobs: @@ -13,7 +14,7 @@ jobs: runs-on: ubuntu-latest env: GO_VERSION: stable - GOLANGCI_LINT_VERSION: v1.62.0 + GOLANGCI_LINT_VERSION: v2.3.0 CGO_ENABLED: 0 steps: diff --git a/.github/workflows/go-cross.yml b/.github/workflows/go-cross.yml index 0ea2c11..385f167 100644 --- a/.github/workflows/go-cross.yml +++ b/.github/workflows/go-cross.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - main pull_request: jobs: diff --git a/.golangci.yml b/.golangci.yml index 4bdaa01..e95ffb9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,65 +1,65 @@ +version: "2" + run: timeout: 10m -linters-settings: - govet: - enable-all: true - disable: - - fieldalignment - gocyclo: - min-complexity: 15 - goconst: - min-len: 5 - min-occurrences: 3 - misspell: - locale: US - funlen: - lines: -1 - statements: 50 - godox: - keywords: - - FIXME - gofumpt: - extra-rules: true - depguard: - rules: - main: - deny: - - pkg: "github.com/instana/testify" - desc: not allowed - - pkg: "github.com/pkg/errors" - desc: Should be replaced by standard lib errors package - gocritic: - enabled-tags: - - diagnostic - - style - - performance - disabled-checks: - - sloppyReassign - - rangeValCopy - - octalLiteral - - paramTypeCombine # already handle by gofumpt.extra-rules - - unnamedResult - - hugeParam - tagliatelle: - case: +linters: + settings: + govet: + enable-all: true + disable: + - fieldalignment + gocyclo: + min-complexity: 15 + goconst: + min-len: 5 + min-occurrences: 3 + misspell: + locale: US + funlen: + lines: -1 + statements: 50 + godox: + keywords: + - FIXME + depguard: rules: - json: pascal - gosec: - excludes: - - G304 - - G306 + main: + deny: + - pkg: "github.com/instana/testify" + desc: not allowed + - pkg: "github.com/pkg/errors" + desc: Should be replaced by standard lib errors package + gocritic: + enabled-tags: + - diagnostic + - style + - performance + disabled-checks: + - sloppyReassign + - rangeValCopy + - octalLiteral + - paramTypeCombine # already handle by gofumpt.extra-rules + - unnamedResult + - hugeParam + tagliatelle: + case: + rules: + json: pascal + gosec: + excludes: + - G304 + - G306 -linters: - enable-all: true + default: all disable: - - exportloopref # deprecated - sqlclosecheck # not relevant (SQL) - rowserrcheck # not relevant (SQL) - cyclop # duplicate of gocyclo - lll - dupl - wsl + - wsl_v5 - nlreturn - mnd - err113 @@ -78,17 +78,25 @@ linters: - errchkjson - contextcheck + exclusions: + warn-unused: true + rules: + - text: fmt.Sprintf can be replaced with string + linters: + - perfsprint + issues: - exclude-use-default: false max-issues-per-linter: 0 max-same-issues: 0 - exclude: - - 'fmt.Sprintf can be replaced with string' - exclude-rules: - - path: .*_test.go - linters: - - funlen - - noctx + +formatters: + enable: + - gci + - gofumpt + + settings: + gofumpt: + extra-rules: true output: show-stats: true diff --git a/.goreleaser.yml b/.goreleaser.yml index 227a18b..53f8b52 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -44,17 +44,4 @@ archives: files: - LICENSE -brews: - - repository: - owner: traefik - name: homebrew-tap - commit_author: - name: traefiker - email: 30906710+traefiker@users.noreply.github.com - directory: Formula - homepage: https://github.com/traefik/mocktail - description: | - Naive code generator that creates mock implementation using testify.mock. - test: | - system "echo 0" diff --git a/LICENSE b/LICENSE index aa6b63a..bae8fac 100644 --- a/LICENSE +++ b/LICENSE @@ -186,6 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. + Copyright [2025] [PaperBalls Authors] Copyright [2022] [Traefik Labs] Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/Makefile b/Makefile index 97b7323..a5bc39f 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ -.PHONY: clean lint test build +.PHONY: default clean lint test build default: clean lint test build lint: - golangci-lint run + go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.3.0 run clean: rm -rf cover.out diff --git a/go.mod b/go.mod index 9e66051..86ed42f 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,17 @@ -module github.com/traefik/mocktail +module github.com/paperballs/mocktail -go 1.23.0 +go 1.24.0 require ( github.com/ettle/strcase v0.2.0 - github.com/stretchr/testify v1.9.0 - golang.org/x/tools v0.32.0 + github.com/stretchr/testify v1.10.0 + golang.org/x/tools v0.35.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.13.0 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/sync v0.16.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1881a13..4fe7997 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,14 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/mocktail.go b/mocktail.go index 8370945..8f3bc04 100644 --- a/mocktail.go +++ b/mocktail.go @@ -4,6 +4,7 @@ package main import ( "bufio" "bytes" + "context" "flag" "fmt" "go/format" @@ -37,12 +38,15 @@ type PackageDesc struct { // InterfaceDesc represent an interface. type InterfaceDesc struct { - Name string - Methods []*types.Func + Name string + Methods []*types.Func + TypeParams *types.TypeParamList // Generic type parameters } func main() { - info, err := getModuleInfo(os.Getenv("MOCKTAIL_TEST_PATH")) + ctx := context.Background() + + info, err := getModuleInfo(ctx, os.Getenv("MOCKTAIL_TEST_PATH")) if err != nil { log.Fatal("get module path", err) } @@ -153,6 +157,11 @@ func walk(root, moduleName string) (map[string]PackageDesc, error) { interfaceDesc := InterfaceDesc{Name: interfaceName} + // Check if this is a generic interface + if namedType, ok := lookup.Type().(*types.Named); ok { + interfaceDesc.TypeParams = namedType.TypeParams() + } + interfaceType, ok := lookup.Type().Underlying().(*types.Interface) if !ok { return fmt.Errorf("type %q in %q is not an interface", lookup.Type(), fp) @@ -252,6 +261,9 @@ func getTypeImports(t types.Type) []string { case *types.Chan: return []string{""} + case *types.TypeParam: + return []string{""} + default: panic(fmt.Sprintf("OOPS %[1]T %[1]s", t)) } @@ -267,7 +279,7 @@ func generate(model map[string]PackageDesc, exported bool) error { } for _, interfaceDesc := range pkgDesc.Interfaces { - err = writeMockBase(buffer, interfaceDesc.Name, exported) + err = writeMockBase(buffer, interfaceDesc, exported) if err != nil { return err } @@ -282,6 +294,7 @@ func generate(model map[string]PackageDesc, exported bool) error { InterfaceName: interfaceDesc.Name, Method: method, Signature: signature, + TypeParams: interfaceDesc.TypeParams, } err = syrup.MockMethod(buffer) diff --git a/mocktail_test.go b/mocktail_test.go index c6968bc..b99445f 100644 --- a/mocktail_test.go +++ b/mocktail_test.go @@ -29,7 +29,7 @@ func TestMocktail(t *testing.T) { t.Setenv("MOCKTAIL_TEST_PATH", filepath.Join(testRoot, entry.Name())) - output, err := exec.Command("go", "run", ".").CombinedOutput() + output, err := exec.CommandContext(t.Context(), "go", "run", ".").CombinedOutput() t.Log(string(output)) require.NoError(t, err) @@ -61,7 +61,7 @@ func TestMocktail(t *testing.T) { continue } - cmd := exec.Command("go", "test", "-v", "./...") + cmd := exec.CommandContext(t.Context(), "go", "test", "-v", "./...") cmd.Dir = filepath.Join(testRoot, entry.Name()) output, err := cmd.CombinedOutput() @@ -88,7 +88,7 @@ func TestMocktail_exported(t *testing.T) { t.Setenv("MOCKTAIL_TEST_PATH", filepath.Join(testRoot, entry.Name())) - output, err := exec.Command("go", "run", ".", "-e").CombinedOutput() + output, err := exec.CommandContext(t.Context(), "go", "run", ".", "-e").CombinedOutput() t.Log(string(output)) require.NoError(t, err) @@ -120,7 +120,7 @@ func TestMocktail_exported(t *testing.T) { continue } - cmd := exec.Command("go", "test", "-v", "./...") + cmd := exec.CommandContext(t.Context(), "go", "test", "-v", "./...") cmd.Dir = filepath.Join(testRoot, entry.Name()) output, err := cmd.CombinedOutput() diff --git a/mod.go b/mod.go index 11f58b3..e642f5c 100644 --- a/mod.go +++ b/mod.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -16,9 +17,9 @@ type modInfo struct { Main bool `json:"Main"` } -func getModuleInfo(dir string) (modInfo, error) { +func getModuleInfo(ctx context.Context, dir string) (modInfo, error) { // https://github.com/golang/go/issues/44753#issuecomment-790089020 - cmd := exec.Command("go", "list", "-m", "-json") + cmd := exec.CommandContext(ctx, "go", "list", "-m", "-json") if dir != "" { cmd.Dir = dir } diff --git a/readme.md b/readme.md index 4ebe006..f95f529 100644 --- a/readme.md +++ b/readme.md @@ -40,7 +40,7 @@ go install github.com/traefik/mocktail@latest You can use pre-compiled binaries: -* To get the binary just download the latest release for your OS/Arch from [the releases page](https://github.com/traefik/mocktail/releases) +* To get the binary just download the latest release for your OS/Arch from [the releases page](https://github.com/paperballs/mocktail/releases) * Unzip the archive. * Add `mocktail` in your `PATH`. diff --git a/syrup.go b/syrup.go index a070135..8c5153b 100644 --- a/syrup.go +++ b/syrup.go @@ -25,13 +25,13 @@ package {{ .Name }} templateMockBase = ` // {{ .InterfaceName | ToGoCamel }}Mock mock of {{ .InterfaceName }}. -type {{ .InterfaceName | ToGoCamel }}Mock struct { mock.Mock } +type {{ .InterfaceName | ToGoCamel }}Mock{{ .TypeParamsDecl }} struct { mock.Mock } // {{.ConstructorPrefix}}{{ .InterfaceName | ToGoPascal }}Mock creates a new {{ .InterfaceName | ToGoCamel }}Mock. -func {{.ConstructorPrefix}}{{ .InterfaceName | ToGoPascal }}Mock(tb testing.TB) *{{ .InterfaceName | ToGoCamel }}Mock { +func {{.ConstructorPrefix}}{{ .InterfaceName | ToGoPascal }}Mock{{ .TypeParamsDecl }}(tb testing.TB) *{{ .InterfaceName | ToGoCamel }}Mock{{ .TypeParamsUse }} { tb.Helper() - m := &{{ .InterfaceName | ToGoCamel }}Mock{} + m := &{{ .InterfaceName | ToGoCamel }}Mock{{ .TypeParamsUse }}{} m.Mock.Test(tb) tb.Cleanup(func() { m.AssertExpectations(tb) }) @@ -41,48 +41,48 @@ func {{.ConstructorPrefix}}{{ .InterfaceName | ToGoPascal }}Mock(tb testing.TB) ` templateCallBase = ` -type {{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call struct{ +type {{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsDecl }} struct{ *mock.Call - Parent *{{ .InterfaceName | ToGoCamel }}Mock + Parent *{{ .InterfaceName | ToGoCamel }}Mock{{ .TypeParamsUse }} } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) Panic(msg string) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) Panic(msg string) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.Panic(msg) return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) Once() *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) Once() *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.Once() return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) Twice() *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) Twice() *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.Twice() return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) Times(i int) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) Times(i int) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.Times(i) return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) WaitUntil(w <-chan time.Time) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) WaitUntil(w <-chan time.Time) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.WaitUntil(w) return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) After(d time.Duration) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) After(d time.Duration) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.After(d) return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) Run(fn func(args mock.Arguments)) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) Run(fn func(args mock.Arguments)) *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.Run(fn) return _c } -func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call) Maybe() *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call { +func (_c *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }}) Maybe() *{{ .InterfaceName | ToGoCamel }}{{ .MethodName }}Call{{ .TypeParamsUse }} { _c.Call = _c.Call.Maybe() return _c } @@ -96,6 +96,51 @@ type Syrup struct { InterfaceName string Method *types.Func Signature *types.Signature + TypeParams *types.TypeParamList +} + +// getTypeParamsUse returns type parameters for usage in method receivers. +func (s Syrup) getTypeParamsUse() string { + if s.TypeParams == nil || s.TypeParams.Len() == 0 { + return "" + } + + var names []string + for i := range s.TypeParams.Len() { + tp := s.TypeParams.At(i) + names = append(names, tp.Obj().Name()) + } + return "[" + strings.Join(names, ", ") + "]" +} + +// Call generates mock.Call wrapper. +func (s Syrup) Call(writer io.Writer, methods []*types.Func) error { + err := s.callBase(writer) + if err != nil { + return err + } + + err = s.typedReturns(writer) + if err != nil { + return err + } + + err = s.returnsFn(writer) + if err != nil { + return err + } + + err = s.typedRun(writer) + if err != nil { + return err + } + + err = s.callMethodsOn(writer, methods) + if err != nil { + return err + } + + return s.callMethodOnRaw(writer, methods) } // MockMethod generates method mocks. @@ -116,7 +161,8 @@ func (s Syrup) MockMethod(writer io.Writer) error { func (s Syrup) mockedMethod(writer io.Writer) error { w := &Writer{writer: writer} - w.Printf("func (_m *%sMock) %s(", strcase.ToGoCamel(s.InterfaceName), s.Method.Name()) + typeParamsUse := s.getTypeParamsUse() + w.Printf("func (_m *%sMock%s) %s(", strcase.ToGoCamel(s.InterfaceName), typeParamsUse, s.Method.Name()) params := s.Signature.Params() @@ -225,7 +271,8 @@ func (s Syrup) methodOn(writer io.Writer) error { structBaseName := strcase.ToGoCamel(s.InterfaceName) - w.Printf("func (_m *%sMock) On%s(", structBaseName, s.Method.Name()) + typeParamsUse := s.getTypeParamsUse() + w.Printf("func (_m *%sMock%s) On%s(", structBaseName, typeParamsUse, s.Method.Name()) params := s.Signature.Params() @@ -254,10 +301,10 @@ func (s Syrup) methodOn(writer io.Writer) error { } } - w.Printf(") *%s%sCall {\n", structBaseName, s.Method.Name()) + w.Printf(") *%s%sCall%s {\n", structBaseName, s.Method.Name(), typeParamsUse) - w.Printf(` return &%s%sCall{Call: _m.Mock.On("%s", %s), Parent: _m}`, - structBaseName, s.Method.Name(), s.Method.Name(), strings.Join(argNames, ", ")) + w.Printf(` return &%s%sCall%s{Call: _m.Mock.On("%s", %s), Parent: _m}`, + structBaseName, s.Method.Name(), typeParamsUse, s.Method.Name(), strings.Join(argNames, ", ")) w.Println() w.Println("}") @@ -271,7 +318,8 @@ func (s Syrup) methodOnRaw(writer io.Writer) error { structBaseName := strcase.ToGoCamel(s.InterfaceName) - w.Printf("func (_m *%sMock) On%sRaw(", structBaseName, s.Method.Name()) + typeParamsUse := s.getTypeParamsUse() + w.Printf("func (_m *%sMock%s) On%sRaw(", structBaseName, typeParamsUse, s.Method.Name()) params := s.Signature.Params() @@ -300,10 +348,10 @@ func (s Syrup) methodOnRaw(writer io.Writer) error { } } - w.Printf(") *%s%sCall {\n", structBaseName, s.Method.Name()) + w.Printf(") *%s%sCall%s {\n", structBaseName, s.Method.Name(), typeParamsUse) - w.Printf(` return &%s%sCall{Call: _m.Mock.On("%s", %s), Parent: _m}`, - structBaseName, s.Method.Name(), s.Method.Name(), strings.Join(argNames, ", ")) + w.Printf(` return &%s%sCall%s{Call: _m.Mock.On("%s", %s), Parent: _m}`, + structBaseName, s.Method.Name(), typeParamsUse, s.Method.Name(), strings.Join(argNames, ", ")) w.Println() w.Println("}") @@ -312,36 +360,6 @@ func (s Syrup) methodOnRaw(writer io.Writer) error { return w.Err() } -// Call generates mock.Call wrapper. -func (s Syrup) Call(writer io.Writer, methods []*types.Func) error { - err := s.callBase(writer) - if err != nil { - return err - } - - err = s.typedReturns(writer) - if err != nil { - return err - } - - err = s.returnsFn(writer) - if err != nil { - return err - } - - err = s.typedRun(writer) - if err != nil { - return err - } - - err = s.callMethodsOn(writer, methods) - if err != nil { - return err - } - - return s.callMethodOnRaw(writer, methods) -} - func (s Syrup) callBase(writer io.Writer) error { base := template.New("templateCallBase").Funcs(template.FuncMap{ "ToGoCamel": strcase.ToGoCamel, @@ -353,9 +371,26 @@ func (s Syrup) callBase(writer io.Writer) error { return err } + // Generate type parameter declarations and usage + typeParamsDecl := "" + typeParamsUse := "" + if s.TypeParams != nil && s.TypeParams.Len() > 0 { + var params []string + var names []string + for i := range s.TypeParams.Len() { + tp := s.TypeParams.At(i) + params = append(params, tp.Obj().Name()+" "+tp.Constraint().String()) + names = append(names, tp.Obj().Name()) + } + typeParamsDecl = "[" + strings.Join(params, ", ") + "]" + typeParamsUse = "[" + strings.Join(names, ", ") + "]" + } + data := map[string]string{ - "InterfaceName": s.InterfaceName, - "MethodName": s.Method.Name(), + "InterfaceName": s.InterfaceName, + "MethodName": s.Method.Name(), + "TypeParamsDecl": typeParamsDecl, + "TypeParamsUse": typeParamsUse, } return tmpl.Execute(writer, data) @@ -371,7 +406,8 @@ func (s Syrup) typedReturns(writer io.Writer) error { structBaseName := strcase.ToGoCamel(s.InterfaceName) - w.Printf("func (_c *%s%sCall) TypedReturns(", structBaseName, s.Method.Name()) + typeParamsUse := s.getTypeParamsUse() + w.Printf("func (_c *%s%sCall%s) TypedReturns(", structBaseName, s.Method.Name(), typeParamsUse) var returnNames string for i := range results.Len() { @@ -386,7 +422,7 @@ func (s Syrup) typedReturns(writer io.Writer) error { } } - w.Printf(") *%s%sCall {\n", structBaseName, s.Method.Name()) + w.Printf(") *%s%sCall%s {\n", structBaseName, s.Method.Name(), typeParamsUse) w.Printf("\t_c.Call = _c.Return(%s)\n", returnNames) w.Println("\treturn _c") w.Println("}") @@ -402,8 +438,8 @@ func (s Syrup) typedRun(writer io.Writer) error { structBaseName := strcase.ToGoCamel(s.InterfaceName) - w.Printf("func (_c *%[1]s%[2]sCall) TypedRun(fn %[3]s) *%[1]s%[2]sCall {\n", - structBaseName, s.Method.Name(), s.createFuncSignature(params, nil)) + w.Printf("func (_c *%[1]s%[2]sCall%[4]s) TypedRun(fn %[3]s) *%[1]s%[2]sCall%[4]s {\n", + structBaseName, s.Method.Name(), s.createFuncSignature(params, nil), s.getTypeParamsUse()) w.Println("\t_c.Call = _c.Call.Run(func(args mock.Arguments) {") var pos int @@ -456,8 +492,8 @@ func (s Syrup) returnsFn(writer io.Writer) error { structBaseName := strcase.ToGoCamel(s.InterfaceName) - w.Printf("func (_c *%[1]s%[2]sCall) ReturnsFn(fn %[3]s) *%[1]s%[2]sCall {\n", - structBaseName, s.Method.Name(), s.createFuncSignature(params, results)) + w.Printf("func (_c *%[1]s%[2]sCall%[4]s) ReturnsFn(fn %[3]s) *%[1]s%[2]sCall%[4]s {\n", + structBaseName, s.Method.Name(), s.createFuncSignature(params, results), s.getTypeParamsUse()) w.Println("\t_c.Call = _c.Return(fn)") w.Println("\treturn _c") w.Println("}") @@ -469,7 +505,8 @@ func (s Syrup) returnsFn(writer io.Writer) error { func (s Syrup) callMethodsOn(writer io.Writer, methods []*types.Func) error { w := &Writer{writer: writer} - callType := fmt.Sprintf("%s%sCall", strcase.ToGoCamel(s.InterfaceName), s.Method.Name()) + typeParamsUse := s.getTypeParamsUse() + callType := fmt.Sprintf("%s%sCall%s", strcase.ToGoCamel(s.InterfaceName), s.Method.Name(), typeParamsUse) for _, method := range methods { sign := method.Type().(*types.Signature) @@ -498,7 +535,7 @@ func (s Syrup) callMethodsOn(writer io.Writer, methods []*types.Func) error { } } - w.Printf(") *%s%sCall {\n", strcase.ToGoCamel(s.InterfaceName), method.Name()) + w.Printf(") *%s%sCall%s {\n", strcase.ToGoCamel(s.InterfaceName), method.Name(), typeParamsUse) w.Printf("\treturn _c.Parent.On%s(%s", method.Name(), strings.Join(argNames, ", ")) if sign.Variadic() { @@ -515,7 +552,8 @@ func (s Syrup) callMethodsOn(writer io.Writer, methods []*types.Func) error { func (s Syrup) callMethodOnRaw(writer io.Writer, methods []*types.Func) error { w := &Writer{writer: writer} - callType := fmt.Sprintf("%s%sCall", strcase.ToGoCamel(s.InterfaceName), s.Method.Name()) + typeParamsUse := s.getTypeParamsUse() + callType := fmt.Sprintf("%s%sCall%s", strcase.ToGoCamel(s.InterfaceName), s.Method.Name(), typeParamsUse) for _, method := range methods { sign := method.Type().(*types.Signature) @@ -544,7 +582,7 @@ func (s Syrup) callMethodOnRaw(writer io.Writer, methods []*types.Func) error { } } - w.Printf(") *%s%sCall {\n", strcase.ToGoCamel(s.InterfaceName), method.Name()) + w.Printf(") *%s%sCall%s {\n", strcase.ToGoCamel(s.InterfaceName), method.Name(), typeParamsUse) w.Printf("\treturn _c.Parent.On%sRaw(%s)\n", method.Name(), strings.Join(argNames, ", ")) w.Println("}") @@ -596,6 +634,9 @@ func (s Syrup) getTypeName(t types.Type, last bool) string { case *types.Array: return fmt.Sprintf("[%d]%s", v.Len(), s.getTypeName(v.Elem(), false)) + case *types.TypeParam: + return v.Obj().Name() + default: panic(fmt.Sprintf("OOPS %[1]T %[1]s", t)) } @@ -689,7 +730,7 @@ func writeImports(writer io.Writer, descPkg PackageDesc) error { return tmpl.Execute(writer, data) } -func writeMockBase(writer io.Writer, interfaceName string, exported bool) error { +func writeMockBase(writer io.Writer, interfaceDesc InterfaceDesc, exported bool) error { base := template.New("templateMockBase").Funcs(template.FuncMap{ "ToGoCamel": strcase.ToGoCamel, "ToGoPascal": strcase.ToGoPascal, @@ -700,12 +741,33 @@ func writeMockBase(writer io.Writer, interfaceName string, exported bool) error constructorPrefix = "New" } + // Generate type parameter declarations and usage + typeParamsDecl := "" + typeParamsUse := "" + if interfaceDesc.TypeParams != nil && interfaceDesc.TypeParams.Len() > 0 { + var params []string + var names []string + for i := range interfaceDesc.TypeParams.Len() { + tp := interfaceDesc.TypeParams.At(i) + params = append(params, tp.Obj().Name()+" "+tp.Constraint().String()) + names = append(names, tp.Obj().Name()) + } + typeParamsDecl = "[" + strings.Join(params, ", ") + "]" + typeParamsUse = "[" + strings.Join(names, ", ") + "]" + } + tmpl, err := base.Parse(templateMockBase) if err != nil { return err } - return tmpl.Execute(writer, map[string]interface{}{"InterfaceName": interfaceName, "ConstructorPrefix": constructorPrefix}) + data := map[string]interface{}{ + "InterfaceName": interfaceDesc.Name, + "ConstructorPrefix": constructorPrefix, + "TypeParamsDecl": typeParamsDecl, + "TypeParamsUse": typeParamsUse, + } + return tmpl.Execute(writer, data) } func quickGoImports(descPkg PackageDesc) []string { diff --git a/testdata/src/a/a.go b/testdata/src/a/a.go index 6637fff..c5a9e40 100644 --- a/testdata/src/a/a.go +++ b/testdata/src/a/a.go @@ -43,3 +43,9 @@ type Strawberry interface { type Orange interface { Juice() <-chan struct{} } + +type Banana[T, U any] interface { + Tree(T) + Flower() U + Pudding() +} \ No newline at end of file diff --git a/testdata/src/a/mock_gen_test.go b/testdata/src/a/mock_gen_test.go index c477ba4..80c0da6 100644 --- a/testdata/src/a/mock_gen_test.go +++ b/testdata/src/a/mock_gen_test.go @@ -4115,3 +4115,301 @@ func (_c *cherryV2CarrotCall) OnV2Carrot() *cherryV2CarrotCall { func (_c *cherryV2CarrotCall) OnV2CarrotRaw() *cherryV2CarrotCall { return _c.Parent.OnV2CarrotRaw() } + +// bananaMock mock of Banana. +type bananaMock[T any, U any] struct{ mock.Mock } + +// newBananaMock creates a new bananaMock. +func newBananaMock[T any, U any](tb testing.TB) *bananaMock[T, U] { + tb.Helper() + + m := &bananaMock[T, U]{} + m.Mock.Test(tb) + + tb.Cleanup(func() { m.AssertExpectations(tb) }) + + return m +} + +func (_m *bananaMock[T, U]) Flower() U { + _ret := _m.Called() + + if _rf, ok := _ret.Get(0).(func() U); ok { + return _rf() + } + + _ra0, _ := _ret.Get(0).(U) + + return _ra0 +} + +func (_m *bananaMock[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return &bananaFlowerCall[T, U]{Call: _m.Mock.On("Flower"), Parent: _m} +} + +func (_m *bananaMock[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return &bananaFlowerCall[T, U]{Call: _m.Mock.On("Flower"), Parent: _m} +} + +type bananaFlowerCall[T any, U any] struct { + *mock.Call + Parent *bananaMock[T, U] +} + +func (_c *bananaFlowerCall[T, U]) Panic(msg string) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Panic(msg) + return _c +} + +func (_c *bananaFlowerCall[T, U]) Once() *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Once() + return _c +} + +func (_c *bananaFlowerCall[T, U]) Twice() *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Twice() + return _c +} + +func (_c *bananaFlowerCall[T, U]) Times(i int) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Times(i) + return _c +} + +func (_c *bananaFlowerCall[T, U]) WaitUntil(w <-chan time.Time) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.WaitUntil(w) + return _c +} + +func (_c *bananaFlowerCall[T, U]) After(d time.Duration) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.After(d) + return _c +} + +func (_c *bananaFlowerCall[T, U]) Run(fn func(args mock.Arguments)) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Run(fn) + return _c +} + +func (_c *bananaFlowerCall[T, U]) Maybe() *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Maybe() + return _c +} + +func (_c *bananaFlowerCall[T, U]) TypedReturns(a U) *bananaFlowerCall[T, U] { + _c.Call = _c.Return(a) + return _c +} + +func (_c *bananaFlowerCall[T, U]) ReturnsFn(fn func() U) *bananaFlowerCall[T, U] { + _c.Call = _c.Return(fn) + return _c +} + +func (_c *bananaFlowerCall[T, U]) TypedRun(fn func()) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Run(func(args mock.Arguments) { + fn() + }) + return _c +} + +func (_c *bananaFlowerCall[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlower() +} + +func (_c *bananaFlowerCall[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return _c.Parent.OnPudding() +} + +func (_c *bananaFlowerCall[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return _c.Parent.OnTree(aParam) +} + +func (_c *bananaFlowerCall[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlowerRaw() +} + +func (_c *bananaFlowerCall[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return _c.Parent.OnPuddingRaw() +} + +func (_c *bananaFlowerCall[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return _c.Parent.OnTreeRaw(aParam) +} + +func (_m *bananaMock[T, U]) Pudding() { + _m.Called() +} + +func (_m *bananaMock[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return &bananaPuddingCall[T, U]{Call: _m.Mock.On("Pudding"), Parent: _m} +} + +func (_m *bananaMock[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return &bananaPuddingCall[T, U]{Call: _m.Mock.On("Pudding"), Parent: _m} +} + +type bananaPuddingCall[T any, U any] struct { + *mock.Call + Parent *bananaMock[T, U] +} + +func (_c *bananaPuddingCall[T, U]) Panic(msg string) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Panic(msg) + return _c +} + +func (_c *bananaPuddingCall[T, U]) Once() *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Once() + return _c +} + +func (_c *bananaPuddingCall[T, U]) Twice() *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Twice() + return _c +} + +func (_c *bananaPuddingCall[T, U]) Times(i int) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Times(i) + return _c +} + +func (_c *bananaPuddingCall[T, U]) WaitUntil(w <-chan time.Time) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.WaitUntil(w) + return _c +} + +func (_c *bananaPuddingCall[T, U]) After(d time.Duration) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.After(d) + return _c +} + +func (_c *bananaPuddingCall[T, U]) Run(fn func(args mock.Arguments)) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Run(fn) + return _c +} + +func (_c *bananaPuddingCall[T, U]) Maybe() *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Maybe() + return _c +} + +func (_c *bananaPuddingCall[T, U]) TypedRun(fn func()) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Run(func(args mock.Arguments) { + fn() + }) + return _c +} + +func (_c *bananaPuddingCall[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlower() +} + +func (_c *bananaPuddingCall[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return _c.Parent.OnPudding() +} + +func (_c *bananaPuddingCall[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return _c.Parent.OnTree(aParam) +} + +func (_c *bananaPuddingCall[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlowerRaw() +} + +func (_c *bananaPuddingCall[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return _c.Parent.OnPuddingRaw() +} + +func (_c *bananaPuddingCall[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return _c.Parent.OnTreeRaw(aParam) +} + +func (_m *bananaMock[T, U]) Tree(aParam T) { + _m.Called(aParam) +} + +func (_m *bananaMock[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return &bananaTreeCall[T, U]{Call: _m.Mock.On("Tree", aParam), Parent: _m} +} + +func (_m *bananaMock[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return &bananaTreeCall[T, U]{Call: _m.Mock.On("Tree", aParam), Parent: _m} +} + +type bananaTreeCall[T any, U any] struct { + *mock.Call + Parent *bananaMock[T, U] +} + +func (_c *bananaTreeCall[T, U]) Panic(msg string) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Panic(msg) + return _c +} + +func (_c *bananaTreeCall[T, U]) Once() *bananaTreeCall[T, U] { + _c.Call = _c.Call.Once() + return _c +} + +func (_c *bananaTreeCall[T, U]) Twice() *bananaTreeCall[T, U] { + _c.Call = _c.Call.Twice() + return _c +} + +func (_c *bananaTreeCall[T, U]) Times(i int) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Times(i) + return _c +} + +func (_c *bananaTreeCall[T, U]) WaitUntil(w <-chan time.Time) *bananaTreeCall[T, U] { + _c.Call = _c.Call.WaitUntil(w) + return _c +} + +func (_c *bananaTreeCall[T, U]) After(d time.Duration) *bananaTreeCall[T, U] { + _c.Call = _c.Call.After(d) + return _c +} + +func (_c *bananaTreeCall[T, U]) Run(fn func(args mock.Arguments)) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Run(fn) + return _c +} + +func (_c *bananaTreeCall[T, U]) Maybe() *bananaTreeCall[T, U] { + _c.Call = _c.Call.Maybe() + return _c +} + +func (_c *bananaTreeCall[T, U]) TypedRun(fn func(T)) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Run(func(args mock.Arguments) { + _aParam, _ := args.Get(0).(T) + fn(_aParam) + }) + return _c +} + +func (_c *bananaTreeCall[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlower() +} + +func (_c *bananaTreeCall[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return _c.Parent.OnPudding() +} + +func (_c *bananaTreeCall[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return _c.Parent.OnTree(aParam) +} + +func (_c *bananaTreeCall[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlowerRaw() +} + +func (_c *bananaTreeCall[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return _c.Parent.OnPuddingRaw() +} + +func (_c *bananaTreeCall[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return _c.Parent.OnTreeRaw(aParam) +} diff --git a/testdata/src/a/mock_gen_test.go.golden b/testdata/src/a/mock_gen_test.go.golden index c477ba4..80c0da6 100644 --- a/testdata/src/a/mock_gen_test.go.golden +++ b/testdata/src/a/mock_gen_test.go.golden @@ -4115,3 +4115,301 @@ func (_c *cherryV2CarrotCall) OnV2Carrot() *cherryV2CarrotCall { func (_c *cherryV2CarrotCall) OnV2CarrotRaw() *cherryV2CarrotCall { return _c.Parent.OnV2CarrotRaw() } + +// bananaMock mock of Banana. +type bananaMock[T any, U any] struct{ mock.Mock } + +// newBananaMock creates a new bananaMock. +func newBananaMock[T any, U any](tb testing.TB) *bananaMock[T, U] { + tb.Helper() + + m := &bananaMock[T, U]{} + m.Mock.Test(tb) + + tb.Cleanup(func() { m.AssertExpectations(tb) }) + + return m +} + +func (_m *bananaMock[T, U]) Flower() U { + _ret := _m.Called() + + if _rf, ok := _ret.Get(0).(func() U); ok { + return _rf() + } + + _ra0, _ := _ret.Get(0).(U) + + return _ra0 +} + +func (_m *bananaMock[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return &bananaFlowerCall[T, U]{Call: _m.Mock.On("Flower"), Parent: _m} +} + +func (_m *bananaMock[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return &bananaFlowerCall[T, U]{Call: _m.Mock.On("Flower"), Parent: _m} +} + +type bananaFlowerCall[T any, U any] struct { + *mock.Call + Parent *bananaMock[T, U] +} + +func (_c *bananaFlowerCall[T, U]) Panic(msg string) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Panic(msg) + return _c +} + +func (_c *bananaFlowerCall[T, U]) Once() *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Once() + return _c +} + +func (_c *bananaFlowerCall[T, U]) Twice() *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Twice() + return _c +} + +func (_c *bananaFlowerCall[T, U]) Times(i int) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Times(i) + return _c +} + +func (_c *bananaFlowerCall[T, U]) WaitUntil(w <-chan time.Time) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.WaitUntil(w) + return _c +} + +func (_c *bananaFlowerCall[T, U]) After(d time.Duration) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.After(d) + return _c +} + +func (_c *bananaFlowerCall[T, U]) Run(fn func(args mock.Arguments)) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Run(fn) + return _c +} + +func (_c *bananaFlowerCall[T, U]) Maybe() *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Maybe() + return _c +} + +func (_c *bananaFlowerCall[T, U]) TypedReturns(a U) *bananaFlowerCall[T, U] { + _c.Call = _c.Return(a) + return _c +} + +func (_c *bananaFlowerCall[T, U]) ReturnsFn(fn func() U) *bananaFlowerCall[T, U] { + _c.Call = _c.Return(fn) + return _c +} + +func (_c *bananaFlowerCall[T, U]) TypedRun(fn func()) *bananaFlowerCall[T, U] { + _c.Call = _c.Call.Run(func(args mock.Arguments) { + fn() + }) + return _c +} + +func (_c *bananaFlowerCall[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlower() +} + +func (_c *bananaFlowerCall[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return _c.Parent.OnPudding() +} + +func (_c *bananaFlowerCall[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return _c.Parent.OnTree(aParam) +} + +func (_c *bananaFlowerCall[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlowerRaw() +} + +func (_c *bananaFlowerCall[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return _c.Parent.OnPuddingRaw() +} + +func (_c *bananaFlowerCall[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return _c.Parent.OnTreeRaw(aParam) +} + +func (_m *bananaMock[T, U]) Pudding() { + _m.Called() +} + +func (_m *bananaMock[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return &bananaPuddingCall[T, U]{Call: _m.Mock.On("Pudding"), Parent: _m} +} + +func (_m *bananaMock[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return &bananaPuddingCall[T, U]{Call: _m.Mock.On("Pudding"), Parent: _m} +} + +type bananaPuddingCall[T any, U any] struct { + *mock.Call + Parent *bananaMock[T, U] +} + +func (_c *bananaPuddingCall[T, U]) Panic(msg string) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Panic(msg) + return _c +} + +func (_c *bananaPuddingCall[T, U]) Once() *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Once() + return _c +} + +func (_c *bananaPuddingCall[T, U]) Twice() *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Twice() + return _c +} + +func (_c *bananaPuddingCall[T, U]) Times(i int) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Times(i) + return _c +} + +func (_c *bananaPuddingCall[T, U]) WaitUntil(w <-chan time.Time) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.WaitUntil(w) + return _c +} + +func (_c *bananaPuddingCall[T, U]) After(d time.Duration) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.After(d) + return _c +} + +func (_c *bananaPuddingCall[T, U]) Run(fn func(args mock.Arguments)) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Run(fn) + return _c +} + +func (_c *bananaPuddingCall[T, U]) Maybe() *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Maybe() + return _c +} + +func (_c *bananaPuddingCall[T, U]) TypedRun(fn func()) *bananaPuddingCall[T, U] { + _c.Call = _c.Call.Run(func(args mock.Arguments) { + fn() + }) + return _c +} + +func (_c *bananaPuddingCall[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlower() +} + +func (_c *bananaPuddingCall[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return _c.Parent.OnPudding() +} + +func (_c *bananaPuddingCall[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return _c.Parent.OnTree(aParam) +} + +func (_c *bananaPuddingCall[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlowerRaw() +} + +func (_c *bananaPuddingCall[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return _c.Parent.OnPuddingRaw() +} + +func (_c *bananaPuddingCall[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return _c.Parent.OnTreeRaw(aParam) +} + +func (_m *bananaMock[T, U]) Tree(aParam T) { + _m.Called(aParam) +} + +func (_m *bananaMock[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return &bananaTreeCall[T, U]{Call: _m.Mock.On("Tree", aParam), Parent: _m} +} + +func (_m *bananaMock[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return &bananaTreeCall[T, U]{Call: _m.Mock.On("Tree", aParam), Parent: _m} +} + +type bananaTreeCall[T any, U any] struct { + *mock.Call + Parent *bananaMock[T, U] +} + +func (_c *bananaTreeCall[T, U]) Panic(msg string) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Panic(msg) + return _c +} + +func (_c *bananaTreeCall[T, U]) Once() *bananaTreeCall[T, U] { + _c.Call = _c.Call.Once() + return _c +} + +func (_c *bananaTreeCall[T, U]) Twice() *bananaTreeCall[T, U] { + _c.Call = _c.Call.Twice() + return _c +} + +func (_c *bananaTreeCall[T, U]) Times(i int) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Times(i) + return _c +} + +func (_c *bananaTreeCall[T, U]) WaitUntil(w <-chan time.Time) *bananaTreeCall[T, U] { + _c.Call = _c.Call.WaitUntil(w) + return _c +} + +func (_c *bananaTreeCall[T, U]) After(d time.Duration) *bananaTreeCall[T, U] { + _c.Call = _c.Call.After(d) + return _c +} + +func (_c *bananaTreeCall[T, U]) Run(fn func(args mock.Arguments)) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Run(fn) + return _c +} + +func (_c *bananaTreeCall[T, U]) Maybe() *bananaTreeCall[T, U] { + _c.Call = _c.Call.Maybe() + return _c +} + +func (_c *bananaTreeCall[T, U]) TypedRun(fn func(T)) *bananaTreeCall[T, U] { + _c.Call = _c.Call.Run(func(args mock.Arguments) { + _aParam, _ := args.Get(0).(T) + fn(_aParam) + }) + return _c +} + +func (_c *bananaTreeCall[T, U]) OnFlower() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlower() +} + +func (_c *bananaTreeCall[T, U]) OnPudding() *bananaPuddingCall[T, U] { + return _c.Parent.OnPudding() +} + +func (_c *bananaTreeCall[T, U]) OnTree(aParam T) *bananaTreeCall[T, U] { + return _c.Parent.OnTree(aParam) +} + +func (_c *bananaTreeCall[T, U]) OnFlowerRaw() *bananaFlowerCall[T, U] { + return _c.Parent.OnFlowerRaw() +} + +func (_c *bananaTreeCall[T, U]) OnPuddingRaw() *bananaPuddingCall[T, U] { + return _c.Parent.OnPuddingRaw() +} + +func (_c *bananaTreeCall[T, U]) OnTreeRaw(aParam interface{}) *bananaTreeCall[T, U] { + return _c.Parent.OnTreeRaw(aParam) +} diff --git a/testdata/src/a/mock_test.go b/testdata/src/a/mock_test.go index 870dce3..cfdea8c 100644 --- a/testdata/src/a/mock_test.go +++ b/testdata/src/a/mock_test.go @@ -12,6 +12,7 @@ import ( // mocktail-:fmt.Stringer // mocktail:Orange // mocktail:d.Cherry +// mocktail:Banana func TestName(t *testing.T) { var s Pineapple = newPineappleMock(t). @@ -19,7 +20,7 @@ func TestName(t *testing.T) { OnWorld().TypedReturns("a").Once(). OnGoo().TypedReturns("", 1, Water{}).Once(). OnCoo("", Water{}).TypedReturns(Water{}). - TypedRun(func(s string, water Water) {}).Once(). + TypedRun(func(string, Water) {}).Once(). Parent s.Hello(Water{}) @@ -27,7 +28,7 @@ func TestName(t *testing.T) { s.Goo() s.Coo(context.Background(), "", Water{}) - fn := func(st Strawberry, stban Strawberry) Pineapple { + fn := func( Strawberry, Strawberry) Pineapple { return s } @@ -55,4 +56,14 @@ func TestName(t *testing.T) { case <-time.After(10 * time.Millisecond): t.Fatalf("timed out waiting for an orange juice") } + + var b Banana[string, int] = newBananaMock[string, int](t). + OnTree("a").Once(). + OnFlower().TypedReturns(1).Once(). + OnPudding().Once(). + Parent + + b.Tree("a") + b.Flower() + b.Pudding() }