From 1c18f4456d8a9638702a7aff669d88c2143498c1 Mon Sep 17 00:00:00 2001 From: Vitaliy Solodilov Date: Wed, 16 Aug 2023 13:44:38 +0300 Subject: [PATCH 1/2] Generate mock build constraints Supported only the new format of build tags https://pkg.go.dev/cmd/go#hdr-Build_constraints Fixes #691 --- .mockery.yaml | 7 + cmd/mockery.go | 4 +- docs/configuration.md | 3 +- .../IfaceWithCustomBuildTagInComment.go | 123 ++++++++++++++++++ pkg/config/config.go | 1 + .../buildtag/comment/custom2_iface.go | 1 + pkg/generator.go | 8 ++ pkg/generator_test.go | 11 ++ pkg/outputter.go | 1 + pkg/walker.go | 2 + 10 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 mocks/github.com/vektra/mockery/v2/pkg/fixtures/buildtag/comment/IfaceWithCustomBuildTagInComment.go diff --git a/.mockery.yaml b/.mockery.yaml index 25757954..2ae36ff0 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -4,7 +4,14 @@ with-expecter: True mockname: "{{.InterfaceName}}" filename: "{{.MockName}}.go" outpkg: mocks +tags: "custom2" packages: + github.com/vektra/mockery/v2/pkg/fixtures/buildtag/comment: + config: + mock-build-tags: "custom3 && (!windows || !darwin || !freebsd)" + disable-version-string: true + interfaces: + IfaceWithCustomBuildTagInComment: github.com/vektra/mockery/v2/pkg: interfaces: TypesPackage: diff --git a/cmd/mockery.go b/cmd/mockery.go index 922a16a8..da748314 100644 --- a/cmd/mockery.go +++ b/cmd/mockery.go @@ -70,7 +70,8 @@ func NewRootCmd() *cobra.Command { pFlags.Bool("version", false, "prints the installed version of mockery") pFlags.Bool("quiet", false, `suppresses logger output (equivalent to --log-level="")`) pFlags.Bool("keeptree", false, "keep the tree structure of the original interface files into a different repository. Must be used with XX") - pFlags.String("tags", "", "space-separated list of additional build tags to use") + pFlags.String("tags", "", "space-separated list of additional build tags to load packages") + pFlags.String("mock-build-tags", "", "set the build tags of the generated mocks. Read more about the format: https://pkg.go.dev/cmd/go#hdr-Build_constraints") pFlags.String("filename", "", "name of generated file (only works with -name and no regex)") pFlags.String("structname", "", "name of generated struct (only works with -name and no regex)") pFlags.String("log-level", "info", "Level of logging") @@ -376,6 +377,7 @@ func (r *RootApp) Run() error { InPackage: r.Config.InPackage, KeepTree: r.Config.KeepTree, Note: r.Config.Note, + MockBuildTags: r.Config.MockBuildTags, PackageName: r.Config.Outpkg, PackageNamePrefix: r.Config.Packageprefix, StructName: r.Config.StructName, diff --git a/docs/configuration.md b/docs/configuration.md index 473b9b02..8a54f859 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -78,7 +78,8 @@ Parameter Descriptions | [`packages`](features.md#packages-configuration) | :fontawesome-solid-x: | `#!yaml null` | A dictionary containing configuration describing the packages and interfaces to generate mocks for. | | `print` | :fontawesome-solid-x: | `#!yaml false` | Use `print: True` to have the resulting code printed out instead of written to disk. | | [`recursive`](features.md#recursive-package-discovery) | :fontawesome-solid-x: | `#!yaml false` | When set to `true` on a particular package, mockery will recursively search for all sub-packages and inject those packages into the config map. | -| `tags` | :fontawesome-solid-x: | `#!yaml ""` | Set the build tags of the generated mocks. | +| `tags` | :fontawesome-solid-x: | `#!yaml ""` | A space-separated list of additional build tags to load packages. | +| `mock-build-tags` | :fontawesome-solid-x: | `#!yaml ""` | Set the build tags of the generated mocks. Read more about the [format](https://pkg.go.dev/cmd/go#hdr-Build_constraints). | | [`with-expecter`](features.md#expecter-structs) | :fontawesome-solid-x: | `#!yaml true` | Use `with-expecter: True` to generate `EXPECT()` methods for your mocks. This is the preferred way to setup your mocks. | | [`replace-type`](features.md#replace-types) | :fontawesome-solid-x: | `#!yaml null` | Replaces aliases, packages and/or types during generation. | diff --git a/mocks/github.com/vektra/mockery/v2/pkg/fixtures/buildtag/comment/IfaceWithCustomBuildTagInComment.go b/mocks/github.com/vektra/mockery/v2/pkg/fixtures/buildtag/comment/IfaceWithCustomBuildTagInComment.go new file mode 100644 index 00000000..73bc3023 --- /dev/null +++ b/mocks/github.com/vektra/mockery/v2/pkg/fixtures/buildtag/comment/IfaceWithCustomBuildTagInComment.go @@ -0,0 +1,123 @@ +// Code generated by mockery. DO NOT EDIT. + +//go:build custom3 && (!windows || !darwin || !freebsd) + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// IfaceWithCustomBuildTagInComment is an autogenerated mock type for the IfaceWithCustomBuildTagInComment type +type IfaceWithCustomBuildTagInComment struct { + mock.Mock +} + +type IfaceWithCustomBuildTagInComment_Expecter struct { + mock *mock.Mock +} + +func (_m *IfaceWithCustomBuildTagInComment) EXPECT() *IfaceWithCustomBuildTagInComment_Expecter { + return &IfaceWithCustomBuildTagInComment_Expecter{mock: &_m.Mock} +} + +// Custom2 provides a mock function with given fields: +func (_m *IfaceWithCustomBuildTagInComment) Custom2() { + _m.Called() +} + +// IfaceWithCustomBuildTagInComment_Custom2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Custom2' +type IfaceWithCustomBuildTagInComment_Custom2_Call struct { + *mock.Call +} + +// Custom2 is a helper method to define mock.On call +func (_e *IfaceWithCustomBuildTagInComment_Expecter) Custom2() *IfaceWithCustomBuildTagInComment_Custom2_Call { + return &IfaceWithCustomBuildTagInComment_Custom2_Call{Call: _e.mock.On("Custom2")} +} + +func (_c *IfaceWithCustomBuildTagInComment_Custom2_Call) Run(run func()) *IfaceWithCustomBuildTagInComment_Custom2_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *IfaceWithCustomBuildTagInComment_Custom2_Call) Return() *IfaceWithCustomBuildTagInComment_Custom2_Call { + _c.Call.Return() + return _c +} + +func (_c *IfaceWithCustomBuildTagInComment_Custom2_Call) RunAndReturn(run func()) *IfaceWithCustomBuildTagInComment_Custom2_Call { + _c.Call.Return(run) + return _c +} + +// Sprintf provides a mock function with given fields: format, a +func (_m *IfaceWithCustomBuildTagInComment) Sprintf(format string, a ...interface{}) string { + var _ca []interface{} + _ca = append(_ca, format) + _ca = append(_ca, a...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Sprintf") + } + + var r0 string + if rf, ok := ret.Get(0).(func(string, ...interface{}) string); ok { + r0 = rf(format, a...) + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// IfaceWithCustomBuildTagInComment_Sprintf_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Sprintf' +type IfaceWithCustomBuildTagInComment_Sprintf_Call struct { + *mock.Call +} + +// Sprintf is a helper method to define mock.On call +// - format string +// - a ...interface{} +func (_e *IfaceWithCustomBuildTagInComment_Expecter) Sprintf(format interface{}, a ...interface{}) *IfaceWithCustomBuildTagInComment_Sprintf_Call { + return &IfaceWithCustomBuildTagInComment_Sprintf_Call{Call: _e.mock.On("Sprintf", + append([]interface{}{format}, a...)...)} +} + +func (_c *IfaceWithCustomBuildTagInComment_Sprintf_Call) Run(run func(format string, a ...interface{})) *IfaceWithCustomBuildTagInComment_Sprintf_Call { + _c.Call.Run(func(args mock.Arguments) { + variadicArgs := make([]interface{}, len(args)-1) + for i, a := range args[1:] { + if a != nil { + variadicArgs[i] = a.(interface{}) + } + } + run(args[0].(string), variadicArgs...) + }) + return _c +} + +func (_c *IfaceWithCustomBuildTagInComment_Sprintf_Call) Return(_a0 string) *IfaceWithCustomBuildTagInComment_Sprintf_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *IfaceWithCustomBuildTagInComment_Sprintf_Call) RunAndReturn(run func(string, ...interface{}) string) *IfaceWithCustomBuildTagInComment_Sprintf_Call { + _c.Call.Return(run) + return _c +} + +// NewIfaceWithCustomBuildTagInComment creates a new instance of IfaceWithCustomBuildTagInComment. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIfaceWithCustomBuildTagInComment(t interface { + mock.TestingT + Cleanup(func()) +}) *IfaceWithCustomBuildTagInComment { + mock := &IfaceWithCustomBuildTagInComment{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 2152d662..13d457f5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -33,6 +33,7 @@ type Interface struct { type Config struct { All bool `mapstructure:"all"` + MockBuildTags string `mapstructure:"mock-build-tags"` BuildTags string `mapstructure:"tags"` Case string `mapstructure:"case"` Config string `mapstructure:"config"` diff --git a/pkg/fixtures/buildtag/comment/custom2_iface.go b/pkg/fixtures/buildtag/comment/custom2_iface.go index 08bd30d6..02686a26 100644 --- a/pkg/fixtures/buildtag/comment/custom2_iface.go +++ b/pkg/fixtures/buildtag/comment/custom2_iface.go @@ -5,4 +5,5 @@ package comment type IfaceWithCustomBuildTagInComment interface { Sprintf(format string, a ...interface{}) string + Custom2() } diff --git a/pkg/generator.go b/pkg/generator.go index 5cfacc73..6a9d9034 100644 --- a/pkg/generator.go +++ b/pkg/generator.go @@ -57,6 +57,7 @@ type GeneratorConfig struct { InPackage bool KeepTree bool Note string + MockBuildTags string PackageName string PackageNamePrefix string StructName string @@ -111,6 +112,7 @@ func NewGenerator(ctx context.Context, c GeneratorConfig, iface *Interface, pkg func (g *Generator) GenerateAll(ctx context.Context) error { g.GenerateBoilerplate(g.config.Boilerplate) g.GeneratePrologueNote(g.config.Note) + g.GenerateBuildTags(g.config.MockBuildTags) g.GeneratePrologue(ctx, g.pkg) return g.Generate(ctx) } @@ -418,6 +420,12 @@ func (g *Generator) GenerateBoilerplate(boilerplate string) { } } +func (g *Generator) GenerateBuildTags(buildTags string) { + if buildTags != "" { + g.printf("//go:build %s\n\n", buildTags) + } +} + // ErrNotInterface is returned when the given type is not an interface // type. var ErrNotInterface = errors.New("expression not an interface") diff --git a/pkg/generator_test.go b/pkg/generator_test.go index 5ea54b5b..283337fb 100644 --- a/pkg/generator_test.go +++ b/pkg/generator_test.go @@ -296,6 +296,17 @@ func (s *GeneratorSuite) TestGeneratorBoilerplate() { s.Equal(expected, generator.buf.String()) } +func (s *GeneratorSuite) TestGeneratorBuildTags() { + generator := s.getGenerator(testFile, "Requester", false, "") + generator.GenerateBuildTags("custom && (!windows || !linux)") + + expected := `//go:build custom && (!windows || !linux) + +` + + s.Equal(expected, generator.buf.String()) +} + func (s *GeneratorSuite) TestGeneratorPrologueNoteNoVersionString() { generator := s.getGenerator(testFile, "Requester", false, "") generator.config.DisableVersionString = true diff --git a/pkg/outputter.go b/pkg/outputter.go index 2f1a8192..1960be9b 100644 --- a/pkg/outputter.go +++ b/pkg/outputter.go @@ -336,6 +336,7 @@ func (m *Outputter) Generate(ctx context.Context, iface *Interface) error { InPackage: interfaceConfig.InPackage, KeepTree: interfaceConfig.KeepTree, Note: interfaceConfig.Note, + MockBuildTags: interfaceConfig.MockBuildTags, PackageName: interfaceConfig.Outpkg, PackageNamePrefix: interfaceConfig.Packageprefix, StructName: interfaceConfig.MockName, diff --git a/pkg/walker.go b/pkg/walker.go index 61d33c7f..34bab4e0 100644 --- a/pkg/walker.go +++ b/pkg/walker.go @@ -120,6 +120,7 @@ type GeneratorVisitorConfig struct { InPackage bool KeepTree bool Note string + MockBuildTags string // The name of the output package, if InPackage is false (defaults to "mocks") PackageName string PackageNamePrefix string @@ -181,6 +182,7 @@ func (v *GeneratorVisitor) VisitWalk(ctx context.Context, iface *Interface) erro InPackage: v.config.InPackage, KeepTree: v.config.KeepTree, Note: v.config.Note, + MockBuildTags: v.config.MockBuildTags, PackageName: v.config.PackageName, PackageNamePrefix: v.config.PackageNamePrefix, StructName: v.config.StructName, From 58b0c4ce7fe5389801ddf98d449e821b96d89031 Mon Sep 17 00:00:00 2001 From: Vitaliy Solodilov Date: Thu, 11 Jan 2024 01:20:43 +0300 Subject: [PATCH 2/2] code review fixes --- docs/configuration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 8a54f859..c3bfcd6b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -72,16 +72,16 @@ Parameter Descriptions | `include-auto-generated` | :fontawesome-solid-x: | `#!yaml true` | Set to `#!yaml false` if you need mockery to skip auto-generated files during its recursive package discovery. When set to `#!yaml true`, mockery includes auto-generated files when determining if a particular directory is an importable package. | | `include-regex` | :fontawesome-solid-x: | `#!yaml ""` | When set, only interface names that match the expression will be generated. This setting is ignored if `all: True` is specified in the configuration. To further refine the interfaces generated, use `exclude-regex`. | | `inpackage` | :fontawesome-solid-x: | `#!yaml false` | When generating mocks alongside the original interfaces, you must specify `inpackage: True` to inform mockery that the mock is being placed in the same package as the original interface. | +| `log-level` | :fontawesome-solid-x: | `#!yaml "info"` | Set the level of the logger | +| `mock-build-tags` | :fontawesome-solid-x: | `#!yaml ""` | Set the build tags of the generated mocks. Read more about the [format](https://pkg.go.dev/cmd/go#hdr-Build_constraints). | | `mockname` | :fontawesome-solid-check: | `#!yaml "Mock{{.InterfaceName}}"` | The name of the generated mock. | | `outpkg` | :fontawesome-solid-check: | `#!yaml "{{.PackageName}}"` | Use `outpkg` to specify the package name of the generated mocks. | -| `log-level` | :fontawesome-solid-x: | `#!yaml "info"` | Set the level of the logger | | [`packages`](features.md#packages-configuration) | :fontawesome-solid-x: | `#!yaml null` | A dictionary containing configuration describing the packages and interfaces to generate mocks for. | | `print` | :fontawesome-solid-x: | `#!yaml false` | Use `print: True` to have the resulting code printed out instead of written to disk. | | [`recursive`](features.md#recursive-package-discovery) | :fontawesome-solid-x: | `#!yaml false` | When set to `true` on a particular package, mockery will recursively search for all sub-packages and inject those packages into the config map. | +| [`replace-type`](features.md#replace-types) | :fontawesome-solid-x: | `#!yaml null` | Replaces aliases, packages and/or types during generation. | | `tags` | :fontawesome-solid-x: | `#!yaml ""` | A space-separated list of additional build tags to load packages. | -| `mock-build-tags` | :fontawesome-solid-x: | `#!yaml ""` | Set the build tags of the generated mocks. Read more about the [format](https://pkg.go.dev/cmd/go#hdr-Build_constraints). | | [`with-expecter`](features.md#expecter-structs) | :fontawesome-solid-x: | `#!yaml true` | Use `with-expecter: True` to generate `EXPECT()` methods for your mocks. This is the preferred way to setup your mocks. | -| [`replace-type`](features.md#replace-types) | :fontawesome-solid-x: | `#!yaml null` | Replaces aliases, packages and/or types during generation. | Layouts -------