diff --git a/mockery/fixtures/buildtag/comment/darwin_iface.go b/mockery/fixtures/buildtag/comment/darwin_iface.go new file mode 100644 index 00000000..370f61fd --- /dev/null +++ b/mockery/fixtures/buildtag/comment/darwin_iface.go @@ -0,0 +1,7 @@ +// +build darwin + +package comment + +type IfaceWithBuildTagInComment interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/comment/freebsd_iface.go b/mockery/fixtures/buildtag/comment/freebsd_iface.go new file mode 100644 index 00000000..21ddbb72 --- /dev/null +++ b/mockery/fixtures/buildtag/comment/freebsd_iface.go @@ -0,0 +1,7 @@ +// +build freebsd + +package comment + +type IfaceWithBuildTagInComment interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/comment/linux_iface.go b/mockery/fixtures/buildtag/comment/linux_iface.go new file mode 100644 index 00000000..26fef160 --- /dev/null +++ b/mockery/fixtures/buildtag/comment/linux_iface.go @@ -0,0 +1,7 @@ +// +build linux + +package comment + +type IfaceWithBuildTagInComment interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/comment/windows_iface.go b/mockery/fixtures/buildtag/comment/windows_iface.go new file mode 100644 index 00000000..44bb1ed7 --- /dev/null +++ b/mockery/fixtures/buildtag/comment/windows_iface.go @@ -0,0 +1,7 @@ +// +build windows + +package comment + +type IfaceWithBuildTagInComment interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/filename/iface_darwin.go b/mockery/fixtures/buildtag/filename/iface_darwin.go new file mode 100644 index 00000000..47f45611 --- /dev/null +++ b/mockery/fixtures/buildtag/filename/iface_darwin.go @@ -0,0 +1,5 @@ +package filename + +type IfaceWithBuildTagInFilename interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/filename/iface_freebsd.go b/mockery/fixtures/buildtag/filename/iface_freebsd.go new file mode 100644 index 00000000..47f45611 --- /dev/null +++ b/mockery/fixtures/buildtag/filename/iface_freebsd.go @@ -0,0 +1,5 @@ +package filename + +type IfaceWithBuildTagInFilename interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/filename/iface_linux.go b/mockery/fixtures/buildtag/filename/iface_linux.go new file mode 100644 index 00000000..47f45611 --- /dev/null +++ b/mockery/fixtures/buildtag/filename/iface_linux.go @@ -0,0 +1,5 @@ +package filename + +type IfaceWithBuildTagInFilename interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/fixtures/buildtag/filename/iface_windows.go b/mockery/fixtures/buildtag/filename/iface_windows.go new file mode 100644 index 00000000..47f45611 --- /dev/null +++ b/mockery/fixtures/buildtag/filename/iface_windows.go @@ -0,0 +1,5 @@ +package filename + +type IfaceWithBuildTagInFilename interface { + Sprintf(format string, a ...interface{}) string +} diff --git a/mockery/mockery_test.go b/mockery/mockery_test.go new file mode 100644 index 00000000..bcce2ce6 --- /dev/null +++ b/mockery/mockery_test.go @@ -0,0 +1,24 @@ +package mockery + +import ( + "os" + "path/filepath" +) + +var fixturePath string + +func init() { + dir, err := os.Getwd() + if err != nil { + panic(err) + } + fixturePath = filepath.Join(dir, "fixtures") +} + +// getFixturePath returns an absolute path to a fixture sub-directory or file. +// +// getFixturePath("src.go") returns "/path/to/fixtures/src.go" +// getFixturePath("a", "b", "c", "src.go") returns "/path/to/fixtures/a/b/c/src.go" +func getFixturePath(subdirOrBasename ...string) string { + return filepath.Join(append([]string{fixturePath}, subdirOrBasename...)...) +} diff --git a/mockery/parse.go b/mockery/parse.go index 187a0fbc..0ec218ca 100644 --- a/mockery/parse.go +++ b/mockery/parse.go @@ -2,15 +2,17 @@ package mockery import ( "go/ast" + "go/build" "go/importer" "go/types" "io/ioutil" "path/filepath" "strings" - "golang.org/x/tools/go/loader" "sort" "sync" + + "golang.org/x/tools/go/loader" ) type Parser struct { @@ -27,6 +29,12 @@ func NewParser() *Parser { conf.TypeCheckFuncBodies = func(_ string) bool { return false } conf.TypeChecker.DisableUnusedImportCheck = true conf.TypeChecker.Importer = importer.Default() + + // Initialize the build context (e.g. GOARCH/GOOS fields) so we can use it for respecting + // build tags during Parse. + defaultBuildCtx := build.Default + conf.Build = &defaultBuildCtx + return &Parser{ parserPackages: make([]*types.Package, 0), configMapping: make(map[string][]*ast.File), @@ -37,6 +45,7 @@ func NewParser() *Parser { } func (p *Parser) Parse(path string) error { + // To support relative paths to mock targets w/ vendor deps, we need to provide eventual // calls to build.Context.Import with an absolute path. It needs to be absolute because // Import will only find the vendor directory if our target path for parsing is under @@ -61,10 +70,25 @@ func (p *Parser) Parse(path string) error { continue } - fpath := filepath.Join(dir, fi.Name()) - f, err := p.conf.ParseFile(fpath, nil) - if err != nil { - return err + fname := fi.Name() + fpath := filepath.Join(dir, fname) + + // If go/build would ignore this file, e.g. based on build tags, also ignore it here. + // + // (Further coupling with go internals and x/tools may of course bear a cost eventually + // e.g. https://github.com/vektra/mockery/pull/117#issue-199337071, but should add + // worthwhile consistency in this tool's behavior in the meantime.) + match, matchErr := p.conf.Build.MatchFile(dir, fname) + if matchErr != nil { + return matchErr + } + if !match { + continue + } + + f, parseErr := p.conf.ParseFile(fpath, nil) + if parseErr != nil { + return parseErr } p.configMapping[path] = append(p.configMapping[path], f) diff --git a/mockery/parse_test.go b/mockery/parse_test.go index 37ee44e0..160c1892 100644 --- a/mockery/parse_test.go +++ b/mockery/parse_test.go @@ -1,27 +1,17 @@ package mockery import ( - "os" - "path/filepath" "testing" "github.com/stretchr/testify/assert" ) -var fixturePath string var testFile string var testFile2 string func init() { - dir, err := os.Getwd() - if err != nil { - panic(err) - } - - fixturePath = filepath.Join(dir, "fixtures") - - testFile = filepath.Join(dir, "fixtures", "requester.go") - testFile2 = filepath.Join(dir, "fixtures", "requester2.go") + testFile = getFixturePath("requester.go") + testFile2 = getFixturePath("requester2.go") } func TestFileParse(t *testing.T) { @@ -51,3 +41,47 @@ func noTestFileInterfaces(t *testing.T) { assert.Equal(t, 1, len(nodes)) assert.Equal(t, "Requester", nodes[0].Name) } + +func TestBuildTagInFilename(t *testing.T) { + parser := NewParser() + + // Include the major OS values found on https://golang.org/dl/ so we're likely to match + // anywhere the test is executed. + err := parser.Parse(getFixturePath("buildtag", "filename", "iface_windows.go")) + assert.NoError(t, err) + err = parser.Parse(getFixturePath("buildtag", "filename", "iface_linux.go")) + assert.NoError(t, err) + err = parser.Parse(getFixturePath("buildtag", "filename", "iface_darwin.go")) + assert.NoError(t, err) + err = parser.Parse(getFixturePath("buildtag", "filename", "iface_freebsd.go")) + assert.NoError(t, err) + + err = parser.Load() + assert.NoError(t, err) // Expect "redeclared in this block" if tags aren't respected + + nodes := parser.Interfaces() + assert.Equal(t, 1, len(nodes)) + assert.Equal(t, "IfaceWithBuildTagInFilename", nodes[0].Name) +} + +func TestBuildTagInComment(t *testing.T) { + parser := NewParser() + + // Include the major OS values found on https://golang.org/dl/ so we're likely to match + // anywhere the test is executed. + err := parser.Parse(getFixturePath("buildtag", "comment", "windows_iface.go")) + assert.NoError(t, err) + err = parser.Parse(getFixturePath("buildtag", "comment", "linux_iface.go")) + assert.NoError(t, err) + err = parser.Parse(getFixturePath("buildtag", "comment", "darwin_iface.go")) + assert.NoError(t, err) + err = parser.Parse(getFixturePath("buildtag", "comment", "freebsd_iface.go")) + assert.NoError(t, err) + + err = parser.Load() + assert.NoError(t, err) // Expect "redeclared in this block" if tags aren't respected + + nodes := parser.Interfaces() + assert.Equal(t, 1, len(nodes)) + assert.Equal(t, "IfaceWithBuildTagInComment", nodes[0].Name) +} diff --git a/mockery/walker_test.go b/mockery/walker_test.go index 3bead47b..55055ec5 100644 --- a/mockery/walker_test.go +++ b/mockery/walker_test.go @@ -2,7 +2,6 @@ package mockery import ( "os" - "path/filepath" "regexp" "testing" @@ -45,7 +44,7 @@ func TestWalkerHere(t *testing.T) { assert.True(t, len(gv.Interfaces) > 10) first := gv.Interfaces[0] assert.Equal(t, "AsyncProducer", first.Name) - assert.Equal(t, filepath.Join(wd, "fixtures", "async.go"), first.Path) + assert.Equal(t, getFixturePath("async.go"), first.Path) } func TestWalkerRegexp(t *testing.T) { @@ -69,5 +68,5 @@ func TestWalkerRegexp(t *testing.T) { assert.True(t, len(gv.Interfaces) >= 1) first := gv.Interfaces[0] assert.Equal(t, "AsyncProducer", first.Name) - assert.Equal(t, filepath.Join(wd, "fixtures", "async.go"), first.Path) + assert.Equal(t, getFixturePath("async.go"), first.Path) }