Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

MOCK-429: add support for assignable types to Eq matcher #481

Merged
merged 1 commit into from
Oct 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion gomock/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,21 @@ type eqMatcher struct {
}

func (e eqMatcher) Matches(x interface{}) bool {
return reflect.DeepEqual(e.x, x)
// In case, some value is nil
if e.x == nil || x == nil {
return reflect.DeepEqual(e.x, x)
}

// Check if types assignable and convert them to common type
x1Val := reflect.ValueOf(e.x)
x2Val := reflect.ValueOf(x)

if x1Val.Type().AssignableTo(x2Val.Type()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for aliased types (type A []string) it doesn't matter, but if we are talking about interfaces I think there may be an issue here.

type Foo interface {
  Meow() string
}

type Baz struct {}

func (b Baz) Meow() string {
  return "meow"
}

type Boomer interface {
  Boom(Foo)
}

func TestBoom(t *testing.T) {
  // omitted for brevity
  var bazzer Baz = newBazzer()
  mockBoomer.EXPECT().Boom(gomock.Eq(bazzer)).Times(1)
  var fooer Foo = Foo(bazzer)
  mockBoomer.Boom(fooer) // Foo not assignable to Baz
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But interface isn't a type(it has underlying type instead). Here is script in playground https://play.golang.org/p/AvDmLq8arUK -- hope i correctly understood your concern.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, I think I understand. In the case of an interface the object will be resolved to its underlying type which must be assignable to the expected type. Thanks for the demo!

x1ValConverted := x1Val.Convert(x2Val.Type())
return reflect.DeepEqual(x1ValConverted.Interface(), x2Val.Interface())
}

return false
}

func (e eqMatcher) String() string {
Expand Down
6 changes: 6 additions & 0 deletions gomock/matchers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"github.com/golang/mock/gomock/internal/mock_gomock"
)

type A []string

func TestMatchers(t *testing.T) {
type e interface{}
tests := []struct {
Expand All @@ -44,6 +46,10 @@ func TestMatchers(t *testing.T) {
[]e{[]int{1, 2}, "ab", map[string]int{"a": 0, "b": 1}, [2]string{"a", "b"}},
[]e{[]int{1}, "a", 42, 42.0, false, [1]string{"a"}},
},
{"test assignable types", gomock.Eq(A{"a", "b"}),
[]e{[]string{"a", "b"}, A{"a", "b"}},
[]e{[]string{"a"}, A{"b"}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down