Skip to content

Commit

Permalink
fix: duplicate interfaces being generated
Browse files Browse the repository at this point in the history
The problem is because I'm passing the `Tests: true` config option to `packages.Load`, multiple packages can be returned for a single package ID. For now I've just taken the easy option of using a map to ensure that there's no duplicates.
  • Loading branch information
adamconnelly committed Jun 10, 2024
1 parent c1bacd3 commit f9917e4
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 8 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ At the moment Kelpie is very much in development, and there are missing features

The following is a list of known-outstanding features and known issues:

- [ ] For some reason Kelpie is generating multiple mocks when specifying built-in interfaces. See the [Mock Generation](#mock-generation) section for an example.
- [ ] Add support for embedded interfaces.

## Quickstart

Expand Down Expand Up @@ -96,7 +96,6 @@ Kelpie mock generation starting - preparing to add some magic to your code-base!
Parsing package 'io' for interfaces to mock.
- Generating a mock for 'Reader'.
- Generating a mock for 'Reader'.
Parsing package 'github.com/adamconnelly/kelpie/examples' for interfaces to mock.
- Generating a mock for 'Maths'.
Expand Down
3 changes: 2 additions & 1 deletion examples/external_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package examples
import (
"testing"

"github.com/stretchr/testify/suite"

"github.com/adamconnelly/kelpie"
"github.com/adamconnelly/kelpie/examples/mocks/reader"
"github.com/stretchr/testify/suite"
)

type ExternalTypesTests struct {
Expand Down
12 changes: 12 additions & 0 deletions maps/maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,15 @@ func Keys[TKey comparable, TValue any](m map[TKey]TValue) []TKey {

return keys
}

// Values returns the values in the map.
func Values[TKey comparable, TValue any](m map[TKey]TValue) []TValue {
values := make([]TValue, len(m))
index := 0
for _, v := range m {
values[index] = v
index++
}

return values
}
13 changes: 8 additions & 5 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/pkg/errors"
"golang.org/x/tools/go/packages"

"github.com/adamconnelly/kelpie/maps"
"github.com/adamconnelly/kelpie/slices"
)

Expand Down Expand Up @@ -117,8 +118,6 @@ func (f *IncludingInterfaceFilter) Include(name string) bool {

// Parse parses the source contained in the reader.
func Parse(packageName string, directory string, filter InterfaceFilter) (*ParsedPackage, error) {
var interfaces []MockedInterface

pkgs, err := packages.Load(&packages.Config{
Mode: packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedFiles,
Dir: directory,
Expand All @@ -129,6 +128,7 @@ func Parse(packageName string, directory string, filter InterfaceFilter) (*Parse
}

var packageDirectory string
interfaces := map[string]MockedInterface{}

for _, p := range pkgs {
if len(p.Syntax) > 0 && len(p.GoFiles) > 0 {
Expand All @@ -144,11 +144,14 @@ func Parse(packageName string, directory string, filter InterfaceFilter) (*Parse
if t.Name.IsExported() {
if interfaceType, ok := t.Type.(*ast.InterfaceType); ok {
if filter.Include(t.Name.Name) {
interfaces = append(interfaces, parseInterface(t.Name.Name, t.Name.Name, interfaceType, p, fileNode.Imports))
i := parseInterface(t.Name.Name, t.Name.Name, interfaceType, p, fileNode.Imports)
interfaces[i.FullName] = i
}
} else if structType, ok := t.Type.(*ast.StructType); ok {
for _, f := range structType.Fields.List {
interfaces = append(interfaces, parseStructField(t, f, p, fileNode.Imports, filter)...)
for _, i := range parseStructField(t, f, p, fileNode.Imports, filter) {
interfaces[i.FullName] = i
}
}
}
}
Expand All @@ -164,7 +167,7 @@ func Parse(packageName string, directory string, filter InterfaceFilter) (*Parse
}
}

return &ParsedPackage{PackageDirectory: packageDirectory, Mocks: interfaces}, nil
return &ParsedPackage{PackageDirectory: packageDirectory, Mocks: maps.Values(interfaces)}, nil
}

func parseStructField(structNode *ast.TypeSpec, field *ast.Field, pkg *packages.Package, importSpecs []*ast.ImportSpec, filter InterfaceFilter) []MockedInterface {
Expand Down
15 changes: 15 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,21 @@ type DoubleNested struct {
t.Len(DoSomethingElse.Results, 2)
}

func (t *ParserTests) Test_Parse_CanParseStdlibInterfaces() {
// Arrange
t.interfaceFilter.Setup(interfacefilter.Include(kelpie.Any[string]()).Return(false))
t.interfaceFilter.Setup(interfacefilter.Include("Reader").Return(true))

// Act
result, err := parser.Parse("io", ".", t.interfaceFilter.Instance())

// Assert
t.NoError(err)
t.Len(result.Mocks, 1)
t.Len(result.Mocks[0].Methods, 1)
t.Equal("Read", result.Mocks[0].Methods[0].Name)
}

func (t *ParserTests) Test_MockedInterface_AnyMethodsHaveParameters_ReturnsFalseIfNoMethodsHaveParameters() {
// Arrange
mockedInterface := parser.MockedInterface{
Expand Down

0 comments on commit f9917e4

Please sign in to comment.