diff --git a/docs/configuration.md b/docs/configuration.md index 44454288..0353f579 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -96,7 +96,8 @@ These are the config options when using the `packages` config option. Use of the | name | description | |------|-------------| - | InterfaceDir | The path of the original interface being mocked. This can be used as
`#!yaml dir: "{{.InterfaceDir}}"` to place your mocks adjacent to the original interface. This should not be used for external interfaces. | + | InterfaceDir | The directory path of the original interface being mocked. This can be used as
`#!yaml dir: "{{.InterfaceDir}}"` to place your mocks adjacent to the original interface. This should not be used for external interfaces. | + | InterfaceDirRelative | The directory path of the original interface being mocked, relative to the current working directory. If the path cannot be made relative to the current working directory, this variable will be set equal to `PackagePath` | | InterfaceName | The name of the original interface being mocked | | InterfaceNameCamel | Converts a string `interface_name` to `InterfaceName` | | InterfaceNameLowerCamel | Converts `InterfaceName` to `interfaceName` | diff --git a/go.mod b/go.mod index c99d0288..17c630f1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/vektra/mockery/v2 go 1.19 require ( - github.com/chigopher/pathlib v0.13.0 + github.com/chigopher/pathlib v1.0.0 github.com/iancoleman/strcase v0.2.0 github.com/jinzhu/copier v0.3.5 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.work.sum b/go.work.sum index 072c170c..1ec33e6d 100644 --- a/go.work.sum +++ b/go.work.sum @@ -294,6 +294,8 @@ github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnd github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chigopher/pathlib v1.0.0 h1:SbsCrFX4vDf4M2d8mT/RTzuVlKOjTKoPHK0HidsQFak= +github.com/chigopher/pathlib v1.0.0/go.mod h1:3+YPPV21mU9vyw8Mjp+F33CyCfE6iOzinpiqBcccv7I= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= diff --git a/pkg/outputter.go b/pkg/outputter.go index f4a8c107..fc1ced0b 100644 --- a/pkg/outputter.go +++ b/pkg/outputter.go @@ -144,9 +144,29 @@ func parseConfigTemplates(ctx context.Context, c *config.Config, iface *Interfac } else { mock = "mock" } + + workingDir, err := os.Getwd() + if err != nil { + return fmt.Errorf("get working directory: %w", err) + } + var interfaceDirRelative string + interfaceDir := pathlib.NewPath(iface.FileName).Parent() + interfaceDirRelativePath, err := interfaceDir.RelativeToStr(workingDir) + if errors.Is(err, pathlib.ErrRelativeTo) { + log.Debug(). + Stringer("interface-dir", interfaceDir). + Str("working-dir", workingDir). + Msg("can't make interfaceDir relative to working dir. Setting InterfaceDirRelative to package path.") + + interfaceDirRelative = iface.Pkg.Path() + } else { + interfaceDirRelative = interfaceDirRelativePath.String() + } + // data is the struct sent to the template parser data := struct { InterfaceDir string + InterfaceDirRelative string InterfaceName string InterfaceNameCamel string InterfaceNameLowerCamel string @@ -157,6 +177,7 @@ func parseConfigTemplates(ctx context.Context, c *config.Config, iface *Interfac PackagePath string }{ InterfaceDir: filepath.Dir(iface.FileName), + InterfaceDirRelative: interfaceDirRelative, InterfaceName: iface.Name, InterfaceNameCamel: strcase.ToCamel(iface.Name), InterfaceNameLowerCamel: strcase.ToLowerCamel(iface.Name), @@ -166,8 +187,6 @@ func parseConfigTemplates(ctx context.Context, c *config.Config, iface *Interfac PackageName: iface.Pkg.Name(), PackagePath: iface.Pkg.Path(), } - templ := template.New("interface-template") - // These are the config options that we allow // to be parsed by the templater. The keys are // just labels we're using for logs/errors @@ -197,7 +216,7 @@ func parseConfigTemplates(ctx context.Context, c *config.Config, iface *Interfac for name, attributePointer := range templateMap { oldVal := *attributePointer - attributeTempl, err := templ.Parse(*attributePointer) + attributeTempl, err := template.New("interface-template").Parse(*attributePointer) if err != nil { return fmt.Errorf("failed to parse %s template: %w", name, err) } diff --git a/pkg/outputter_test.go b/pkg/outputter_test.go index 5015c331..9a68c697 100644 --- a/pkg/outputter_test.go +++ b/pkg/outputter_test.go @@ -4,10 +4,12 @@ import ( "context" "errors" "fmt" + "os" "reflect" "testing" "github.com/chigopher/pathlib" + "github.com/davecgh/go-spew/spew" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" pkgMocks "github.com/vektra/mockery/v2/mocks/github.com/vektra/mockery/v2/pkg" @@ -65,6 +67,15 @@ func TestUnderscoreCaseName(t *testing.T) { } func Test_parseConfigTemplates(t *testing.T) { + mockPkg := func(t *testing.T) *pkgMocks.TypesPackage { + m := pkgMocks.NewTypesPackage(t) + m.EXPECT().Path().Return("github.com/user/project/package") + m.EXPECT().Name().Return("packageName") + return m + } + cwd, err := os.Getwd() + require.NoError(t, err) + type args struct { c *config.Config iface *Interface @@ -96,12 +107,7 @@ func Test_parseConfigTemplates(t *testing.T) { FileName: "path/to/foobar.go", }, }, - pkg: func(t *testing.T) *pkgMocks.TypesPackage { - m := pkgMocks.NewTypesPackage(t) - m.EXPECT().Path().Return("github.com/user/project/package") - m.EXPECT().Name().Return("packageName") - return m - }, + pkg: mockPkg, want: &config.Config{ Dir: "path/to/github.com/user/project/package", FileName: "FooBar_FooBar_foo_bar.go", @@ -109,6 +115,40 @@ func Test_parseConfigTemplates(t *testing.T) { Outpkg: "packageName", }, }, + { + name: "InterfaceDirRelative in current working directory", + args: args{ + c: &config.Config{ + Dir: "{{.InterfaceDirRelative}}", + }, + + iface: &Interface{ + Name: "FooBar", + FileName: cwd + "/path/to/foobar.go", + }, + }, + pkg: mockPkg, + want: &config.Config{ + Dir: "path/to", + }, + }, + { + name: "InterfaceDirRelative not in current working directory", + args: args{ + c: &config.Config{ + Dir: "mocks/{{.InterfaceDirRelative}}", + }, + + iface: &Interface{ + Name: "FooBar", + FileName: "/path/to/foobar.go", + }, + }, + pkg: mockPkg, + want: &config.Config{ + Dir: "mocks/github.com/user/project/package", + }, + }, { name: "infinite loop in template variables", args: args{ @@ -124,12 +164,7 @@ func Test_parseConfigTemplates(t *testing.T) { FileName: "path/to/foobar.go", }, }, - pkg: func(t *testing.T) *pkgMocks.TypesPackage { - m := pkgMocks.NewTypesPackage(t) - m.EXPECT().Path().Return("github.com/user/project/package") - m.EXPECT().Name().Return("packageName") - return m - }, + pkg: mockPkg, disableWantCheck: true, wantErr: ErrInfiniteLoop, }, @@ -143,7 +178,7 @@ func Test_parseConfigTemplates(t *testing.T) { t.Errorf("parseConfigTemplates() error = %v, wantErr %v", err, tt.wantErr) } if !tt.disableWantCheck && !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("*config.Config = %v, want %v", tt.args.c, tt.want) + t.Errorf("*config.Config = %s\n, want %+v", spew.Sdump(tt.args.c), spew.Sdump(tt.want)) } }) } @@ -173,6 +208,7 @@ func TestOutputter_Generate(t *testing.T) { tt.fields.config.Dir = t.TempDir() tt.fields.config.MockName = "Mock{{.InterfaceName}}" tt.fields.config.FileName = "mock_{{.InterfaceName}}.go" + tt.fields.config.Outpkg = "{{.PackageName}}" t.Run(tt.name, func(t *testing.T) { m := &Outputter{