From dfcb242ef9b927f036f45e3f8fda375b23455012 Mon Sep 17 00:00:00 2001 From: sleygin Date: Wed, 19 Jun 2024 01:36:36 +0300 Subject: [PATCH 1/6] generation from interface aliases added --- Makefile | 3 + cmd/minimock/minimock.go | 14 +- go.mod | 2 + go.sum | 2 - internal/types/interface.go | 143 +++++++++++-- tests/formatter_alias_mock.go | 392 ++++++++++++++++++++++++++++++++++ tests/formatter_type_mock.go | 392 ++++++++++++++++++++++++++++++++++ tests/reader_mock.go | 365 +++++++++++++++++++++++++++++++ tests/types.go | 7 + 9 files changed, 1300 insertions(+), 20 deletions(-) create mode 100644 tests/formatter_alias_mock.go create mode 100644 tests/formatter_type_mock.go create mode 100644 tests/reader_mock.go diff --git a/Makefile b/Makefile index 132bd40..022f0ad 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,9 @@ generate: go run ./cmd/minimock/minimock.go -i ./tests.contextAccepter -o ./tests/context_accepter_mock.go go run ./cmd/minimock/minimock.go -i github.com/gojuno/minimock/v3.Tester -o ./tests/package_name_specified_test.go -p tests_test go run ./cmd/minimock/minimock.go -i ./tests.actor -o ./tests/actor_mock.go + go run ./cmd/minimock/minimock.go -i ./tests.formatterAlias -o ./tests/formatter_alias_mock.go -from-aliases + go run ./cmd/minimock/minimock.go -i ./tests.formatterType -o ./tests/formatter_type_mock.go -from-aliases + go run ./cmd/minimock/minimock.go -i ./tests.reader -o ./tests/reader_mock.go -from-aliases ./bin: mkdir ./bin diff --git a/cmd/minimock/minimock.go b/cmd/minimock/minimock.go index 801ab0e..d7d69b1 100644 --- a/cmd/minimock/minimock.go +++ b/cmd/minimock/minimock.go @@ -51,11 +51,12 @@ var helpers = template.FuncMap{ type ( options struct { - interfaces []interfaceInfo - noGenerate bool - suffix string - mockNames []string - packageNames []string + interfaces []interfaceInfo + noGenerate bool + generateFromAliases bool + suffix string + mockNames []string + packageNames []string } interfaceInfo struct { @@ -146,7 +147,7 @@ func run(opts *options) (err error) { } } - interfaces := types.FindAllInterfaces(astPackage, in.Type) + interfaces := types.FindAllInterfaces(astPackage, in.Type, opts.generateFromAliases) packageName := "" if len(opts.interfaces) == len(opts.packageNames) { @@ -383,6 +384,7 @@ func processArgs(args []string, stdout, stderr io.Writer) (*options, error) { fs.BoolVar(&opts.noGenerate, "g", false, "don't put go:generate instruction into the generated code") fs.StringVar(&opts.suffix, "s", "_mock_test.go", "mock file suffix") + fs.BoolVar(&opts.generateFromAliases, "from-aliases", false, "generate mocks from interface aliases as well") input := fs.String("i", "*", "comma-separated names of the interfaces to mock, i.e fmt.Stringer,io.Reader\nuse io.* notation to generate mocks for all interfaces in the \"io\" package") output := fs.String("o", "", "comma-separated destination file names or packages to put the generated mocks in,\nby default the generated mock is placed in the source package directory") diff --git a/go.mod b/go.mod index 4e3a425..d68744f 100644 --- a/go.mod +++ b/go.mod @@ -22,3 +22,5 @@ require ( go 1.22 toolchain go1.22.0 + +replace github.com/hexdigest/gowrap v1.3.7 => ../gowrap diff --git a/go.sum b/go.sum index c341f64..ddcb28a 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/hexdigest/gowrap v1.3.7 h1:vgfh7NQEZQyMe17PBaMX/d4+7hTLyxo2q4DFTfx3p1E= -github.com/hexdigest/gowrap v1.3.7/go.mod h1:5KTYxPjK1RRfD+9L4Oo9gjP3XNAs4rkoVK2E7eAEFyM= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= diff --git a/internal/types/interface.go b/internal/types/interface.go index 94fd2ec..393ce84 100644 --- a/internal/types/interface.go +++ b/internal/types/interface.go @@ -5,6 +5,9 @@ import ( "go/ast" "go/printer" "go/token" + "strings" + + "github.com/hexdigest/gowrap/pkg" ) // InterfaceSpecification represents abstraction over interface type. It contains all the metadata @@ -22,18 +25,14 @@ type InterfaceSpecificationParam struct { ParamType string } -func FindAllInterfaces(p *ast.Package, pattern string) []InterfaceSpecification { - // Find all declared types in a single package - types := []*ast.TypeSpec{} - for _, file := range p.Files { - types = append(types, findAllTypeSpecsInFile(file)...) - } - +func FindAllInterfaces(p *ast.Package, pattern string, withAliases bool) []InterfaceSpecification { // Filter interfaces from all the declarations interfaces := []*ast.TypeSpec{} - for _, typeSpec := range types { - if isInterface(typeSpec) { - interfaces = append(interfaces, typeSpec) + for _, file := range p.Files { + for _, typeSpec := range findAllTypeSpecsInFile(file) { + if isInterface(typeSpec, file.Imports, withAliases) { + interfaces = append(interfaces, typeSpec) + } } } @@ -57,8 +56,128 @@ func FindAllInterfaces(p *ast.Package, pattern string) []InterfaceSpecification return interfaceSpecifications } -func isInterface(typeSpec *ast.TypeSpec) bool { - // Check if this type declaration is specifically an interface declaration +func isInterface(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec, withAliases bool) bool { + // we are generating mocks for interfaces, + // interface aliases to types from the same package + // and aliases to types from another package + if !withAliases { + return isInterfaceType(typeSpec) + } + return isInterfaceOrAlias(typeSpec, fileImports) +} + +func isInterfaceOrAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { + return isInterfaceType(typeSpec) || + isInterfaceAlias(typeSpec, fileImports) || + isExportedInterfaceAlias(typeSpec, fileImports) +} + +// isInterfaceAlias checks if type is an alias to other +// interface type that is in the same package +func isInterfaceAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { + ident, ok := typeSpec.Type.(*ast.Ident) + if !ok { + return false + } + + if ident.Obj == nil { + return false + } + + if ts, ok := ident.Obj.Decl.(*ast.TypeSpec); ok { + return isInterfaceOrAlias(ts, fileImports) + } + + return false +} + +// isExportedInterfaceAlias checks if type is an alias to other +// interface type that is in other exported package +func isExportedInterfaceAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { + selector, ok := typeSpec.Type.(*ast.SelectorExpr) + if !ok { + return false + } + + ident, ok := selector.X.(*ast.Ident) + if !ok { + return false + } + + srcPkgPath := findSourcePackage(ident, fileImports) + srcAst, err := getPackageAst(srcPkgPath) + if err != nil { + return false + } + + typeSpec, imports := findTypeSoecInPackage(srcAst, selector.Sel.Name) + + // we have to check recursively because checked typed might be + // another alias to other interface + return isInterfaceOrAlias(typeSpec, imports) +} + +func getPackageAst(packagePath string) (*ast.Package, error) { + srcPkg, err := pkg.Load(packagePath) + if err != nil { + return nil, err + } + + fs := token.NewFileSet() + srcAst, err := pkg.AST(fs, srcPkg) + if err != nil { + return nil, err + } + + return srcAst, nil +} + +func findTypeSoecInPackage(p *ast.Package, name string) (typeSpec *ast.TypeSpec, imports []*ast.ImportSpec) { + for _, f := range p.Files { + if f == nil { + continue + } + types := findAllTypeSpecsInFile(f) + typeSpec, found := findTypeByName(types, name) + if found { + return typeSpec, f.Imports + } + } + + return +} + +func findTypeByName(types []*ast.TypeSpec, name string) (*ast.TypeSpec, bool) { + for _, ts := range types { + if ts.Name.Name == name { + return ts, true + } + } + + return nil, false +} + +func findSourcePackage(ident *ast.Ident, imports []*ast.ImportSpec) string { + for _, imp := range imports { + cleanPath := strings.Trim(imp.Path.Value, "\"") + if imp.Name != nil { + if ident.Name == imp.Name.Name { + return cleanPath + } + + continue + } + + slash := strings.LastIndex(cleanPath, "/") + if ident.Name == cleanPath[slash+1:] { + return cleanPath + } + } + + return "" +} + +func isInterfaceType(typeSpec *ast.TypeSpec) bool { _, ok := typeSpec.Type.(*ast.InterfaceType) return ok } diff --git a/tests/formatter_alias_mock.go b/tests/formatter_alias_mock.go new file mode 100644 index 0000000..84fa9ca --- /dev/null +++ b/tests/formatter_alias_mock.go @@ -0,0 +1,392 @@ +// Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. + +package tests + +//go:generate minimock -i github.com/gojuno/minimock/v3/tests.formatterAlias -o formatter_alias_mock.go -n FormatterAliasMock -p tests + +import ( + "sync" + mm_atomic "sync/atomic" + mm_time "time" + + "github.com/gojuno/minimock/v3" +) + +// FormatterAliasMock implements formatterAlias +type FormatterAliasMock struct { + t minimock.Tester + finishOnce sync.Once + + funcFormat func(s1 string, p1 ...interface{}) (s2 string) + inspectFuncFormat func(s1 string, p1 ...interface{}) + afterFormatCounter uint64 + beforeFormatCounter uint64 + FormatMock mFormatterAliasMockFormat +} + +// NewFormatterAliasMock returns a mock for formatterAlias +func NewFormatterAliasMock(t minimock.Tester) *FormatterAliasMock { + m := &FormatterAliasMock{t: t} + + if controller, ok := t.(minimock.MockController); ok { + controller.RegisterMocker(m) + } + + m.FormatMock = mFormatterAliasMockFormat{mock: m} + m.FormatMock.callArgs = []*FormatterAliasMockFormatParams{} + + t.Cleanup(m.MinimockFinish) + + return m +} + +type mFormatterAliasMockFormat struct { + optional bool + mock *FormatterAliasMock + defaultExpectation *FormatterAliasMockFormatExpectation + expectations []*FormatterAliasMockFormatExpectation + + callArgs []*FormatterAliasMockFormatParams + mutex sync.RWMutex + + expectedInvocations uint64 +} + +// FormatterAliasMockFormatExpectation specifies expectation struct of the formatterAlias.Format +type FormatterAliasMockFormatExpectation struct { + mock *FormatterAliasMock + params *FormatterAliasMockFormatParams + paramPtrs *FormatterAliasMockFormatParamPtrs + results *FormatterAliasMockFormatResults + Counter uint64 +} + +// FormatterAliasMockFormatParams contains parameters of the formatterAlias.Format +type FormatterAliasMockFormatParams struct { + s1 string + p1 []interface{} +} + +// FormatterAliasMockFormatParamPtrs contains pointers to parameters of the formatterAlias.Format +type FormatterAliasMockFormatParamPtrs struct { + s1 *string + p1 *[]interface{} +} + +// FormatterAliasMockFormatResults contains results of the formatterAlias.Format +type FormatterAliasMockFormatResults struct { + s2 string +} + +// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning +// the test will fail minimock's automatic final call check if the mocked method was not called at least once. +// Optional() makes method check to work in '0 or more' mode. +// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to +// catch the problems when the expected method call is totally skipped during test run. +func (mmFormat *mFormatterAliasMockFormat) Optional() *mFormatterAliasMockFormat { + mmFormat.optional = true + return mmFormat +} + +// Expect sets up expected params for formatterAlias.Format +func (mmFormat *mFormatterAliasMockFormat) Expect(s1 string, p1 ...interface{}) *mFormatterAliasMockFormat { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterAliasMockFormatExpectation{} + } + + if mmFormat.defaultExpectation.paramPtrs != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by ExpectParams functions") + } + + mmFormat.defaultExpectation.params = &FormatterAliasMockFormatParams{s1, p1} + for _, e := range mmFormat.expectations { + if minimock.Equal(e.params, mmFormat.defaultExpectation.params) { + mmFormat.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmFormat.defaultExpectation.params) + } + } + + return mmFormat +} + +// ExpectS1Param1 sets up expected param s1 for formatterAlias.Format +func (mmFormat *mFormatterAliasMockFormat) ExpectS1Param1(s1 string) *mFormatterAliasMockFormat { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterAliasMockFormatExpectation{} + } + + if mmFormat.defaultExpectation.params != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Expect") + } + + if mmFormat.defaultExpectation.paramPtrs == nil { + mmFormat.defaultExpectation.paramPtrs = &FormatterAliasMockFormatParamPtrs{} + } + mmFormat.defaultExpectation.paramPtrs.s1 = &s1 + + return mmFormat +} + +// ExpectP1Param2 sets up expected param p1 for formatterAlias.Format +func (mmFormat *mFormatterAliasMockFormat) ExpectP1Param2(p1 ...interface{}) *mFormatterAliasMockFormat { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterAliasMockFormatExpectation{} + } + + if mmFormat.defaultExpectation.params != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Expect") + } + + if mmFormat.defaultExpectation.paramPtrs == nil { + mmFormat.defaultExpectation.paramPtrs = &FormatterAliasMockFormatParamPtrs{} + } + mmFormat.defaultExpectation.paramPtrs.p1 = &p1 + + return mmFormat +} + +// Inspect accepts an inspector function that has same arguments as the formatterAlias.Format +func (mmFormat *mFormatterAliasMockFormat) Inspect(f func(s1 string, p1 ...interface{})) *mFormatterAliasMockFormat { + if mmFormat.mock.inspectFuncFormat != nil { + mmFormat.mock.t.Fatalf("Inspect function is already set for FormatterAliasMock.Format") + } + + mmFormat.mock.inspectFuncFormat = f + + return mmFormat +} + +// Return sets up results that will be returned by formatterAlias.Format +func (mmFormat *mFormatterAliasMockFormat) Return(s2 string) *FormatterAliasMock { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterAliasMockFormatExpectation{mock: mmFormat.mock} + } + mmFormat.defaultExpectation.results = &FormatterAliasMockFormatResults{s2} + return mmFormat.mock +} + +// Set uses given function f to mock the formatterAlias.Format method +func (mmFormat *mFormatterAliasMockFormat) Set(f func(s1 string, p1 ...interface{}) (s2 string)) *FormatterAliasMock { + if mmFormat.defaultExpectation != nil { + mmFormat.mock.t.Fatalf("Default expectation is already set for the formatterAlias.Format method") + } + + if len(mmFormat.expectations) > 0 { + mmFormat.mock.t.Fatalf("Some expectations are already set for the formatterAlias.Format method") + } + + mmFormat.mock.funcFormat = f + return mmFormat.mock +} + +// When sets expectation for the formatterAlias.Format which will trigger the result defined by the following +// Then helper +func (mmFormat *mFormatterAliasMockFormat) When(s1 string, p1 ...interface{}) *FormatterAliasMockFormatExpectation { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterAliasMock.Format mock is already set by Set") + } + + expectation := &FormatterAliasMockFormatExpectation{ + mock: mmFormat.mock, + params: &FormatterAliasMockFormatParams{s1, p1}, + } + mmFormat.expectations = append(mmFormat.expectations, expectation) + return expectation +} + +// Then sets up formatterAlias.Format return parameters for the expectation previously defined by the When method +func (e *FormatterAliasMockFormatExpectation) Then(s2 string) *FormatterAliasMock { + e.results = &FormatterAliasMockFormatResults{s2} + return e.mock +} + +// Times sets number of times formatterAlias.Format should be invoked +func (mmFormat *mFormatterAliasMockFormat) Times(n uint64) *mFormatterAliasMockFormat { + if n == 0 { + mmFormat.mock.t.Fatalf("Times of FormatterAliasMock.Format mock can not be zero") + } + mm_atomic.StoreUint64(&mmFormat.expectedInvocations, n) + return mmFormat +} + +func (mmFormat *mFormatterAliasMockFormat) invocationsDone() bool { + if len(mmFormat.expectations) == 0 && mmFormat.defaultExpectation == nil && mmFormat.mock.funcFormat == nil { + return true + } + + totalInvocations := mm_atomic.LoadUint64(&mmFormat.mock.afterFormatCounter) + expectedInvocations := mm_atomic.LoadUint64(&mmFormat.expectedInvocations) + + return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) +} + +// Format implements formatterAlias +func (mmFormat *FormatterAliasMock) Format(s1 string, p1 ...interface{}) (s2 string) { + mm_atomic.AddUint64(&mmFormat.beforeFormatCounter, 1) + defer mm_atomic.AddUint64(&mmFormat.afterFormatCounter, 1) + + if mmFormat.inspectFuncFormat != nil { + mmFormat.inspectFuncFormat(s1, p1...) + } + + mm_params := FormatterAliasMockFormatParams{s1, p1} + + // Record call args + mmFormat.FormatMock.mutex.Lock() + mmFormat.FormatMock.callArgs = append(mmFormat.FormatMock.callArgs, &mm_params) + mmFormat.FormatMock.mutex.Unlock() + + for _, e := range mmFormat.FormatMock.expectations { + if minimock.Equal(*e.params, mm_params) { + mm_atomic.AddUint64(&e.Counter, 1) + return e.results.s2 + } + } + + if mmFormat.FormatMock.defaultExpectation != nil { + mm_atomic.AddUint64(&mmFormat.FormatMock.defaultExpectation.Counter, 1) + mm_want := mmFormat.FormatMock.defaultExpectation.params + mm_want_ptrs := mmFormat.FormatMock.defaultExpectation.paramPtrs + + mm_got := FormatterAliasMockFormatParams{s1, p1} + + if mm_want_ptrs != nil { + + if mm_want_ptrs.s1 != nil && !minimock.Equal(*mm_want_ptrs.s1, mm_got.s1) { + mmFormat.t.Errorf("FormatterAliasMock.Format got unexpected parameter s1, want: %#v, got: %#v%s\n", *mm_want_ptrs.s1, mm_got.s1, minimock.Diff(*mm_want_ptrs.s1, mm_got.s1)) + } + + if mm_want_ptrs.p1 != nil && !minimock.Equal(*mm_want_ptrs.p1, mm_got.p1) { + mmFormat.t.Errorf("FormatterAliasMock.Format got unexpected parameter p1, want: %#v, got: %#v%s\n", *mm_want_ptrs.p1, mm_got.p1, minimock.Diff(*mm_want_ptrs.p1, mm_got.p1)) + } + + } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { + mmFormat.t.Errorf("FormatterAliasMock.Format got unexpected parameters, want: %#v, got: %#v%s\n", *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) + } + + mm_results := mmFormat.FormatMock.defaultExpectation.results + if mm_results == nil { + mmFormat.t.Fatal("No results are set for the FormatterAliasMock.Format") + } + return (*mm_results).s2 + } + if mmFormat.funcFormat != nil { + return mmFormat.funcFormat(s1, p1...) + } + mmFormat.t.Fatalf("Unexpected call to FormatterAliasMock.Format. %v %v", s1, p1) + return +} + +// FormatAfterCounter returns a count of finished FormatterAliasMock.Format invocations +func (mmFormat *FormatterAliasMock) FormatAfterCounter() uint64 { + return mm_atomic.LoadUint64(&mmFormat.afterFormatCounter) +} + +// FormatBeforeCounter returns a count of FormatterAliasMock.Format invocations +func (mmFormat *FormatterAliasMock) FormatBeforeCounter() uint64 { + return mm_atomic.LoadUint64(&mmFormat.beforeFormatCounter) +} + +// Calls returns a list of arguments used in each call to FormatterAliasMock.Format. +// The list is in the same order as the calls were made (i.e. recent calls have a higher index) +func (mmFormat *mFormatterAliasMockFormat) Calls() []*FormatterAliasMockFormatParams { + mmFormat.mutex.RLock() + + argCopy := make([]*FormatterAliasMockFormatParams, len(mmFormat.callArgs)) + copy(argCopy, mmFormat.callArgs) + + mmFormat.mutex.RUnlock() + + return argCopy +} + +// MinimockFormatDone returns true if the count of the Format invocations corresponds +// the number of defined expectations +func (m *FormatterAliasMock) MinimockFormatDone() bool { + if m.FormatMock.optional { + // Optional methods provide '0 or more' call count restriction. + return true + } + + for _, e := range m.FormatMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + return false + } + } + + return m.FormatMock.invocationsDone() +} + +// MinimockFormatInspect logs each unmet expectation +func (m *FormatterAliasMock) MinimockFormatInspect() { + for _, e := range m.FormatMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + m.t.Errorf("Expected call to FormatterAliasMock.Format with params: %#v", *e.params) + } + } + + afterFormatCounter := mm_atomic.LoadUint64(&m.afterFormatCounter) + // if default expectation was set then invocations count should be greater than zero + if m.FormatMock.defaultExpectation != nil && afterFormatCounter < 1 { + if m.FormatMock.defaultExpectation.params == nil { + m.t.Error("Expected call to FormatterAliasMock.Format") + } else { + m.t.Errorf("Expected call to FormatterAliasMock.Format with params: %#v", *m.FormatMock.defaultExpectation.params) + } + } + // if func was set then invocations count should be greater than zero + if m.funcFormat != nil && afterFormatCounter < 1 { + m.t.Error("Expected call to FormatterAliasMock.Format") + } + + if !m.FormatMock.invocationsDone() && afterFormatCounter > 0 { + m.t.Errorf("Expected %d calls to FormatterAliasMock.Format but found %d calls", + mm_atomic.LoadUint64(&m.FormatMock.expectedInvocations), afterFormatCounter) + } +} + +// MinimockFinish checks that all mocked methods have been called the expected number of times +func (m *FormatterAliasMock) MinimockFinish() { + m.finishOnce.Do(func() { + if !m.minimockDone() { + m.MinimockFormatInspect() + } + }) +} + +// MinimockWait waits for all mocked methods to be called the expected number of times +func (m *FormatterAliasMock) MinimockWait(timeout mm_time.Duration) { + timeoutCh := mm_time.After(timeout) + for { + if m.minimockDone() { + return + } + select { + case <-timeoutCh: + m.MinimockFinish() + return + case <-mm_time.After(10 * mm_time.Millisecond): + } + } +} + +func (m *FormatterAliasMock) minimockDone() bool { + done := true + return done && + m.MinimockFormatDone() +} diff --git a/tests/formatter_type_mock.go b/tests/formatter_type_mock.go new file mode 100644 index 0000000..3ca88ee --- /dev/null +++ b/tests/formatter_type_mock.go @@ -0,0 +1,392 @@ +// Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. + +package tests + +//go:generate minimock -i github.com/gojuno/minimock/v3/tests.formatterType -o formatter_type_mock.go -n FormatterTypeMock -p tests + +import ( + "sync" + mm_atomic "sync/atomic" + mm_time "time" + + "github.com/gojuno/minimock/v3" +) + +// FormatterTypeMock implements formatterType +type FormatterTypeMock struct { + t minimock.Tester + finishOnce sync.Once + + funcFormat func(s1 string, p1 ...interface{}) (s2 string) + inspectFuncFormat func(s1 string, p1 ...interface{}) + afterFormatCounter uint64 + beforeFormatCounter uint64 + FormatMock mFormatterTypeMockFormat +} + +// NewFormatterTypeMock returns a mock for formatterType +func NewFormatterTypeMock(t minimock.Tester) *FormatterTypeMock { + m := &FormatterTypeMock{t: t} + + if controller, ok := t.(minimock.MockController); ok { + controller.RegisterMocker(m) + } + + m.FormatMock = mFormatterTypeMockFormat{mock: m} + m.FormatMock.callArgs = []*FormatterTypeMockFormatParams{} + + t.Cleanup(m.MinimockFinish) + + return m +} + +type mFormatterTypeMockFormat struct { + optional bool + mock *FormatterTypeMock + defaultExpectation *FormatterTypeMockFormatExpectation + expectations []*FormatterTypeMockFormatExpectation + + callArgs []*FormatterTypeMockFormatParams + mutex sync.RWMutex + + expectedInvocations uint64 +} + +// FormatterTypeMockFormatExpectation specifies expectation struct of the formatterType.Format +type FormatterTypeMockFormatExpectation struct { + mock *FormatterTypeMock + params *FormatterTypeMockFormatParams + paramPtrs *FormatterTypeMockFormatParamPtrs + results *FormatterTypeMockFormatResults + Counter uint64 +} + +// FormatterTypeMockFormatParams contains parameters of the formatterType.Format +type FormatterTypeMockFormatParams struct { + s1 string + p1 []interface{} +} + +// FormatterTypeMockFormatParamPtrs contains pointers to parameters of the formatterType.Format +type FormatterTypeMockFormatParamPtrs struct { + s1 *string + p1 *[]interface{} +} + +// FormatterTypeMockFormatResults contains results of the formatterType.Format +type FormatterTypeMockFormatResults struct { + s2 string +} + +// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning +// the test will fail minimock's automatic final call check if the mocked method was not called at least once. +// Optional() makes method check to work in '0 or more' mode. +// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to +// catch the problems when the expected method call is totally skipped during test run. +func (mmFormat *mFormatterTypeMockFormat) Optional() *mFormatterTypeMockFormat { + mmFormat.optional = true + return mmFormat +} + +// Expect sets up expected params for formatterType.Format +func (mmFormat *mFormatterTypeMockFormat) Expect(s1 string, p1 ...interface{}) *mFormatterTypeMockFormat { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterTypeMockFormatExpectation{} + } + + if mmFormat.defaultExpectation.paramPtrs != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by ExpectParams functions") + } + + mmFormat.defaultExpectation.params = &FormatterTypeMockFormatParams{s1, p1} + for _, e := range mmFormat.expectations { + if minimock.Equal(e.params, mmFormat.defaultExpectation.params) { + mmFormat.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmFormat.defaultExpectation.params) + } + } + + return mmFormat +} + +// ExpectS1Param1 sets up expected param s1 for formatterType.Format +func (mmFormat *mFormatterTypeMockFormat) ExpectS1Param1(s1 string) *mFormatterTypeMockFormat { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterTypeMockFormatExpectation{} + } + + if mmFormat.defaultExpectation.params != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Expect") + } + + if mmFormat.defaultExpectation.paramPtrs == nil { + mmFormat.defaultExpectation.paramPtrs = &FormatterTypeMockFormatParamPtrs{} + } + mmFormat.defaultExpectation.paramPtrs.s1 = &s1 + + return mmFormat +} + +// ExpectP1Param2 sets up expected param p1 for formatterType.Format +func (mmFormat *mFormatterTypeMockFormat) ExpectP1Param2(p1 ...interface{}) *mFormatterTypeMockFormat { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterTypeMockFormatExpectation{} + } + + if mmFormat.defaultExpectation.params != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Expect") + } + + if mmFormat.defaultExpectation.paramPtrs == nil { + mmFormat.defaultExpectation.paramPtrs = &FormatterTypeMockFormatParamPtrs{} + } + mmFormat.defaultExpectation.paramPtrs.p1 = &p1 + + return mmFormat +} + +// Inspect accepts an inspector function that has same arguments as the formatterType.Format +func (mmFormat *mFormatterTypeMockFormat) Inspect(f func(s1 string, p1 ...interface{})) *mFormatterTypeMockFormat { + if mmFormat.mock.inspectFuncFormat != nil { + mmFormat.mock.t.Fatalf("Inspect function is already set for FormatterTypeMock.Format") + } + + mmFormat.mock.inspectFuncFormat = f + + return mmFormat +} + +// Return sets up results that will be returned by formatterType.Format +func (mmFormat *mFormatterTypeMockFormat) Return(s2 string) *FormatterTypeMock { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Set") + } + + if mmFormat.defaultExpectation == nil { + mmFormat.defaultExpectation = &FormatterTypeMockFormatExpectation{mock: mmFormat.mock} + } + mmFormat.defaultExpectation.results = &FormatterTypeMockFormatResults{s2} + return mmFormat.mock +} + +// Set uses given function f to mock the formatterType.Format method +func (mmFormat *mFormatterTypeMockFormat) Set(f func(s1 string, p1 ...interface{}) (s2 string)) *FormatterTypeMock { + if mmFormat.defaultExpectation != nil { + mmFormat.mock.t.Fatalf("Default expectation is already set for the formatterType.Format method") + } + + if len(mmFormat.expectations) > 0 { + mmFormat.mock.t.Fatalf("Some expectations are already set for the formatterType.Format method") + } + + mmFormat.mock.funcFormat = f + return mmFormat.mock +} + +// When sets expectation for the formatterType.Format which will trigger the result defined by the following +// Then helper +func (mmFormat *mFormatterTypeMockFormat) When(s1 string, p1 ...interface{}) *FormatterTypeMockFormatExpectation { + if mmFormat.mock.funcFormat != nil { + mmFormat.mock.t.Fatalf("FormatterTypeMock.Format mock is already set by Set") + } + + expectation := &FormatterTypeMockFormatExpectation{ + mock: mmFormat.mock, + params: &FormatterTypeMockFormatParams{s1, p1}, + } + mmFormat.expectations = append(mmFormat.expectations, expectation) + return expectation +} + +// Then sets up formatterType.Format return parameters for the expectation previously defined by the When method +func (e *FormatterTypeMockFormatExpectation) Then(s2 string) *FormatterTypeMock { + e.results = &FormatterTypeMockFormatResults{s2} + return e.mock +} + +// Times sets number of times formatterType.Format should be invoked +func (mmFormat *mFormatterTypeMockFormat) Times(n uint64) *mFormatterTypeMockFormat { + if n == 0 { + mmFormat.mock.t.Fatalf("Times of FormatterTypeMock.Format mock can not be zero") + } + mm_atomic.StoreUint64(&mmFormat.expectedInvocations, n) + return mmFormat +} + +func (mmFormat *mFormatterTypeMockFormat) invocationsDone() bool { + if len(mmFormat.expectations) == 0 && mmFormat.defaultExpectation == nil && mmFormat.mock.funcFormat == nil { + return true + } + + totalInvocations := mm_atomic.LoadUint64(&mmFormat.mock.afterFormatCounter) + expectedInvocations := mm_atomic.LoadUint64(&mmFormat.expectedInvocations) + + return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) +} + +// Format implements formatterType +func (mmFormat *FormatterTypeMock) Format(s1 string, p1 ...interface{}) (s2 string) { + mm_atomic.AddUint64(&mmFormat.beforeFormatCounter, 1) + defer mm_atomic.AddUint64(&mmFormat.afterFormatCounter, 1) + + if mmFormat.inspectFuncFormat != nil { + mmFormat.inspectFuncFormat(s1, p1...) + } + + mm_params := FormatterTypeMockFormatParams{s1, p1} + + // Record call args + mmFormat.FormatMock.mutex.Lock() + mmFormat.FormatMock.callArgs = append(mmFormat.FormatMock.callArgs, &mm_params) + mmFormat.FormatMock.mutex.Unlock() + + for _, e := range mmFormat.FormatMock.expectations { + if minimock.Equal(*e.params, mm_params) { + mm_atomic.AddUint64(&e.Counter, 1) + return e.results.s2 + } + } + + if mmFormat.FormatMock.defaultExpectation != nil { + mm_atomic.AddUint64(&mmFormat.FormatMock.defaultExpectation.Counter, 1) + mm_want := mmFormat.FormatMock.defaultExpectation.params + mm_want_ptrs := mmFormat.FormatMock.defaultExpectation.paramPtrs + + mm_got := FormatterTypeMockFormatParams{s1, p1} + + if mm_want_ptrs != nil { + + if mm_want_ptrs.s1 != nil && !minimock.Equal(*mm_want_ptrs.s1, mm_got.s1) { + mmFormat.t.Errorf("FormatterTypeMock.Format got unexpected parameter s1, want: %#v, got: %#v%s\n", *mm_want_ptrs.s1, mm_got.s1, minimock.Diff(*mm_want_ptrs.s1, mm_got.s1)) + } + + if mm_want_ptrs.p1 != nil && !minimock.Equal(*mm_want_ptrs.p1, mm_got.p1) { + mmFormat.t.Errorf("FormatterTypeMock.Format got unexpected parameter p1, want: %#v, got: %#v%s\n", *mm_want_ptrs.p1, mm_got.p1, minimock.Diff(*mm_want_ptrs.p1, mm_got.p1)) + } + + } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { + mmFormat.t.Errorf("FormatterTypeMock.Format got unexpected parameters, want: %#v, got: %#v%s\n", *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) + } + + mm_results := mmFormat.FormatMock.defaultExpectation.results + if mm_results == nil { + mmFormat.t.Fatal("No results are set for the FormatterTypeMock.Format") + } + return (*mm_results).s2 + } + if mmFormat.funcFormat != nil { + return mmFormat.funcFormat(s1, p1...) + } + mmFormat.t.Fatalf("Unexpected call to FormatterTypeMock.Format. %v %v", s1, p1) + return +} + +// FormatAfterCounter returns a count of finished FormatterTypeMock.Format invocations +func (mmFormat *FormatterTypeMock) FormatAfterCounter() uint64 { + return mm_atomic.LoadUint64(&mmFormat.afterFormatCounter) +} + +// FormatBeforeCounter returns a count of FormatterTypeMock.Format invocations +func (mmFormat *FormatterTypeMock) FormatBeforeCounter() uint64 { + return mm_atomic.LoadUint64(&mmFormat.beforeFormatCounter) +} + +// Calls returns a list of arguments used in each call to FormatterTypeMock.Format. +// The list is in the same order as the calls were made (i.e. recent calls have a higher index) +func (mmFormat *mFormatterTypeMockFormat) Calls() []*FormatterTypeMockFormatParams { + mmFormat.mutex.RLock() + + argCopy := make([]*FormatterTypeMockFormatParams, len(mmFormat.callArgs)) + copy(argCopy, mmFormat.callArgs) + + mmFormat.mutex.RUnlock() + + return argCopy +} + +// MinimockFormatDone returns true if the count of the Format invocations corresponds +// the number of defined expectations +func (m *FormatterTypeMock) MinimockFormatDone() bool { + if m.FormatMock.optional { + // Optional methods provide '0 or more' call count restriction. + return true + } + + for _, e := range m.FormatMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + return false + } + } + + return m.FormatMock.invocationsDone() +} + +// MinimockFormatInspect logs each unmet expectation +func (m *FormatterTypeMock) MinimockFormatInspect() { + for _, e := range m.FormatMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + m.t.Errorf("Expected call to FormatterTypeMock.Format with params: %#v", *e.params) + } + } + + afterFormatCounter := mm_atomic.LoadUint64(&m.afterFormatCounter) + // if default expectation was set then invocations count should be greater than zero + if m.FormatMock.defaultExpectation != nil && afterFormatCounter < 1 { + if m.FormatMock.defaultExpectation.params == nil { + m.t.Error("Expected call to FormatterTypeMock.Format") + } else { + m.t.Errorf("Expected call to FormatterTypeMock.Format with params: %#v", *m.FormatMock.defaultExpectation.params) + } + } + // if func was set then invocations count should be greater than zero + if m.funcFormat != nil && afterFormatCounter < 1 { + m.t.Error("Expected call to FormatterTypeMock.Format") + } + + if !m.FormatMock.invocationsDone() && afterFormatCounter > 0 { + m.t.Errorf("Expected %d calls to FormatterTypeMock.Format but found %d calls", + mm_atomic.LoadUint64(&m.FormatMock.expectedInvocations), afterFormatCounter) + } +} + +// MinimockFinish checks that all mocked methods have been called the expected number of times +func (m *FormatterTypeMock) MinimockFinish() { + m.finishOnce.Do(func() { + if !m.minimockDone() { + m.MinimockFormatInspect() + } + }) +} + +// MinimockWait waits for all mocked methods to be called the expected number of times +func (m *FormatterTypeMock) MinimockWait(timeout mm_time.Duration) { + timeoutCh := mm_time.After(timeout) + for { + if m.minimockDone() { + return + } + select { + case <-timeoutCh: + m.MinimockFinish() + return + case <-mm_time.After(10 * mm_time.Millisecond): + } + } +} + +func (m *FormatterTypeMock) minimockDone() bool { + done := true + return done && + m.MinimockFormatDone() +} diff --git a/tests/reader_mock.go b/tests/reader_mock.go new file mode 100644 index 0000000..cf2dd7a --- /dev/null +++ b/tests/reader_mock.go @@ -0,0 +1,365 @@ +// Code generated by http://github.com/gojuno/minimock (dev). DO NOT EDIT. + +package tests + +//go:generate minimock -i github.com/gojuno/minimock/v3/tests.reader -o reader_mock.go -n ReaderMock -p tests + +import ( + "sync" + mm_atomic "sync/atomic" + mm_time "time" + + "github.com/gojuno/minimock/v3" +) + +// ReaderMock implements reader +type ReaderMock struct { + t minimock.Tester + finishOnce sync.Once + + funcRead func(p []byte) (n int, err error) + inspectFuncRead func(p []byte) + afterReadCounter uint64 + beforeReadCounter uint64 + ReadMock mReaderMockRead +} + +// NewReaderMock returns a mock for reader +func NewReaderMock(t minimock.Tester) *ReaderMock { + m := &ReaderMock{t: t} + + if controller, ok := t.(minimock.MockController); ok { + controller.RegisterMocker(m) + } + + m.ReadMock = mReaderMockRead{mock: m} + m.ReadMock.callArgs = []*ReaderMockReadParams{} + + t.Cleanup(m.MinimockFinish) + + return m +} + +type mReaderMockRead struct { + optional bool + mock *ReaderMock + defaultExpectation *ReaderMockReadExpectation + expectations []*ReaderMockReadExpectation + + callArgs []*ReaderMockReadParams + mutex sync.RWMutex + + expectedInvocations uint64 +} + +// ReaderMockReadExpectation specifies expectation struct of the reader.Read +type ReaderMockReadExpectation struct { + mock *ReaderMock + params *ReaderMockReadParams + paramPtrs *ReaderMockReadParamPtrs + results *ReaderMockReadResults + Counter uint64 +} + +// ReaderMockReadParams contains parameters of the reader.Read +type ReaderMockReadParams struct { + p []byte +} + +// ReaderMockReadParamPtrs contains pointers to parameters of the reader.Read +type ReaderMockReadParamPtrs struct { + p *[]byte +} + +// ReaderMockReadResults contains results of the reader.Read +type ReaderMockReadResults struct { + n int + err error +} + +// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning +// the test will fail minimock's automatic final call check if the mocked method was not called at least once. +// Optional() makes method check to work in '0 or more' mode. +// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to +// catch the problems when the expected method call is totally skipped during test run. +func (mmRead *mReaderMockRead) Optional() *mReaderMockRead { + mmRead.optional = true + return mmRead +} + +// Expect sets up expected params for reader.Read +func (mmRead *mReaderMockRead) Expect(p []byte) *mReaderMockRead { + if mmRead.mock.funcRead != nil { + mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") + } + + if mmRead.defaultExpectation == nil { + mmRead.defaultExpectation = &ReaderMockReadExpectation{} + } + + if mmRead.defaultExpectation.paramPtrs != nil { + mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by ExpectParams functions") + } + + mmRead.defaultExpectation.params = &ReaderMockReadParams{p} + for _, e := range mmRead.expectations { + if minimock.Equal(e.params, mmRead.defaultExpectation.params) { + mmRead.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmRead.defaultExpectation.params) + } + } + + return mmRead +} + +// ExpectPParam1 sets up expected param p for reader.Read +func (mmRead *mReaderMockRead) ExpectPParam1(p []byte) *mReaderMockRead { + if mmRead.mock.funcRead != nil { + mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") + } + + if mmRead.defaultExpectation == nil { + mmRead.defaultExpectation = &ReaderMockReadExpectation{} + } + + if mmRead.defaultExpectation.params != nil { + mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Expect") + } + + if mmRead.defaultExpectation.paramPtrs == nil { + mmRead.defaultExpectation.paramPtrs = &ReaderMockReadParamPtrs{} + } + mmRead.defaultExpectation.paramPtrs.p = &p + + return mmRead +} + +// Inspect accepts an inspector function that has same arguments as the reader.Read +func (mmRead *mReaderMockRead) Inspect(f func(p []byte)) *mReaderMockRead { + if mmRead.mock.inspectFuncRead != nil { + mmRead.mock.t.Fatalf("Inspect function is already set for ReaderMock.Read") + } + + mmRead.mock.inspectFuncRead = f + + return mmRead +} + +// Return sets up results that will be returned by reader.Read +func (mmRead *mReaderMockRead) Return(n int, err error) *ReaderMock { + if mmRead.mock.funcRead != nil { + mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") + } + + if mmRead.defaultExpectation == nil { + mmRead.defaultExpectation = &ReaderMockReadExpectation{mock: mmRead.mock} + } + mmRead.defaultExpectation.results = &ReaderMockReadResults{n, err} + return mmRead.mock +} + +// Set uses given function f to mock the reader.Read method +func (mmRead *mReaderMockRead) Set(f func(p []byte) (n int, err error)) *ReaderMock { + if mmRead.defaultExpectation != nil { + mmRead.mock.t.Fatalf("Default expectation is already set for the reader.Read method") + } + + if len(mmRead.expectations) > 0 { + mmRead.mock.t.Fatalf("Some expectations are already set for the reader.Read method") + } + + mmRead.mock.funcRead = f + return mmRead.mock +} + +// When sets expectation for the reader.Read which will trigger the result defined by the following +// Then helper +func (mmRead *mReaderMockRead) When(p []byte) *ReaderMockReadExpectation { + if mmRead.mock.funcRead != nil { + mmRead.mock.t.Fatalf("ReaderMock.Read mock is already set by Set") + } + + expectation := &ReaderMockReadExpectation{ + mock: mmRead.mock, + params: &ReaderMockReadParams{p}, + } + mmRead.expectations = append(mmRead.expectations, expectation) + return expectation +} + +// Then sets up reader.Read return parameters for the expectation previously defined by the When method +func (e *ReaderMockReadExpectation) Then(n int, err error) *ReaderMock { + e.results = &ReaderMockReadResults{n, err} + return e.mock +} + +// Times sets number of times reader.Read should be invoked +func (mmRead *mReaderMockRead) Times(n uint64) *mReaderMockRead { + if n == 0 { + mmRead.mock.t.Fatalf("Times of ReaderMock.Read mock can not be zero") + } + mm_atomic.StoreUint64(&mmRead.expectedInvocations, n) + return mmRead +} + +func (mmRead *mReaderMockRead) invocationsDone() bool { + if len(mmRead.expectations) == 0 && mmRead.defaultExpectation == nil && mmRead.mock.funcRead == nil { + return true + } + + totalInvocations := mm_atomic.LoadUint64(&mmRead.mock.afterReadCounter) + expectedInvocations := mm_atomic.LoadUint64(&mmRead.expectedInvocations) + + return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations) +} + +// Read implements reader +func (mmRead *ReaderMock) Read(p []byte) (n int, err error) { + mm_atomic.AddUint64(&mmRead.beforeReadCounter, 1) + defer mm_atomic.AddUint64(&mmRead.afterReadCounter, 1) + + if mmRead.inspectFuncRead != nil { + mmRead.inspectFuncRead(p) + } + + mm_params := ReaderMockReadParams{p} + + // Record call args + mmRead.ReadMock.mutex.Lock() + mmRead.ReadMock.callArgs = append(mmRead.ReadMock.callArgs, &mm_params) + mmRead.ReadMock.mutex.Unlock() + + for _, e := range mmRead.ReadMock.expectations { + if minimock.Equal(*e.params, mm_params) { + mm_atomic.AddUint64(&e.Counter, 1) + return e.results.n, e.results.err + } + } + + if mmRead.ReadMock.defaultExpectation != nil { + mm_atomic.AddUint64(&mmRead.ReadMock.defaultExpectation.Counter, 1) + mm_want := mmRead.ReadMock.defaultExpectation.params + mm_want_ptrs := mmRead.ReadMock.defaultExpectation.paramPtrs + + mm_got := ReaderMockReadParams{p} + + if mm_want_ptrs != nil { + + if mm_want_ptrs.p != nil && !minimock.Equal(*mm_want_ptrs.p, mm_got.p) { + mmRead.t.Errorf("ReaderMock.Read got unexpected parameter p, want: %#v, got: %#v%s\n", *mm_want_ptrs.p, mm_got.p, minimock.Diff(*mm_want_ptrs.p, mm_got.p)) + } + + } else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) { + mmRead.t.Errorf("ReaderMock.Read got unexpected parameters, want: %#v, got: %#v%s\n", *mm_want, mm_got, minimock.Diff(*mm_want, mm_got)) + } + + mm_results := mmRead.ReadMock.defaultExpectation.results + if mm_results == nil { + mmRead.t.Fatal("No results are set for the ReaderMock.Read") + } + return (*mm_results).n, (*mm_results).err + } + if mmRead.funcRead != nil { + return mmRead.funcRead(p) + } + mmRead.t.Fatalf("Unexpected call to ReaderMock.Read. %v", p) + return +} + +// ReadAfterCounter returns a count of finished ReaderMock.Read invocations +func (mmRead *ReaderMock) ReadAfterCounter() uint64 { + return mm_atomic.LoadUint64(&mmRead.afterReadCounter) +} + +// ReadBeforeCounter returns a count of ReaderMock.Read invocations +func (mmRead *ReaderMock) ReadBeforeCounter() uint64 { + return mm_atomic.LoadUint64(&mmRead.beforeReadCounter) +} + +// Calls returns a list of arguments used in each call to ReaderMock.Read. +// The list is in the same order as the calls were made (i.e. recent calls have a higher index) +func (mmRead *mReaderMockRead) Calls() []*ReaderMockReadParams { + mmRead.mutex.RLock() + + argCopy := make([]*ReaderMockReadParams, len(mmRead.callArgs)) + copy(argCopy, mmRead.callArgs) + + mmRead.mutex.RUnlock() + + return argCopy +} + +// MinimockReadDone returns true if the count of the Read invocations corresponds +// the number of defined expectations +func (m *ReaderMock) MinimockReadDone() bool { + if m.ReadMock.optional { + // Optional methods provide '0 or more' call count restriction. + return true + } + + for _, e := range m.ReadMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + return false + } + } + + return m.ReadMock.invocationsDone() +} + +// MinimockReadInspect logs each unmet expectation +func (m *ReaderMock) MinimockReadInspect() { + for _, e := range m.ReadMock.expectations { + if mm_atomic.LoadUint64(&e.Counter) < 1 { + m.t.Errorf("Expected call to ReaderMock.Read with params: %#v", *e.params) + } + } + + afterReadCounter := mm_atomic.LoadUint64(&m.afterReadCounter) + // if default expectation was set then invocations count should be greater than zero + if m.ReadMock.defaultExpectation != nil && afterReadCounter < 1 { + if m.ReadMock.defaultExpectation.params == nil { + m.t.Error("Expected call to ReaderMock.Read") + } else { + m.t.Errorf("Expected call to ReaderMock.Read with params: %#v", *m.ReadMock.defaultExpectation.params) + } + } + // if func was set then invocations count should be greater than zero + if m.funcRead != nil && afterReadCounter < 1 { + m.t.Error("Expected call to ReaderMock.Read") + } + + if !m.ReadMock.invocationsDone() && afterReadCounter > 0 { + m.t.Errorf("Expected %d calls to ReaderMock.Read but found %d calls", + mm_atomic.LoadUint64(&m.ReadMock.expectedInvocations), afterReadCounter) + } +} + +// MinimockFinish checks that all mocked methods have been called the expected number of times +func (m *ReaderMock) MinimockFinish() { + m.finishOnce.Do(func() { + if !m.minimockDone() { + m.MinimockReadInspect() + } + }) +} + +// MinimockWait waits for all mocked methods to be called the expected number of times +func (m *ReaderMock) MinimockWait(timeout mm_time.Duration) { + timeoutCh := mm_time.After(timeout) + for { + if m.minimockDone() { + return + } + select { + case <-timeoutCh: + m.MinimockFinish() + return + case <-mm_time.After(10 * mm_time.Millisecond): + } + } +} + +func (m *ReaderMock) minimockDone() bool { + done := true + return done && + m.MinimockReadDone() +} diff --git a/tests/types.go b/tests/types.go index d5714cf..c4a37d1 100644 --- a/tests/types.go +++ b/tests/types.go @@ -3,6 +3,7 @@ package tests import ( "context" + "io" "google.golang.org/protobuf/proto" ) @@ -68,6 +69,12 @@ type ( Name(T, K) } + formatterAlias = Formatter + + formatterType Formatter + + reader = io.Reader + structArg struct { a int b string From 56a995be83f20f4308f4a4b0fe61af7a66a49a73 Mon Sep 17 00:00:00 2001 From: sleygin Date: Fri, 21 Jun 2024 00:55:43 +0300 Subject: [PATCH 2/6] made aliases generation as default behaviour --- cmd/minimock/minimock.go | 14 ++++++-------- internal/types/interface.go | 21 +++++++-------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/cmd/minimock/minimock.go b/cmd/minimock/minimock.go index d7d69b1..801ab0e 100644 --- a/cmd/minimock/minimock.go +++ b/cmd/minimock/minimock.go @@ -51,12 +51,11 @@ var helpers = template.FuncMap{ type ( options struct { - interfaces []interfaceInfo - noGenerate bool - generateFromAliases bool - suffix string - mockNames []string - packageNames []string + interfaces []interfaceInfo + noGenerate bool + suffix string + mockNames []string + packageNames []string } interfaceInfo struct { @@ -147,7 +146,7 @@ func run(opts *options) (err error) { } } - interfaces := types.FindAllInterfaces(astPackage, in.Type, opts.generateFromAliases) + interfaces := types.FindAllInterfaces(astPackage, in.Type) packageName := "" if len(opts.interfaces) == len(opts.packageNames) { @@ -384,7 +383,6 @@ func processArgs(args []string, stdout, stderr io.Writer) (*options, error) { fs.BoolVar(&opts.noGenerate, "g", false, "don't put go:generate instruction into the generated code") fs.StringVar(&opts.suffix, "s", "_mock_test.go", "mock file suffix") - fs.BoolVar(&opts.generateFromAliases, "from-aliases", false, "generate mocks from interface aliases as well") input := fs.String("i", "*", "comma-separated names of the interfaces to mock, i.e fmt.Stringer,io.Reader\nuse io.* notation to generate mocks for all interfaces in the \"io\" package") output := fs.String("o", "", "comma-separated destination file names or packages to put the generated mocks in,\nby default the generated mock is placed in the source package directory") diff --git a/internal/types/interface.go b/internal/types/interface.go index 393ce84..90bc459 100644 --- a/internal/types/interface.go +++ b/internal/types/interface.go @@ -25,12 +25,12 @@ type InterfaceSpecificationParam struct { ParamType string } -func FindAllInterfaces(p *ast.Package, pattern string, withAliases bool) []InterfaceSpecification { +func FindAllInterfaces(p *ast.Package, pattern string) []InterfaceSpecification { // Filter interfaces from all the declarations interfaces := []*ast.TypeSpec{} for _, file := range p.Files { for _, typeSpec := range findAllTypeSpecsInFile(file) { - if isInterface(typeSpec, file.Imports, withAliases) { + if isInterface(typeSpec, file.Imports) { interfaces = append(interfaces, typeSpec) } } @@ -56,17 +56,10 @@ func FindAllInterfaces(p *ast.Package, pattern string, withAliases bool) []Inter return interfaceSpecifications } -func isInterface(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec, withAliases bool) bool { +func isInterface(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { // we are generating mocks for interfaces, // interface aliases to types from the same package // and aliases to types from another package - if !withAliases { - return isInterfaceType(typeSpec) - } - return isInterfaceOrAlias(typeSpec, fileImports) -} - -func isInterfaceOrAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) bool { return isInterfaceType(typeSpec) || isInterfaceAlias(typeSpec, fileImports) || isExportedInterfaceAlias(typeSpec, fileImports) @@ -85,7 +78,7 @@ func isInterfaceAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportSpec) boo } if ts, ok := ident.Obj.Decl.(*ast.TypeSpec); ok { - return isInterfaceOrAlias(ts, fileImports) + return isInterface(ts, fileImports) } return false @@ -110,11 +103,11 @@ func isExportedInterfaceAlias(typeSpec *ast.TypeSpec, fileImports []*ast.ImportS return false } - typeSpec, imports := findTypeSoecInPackage(srcAst, selector.Sel.Name) + typeSpec, imports := findTypeSpecInPackage(srcAst, selector.Sel.Name) // we have to check recursively because checked typed might be // another alias to other interface - return isInterfaceOrAlias(typeSpec, imports) + return isInterface(typeSpec, imports) } func getPackageAst(packagePath string) (*ast.Package, error) { @@ -132,7 +125,7 @@ func getPackageAst(packagePath string) (*ast.Package, error) { return srcAst, nil } -func findTypeSoecInPackage(p *ast.Package, name string) (typeSpec *ast.TypeSpec, imports []*ast.ImportSpec) { +func findTypeSpecInPackage(p *ast.Package, name string) (typeSpec *ast.TypeSpec, imports []*ast.ImportSpec) { for _, f := range p.Files { if f == nil { continue From 353dc0cdeb076f0945498fa5e99a6efea217011b Mon Sep 17 00:00:00 2001 From: sleygin Date: Fri, 21 Jun 2024 00:57:13 +0300 Subject: [PATCH 3/6] made aliases generation as default behaviour --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 022f0ad..7a6d4df 100644 --- a/Makefile +++ b/Makefile @@ -20,9 +20,9 @@ generate: go run ./cmd/minimock/minimock.go -i ./tests.contextAccepter -o ./tests/context_accepter_mock.go go run ./cmd/minimock/minimock.go -i github.com/gojuno/minimock/v3.Tester -o ./tests/package_name_specified_test.go -p tests_test go run ./cmd/minimock/minimock.go -i ./tests.actor -o ./tests/actor_mock.go - go run ./cmd/minimock/minimock.go -i ./tests.formatterAlias -o ./tests/formatter_alias_mock.go -from-aliases - go run ./cmd/minimock/minimock.go -i ./tests.formatterType -o ./tests/formatter_type_mock.go -from-aliases - go run ./cmd/minimock/minimock.go -i ./tests.reader -o ./tests/reader_mock.go -from-aliases + go run ./cmd/minimock/minimock.go -i ./tests.formatterAlias -o ./tests/formatter_alias_mock.go + go run ./cmd/minimock/minimock.go -i ./tests.formatterType -o ./tests/formatter_type_mock.go + go run ./cmd/minimock/minimock.go -i ./tests.reader -o ./tests/reader_mock.go ./bin: mkdir ./bin From 4b3b584898aea901eeb96c7dc9e3232a44359e3c Mon Sep 17 00:00:00 2001 From: sleygin Date: Fri, 21 Jun 2024 00:58:47 +0300 Subject: [PATCH 4/6] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a88f2fd..2d980e7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ The main features of minimock are: * It supports generics. * It works well with [table driven tests](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) because you can set up mocks for several methods in one line of code using the builder pattern. * It can generate several mocks in one run. +* It can generate mocks from interface aliases. * It generates code that passes default set of [golangci-lint](https://github.com/golangci/golangci-lint) checks. * It puts //go:generate instruction into the generated code, so all you need to do when the source interface is updated is to run the `go generate ./...` command from within the project's directory. * It makes sure that all mocked methods have been called during the test and keeps your test code clean and up to date. From f80adb07ecc97d6541d2c44729618f2fef0b1247 Mon Sep 17 00:00:00 2001 From: sleygin Date: Sun, 23 Jun 2024 23:10:16 +0300 Subject: [PATCH 5/6] gowrap updated --- go.mod | 4 +--- go.sum | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d68744f..762caa9 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/gojuno/minimock/v3 require ( github.com/davecgh/go-spew v1.1.1 - github.com/hexdigest/gowrap v1.3.7 + github.com/hexdigest/gowrap v1.3.10 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/stretchr/testify v1.8.4 @@ -22,5 +22,3 @@ require ( go 1.22 toolchain go1.22.0 - -replace github.com/hexdigest/gowrap v1.3.7 => ../gowrap diff --git a/go.sum b/go.sum index ddcb28a..9610285 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/hexdigest/gowrap v1.3.10 h1:e8NwAdtnwpRJ4Ks76+GctDNCFIkHR2TCFCWshug/zIE= +github.com/hexdigest/gowrap v1.3.10/go.mod h1:5KTYxPjK1RRfD+9L4Oo9gjP3XNAs4rkoVK2E7eAEFyM= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= From 92d336222a318cffe557b9114f9abfaf467058b6 Mon Sep 17 00:00:00 2001 From: sleygin Date: Fri, 28 Jun 2024 00:27:05 +0300 Subject: [PATCH 6/6] fixed panic when using expect params on pointer to struct --- equal.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/equal.go b/equal.go index 847d2ec..80e9ecb 100644 --- a/equal.go +++ b/equal.go @@ -64,6 +64,7 @@ func Diff(e, a interface{}) string { return "" } + initialKind := k if k == reflect.Ptr { t = t.Elem() k = t.Kind() @@ -73,7 +74,7 @@ func Diff(e, a interface{}) string { return "" } - if k == reflect.Struct { + if initialKind == reflect.Struct { a = setAnyContext(e, a) }