diff --git a/testing/protocmp/util.go b/testing/protocmp/util.go index 7a0b49b1..b9ccae49 100644 --- a/testing/protocmp/util.go +++ b/testing/protocmp/util.go @@ -22,8 +22,90 @@ var ( messageReflectType = reflect.TypeOf(Message{}) ) +// FilterEnum filters opt to only be applicable on standalone Enums, +// singular fields of enums, list fields of enums, or map fields of enum values, +// where the enum is the same type as the specified enum. +// +// The Go type of the last path step may be an: +// • Enum for singular fields, elements of a repeated field, +// values of a map field, or standalone Enums +// • []Enum for list fields +// • map[K]Enum for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterEnum(enum protoreflect.Enum, opt cmp.Option) cmp.Option { + return FilterDescriptor(enum.Descriptor(), opt) +} + +// FilterMessage filters opt to only be applicable on standalone Messages, +// singular fields of messages, list fields of messages, or map fields of +// message values, where the message is the same type as the specified message. +// +// The Go type of the last path step may be an: +// • Message for singular fields, elements of a repeated field, +// values of a map field, or standalone Messages +// • []Message for list fields +// • map[K]Message for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterMessage(message proto.Message, opt cmp.Option) cmp.Option { + return FilterDescriptor(message.ProtoReflect().Descriptor(), opt) +} + +// FilterField filters opt to only be applicable on the specified field +// in the message. It panics if a field of the given name does not exist. +// +// The Go type of the last path step may be an: +// • T for singular fields +// • []T for list fields +// • map[K]T for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterField(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { + md := message.ProtoReflect().Descriptor() + return FilterDescriptor(mustFindFieldDescriptor(md, name), opt) +} + +// FilterOneof filters opt to only be applicable on all fields within the +// specified oneof in the message. It panics if a oneof of the given name +// does not exist. +// +// The Go type of the last path step may be an: +// • T for singular fields +// • []T for list fields +// • map[K]T for map fields +// • interface{} for a Message map entry value +// +// This must be used in conjunction with Transform. +func FilterOneof(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option { + md := message.ProtoReflect().Descriptor() + return FilterDescriptor(mustFindOneofDescriptor(md, name), opt) +} + +// FilterDescriptor ignores the specified descriptor. +// +// The following descriptor types may be specified: +// • protoreflect.EnumDescriptor +// • protoreflect.MessageDescriptor +// • protoreflect.FieldDescriptor +// • protoreflect.OneofDescriptor +// +// For the behavior of each, see the corresponding filter function. +// Since this filter accepts a protoreflect.FieldDescriptor, it can be used +// to also filter for extension fields as a protoreflect.ExtensionDescriptor +// is just an alias to protoreflect.FieldDescriptor. +// +// This must be used in conjunction with Transform. +func FilterDescriptor(desc protoreflect.Descriptor, opt cmp.Option) cmp.Option { + f := newNameFilters(desc) + return cmp.FilterPath(f.Filter, opt) +} + // IgnoreEnums ignores all enums of the specified types. -// See IgnoreDescriptors with regard to EnumDescriptors for more information. +// It is equivalent to FilterEnum(enum, cmp.Ignore()) for each enum. // // This must be used in conjunction with Transform. func IgnoreEnums(enums ...protoreflect.Enum) cmp.Option { @@ -35,7 +117,7 @@ func IgnoreEnums(enums ...protoreflect.Enum) cmp.Option { } // IgnoreMessages ignores all messages of the specified types. -// See IgnoreDescriptors with regard to MessageDescriptors for more information. +// It is equivalent to FilterMessage(message, cmp.Ignore()) for each message. // // This must be used in conjunction with Transform. func IgnoreMessages(messages ...proto.Message) cmp.Option { @@ -46,9 +128,9 @@ func IgnoreMessages(messages ...proto.Message) cmp.Option { return IgnoreDescriptors(ds...) } -// IgnoreFields ignores the specified fields in messages of type m. -// This panics if a field of the given name does not exist. -// See IgnoreDescriptors with regard to FieldDescriptors for more information. +// IgnoreFields ignores the specified fields in the specified message. +// It is equivalent to FilterField(message, name, cmp.Ignore()) for each field +// in the message. // // This must be used in conjunction with Transform. func IgnoreFields(message proto.Message, names ...protoreflect.Name) cmp.Option { @@ -60,9 +142,9 @@ func IgnoreFields(message proto.Message, names ...protoreflect.Name) cmp.Option return IgnoreDescriptors(ds...) } -// IgnoreOneofs ignores fields in the specified oneofs in messages of type m. -// This panics if a oneof of the given name does not exist. -// See IgnoreDescriptors with regard to OneofDescriptors for more information. +// IgnoreOneofs ignores fields of the specified oneofs in the specified message. +// It is equivalent to FilterOneof(message, name, cmp.Ignore()) for each oneof +// in the message. // // This must be used in conjunction with Transform. func IgnoreOneofs(message proto.Message, names ...protoreflect.Name) cmp.Option { @@ -75,24 +157,7 @@ func IgnoreOneofs(message proto.Message, names ...protoreflect.Name) cmp.Option } // IgnoreDescriptors ignores the specified set of descriptors. -// The following descriptor types may be specified: -// -// • EnumDescriptor: Enums of this type or messages containing singular fields, -// list fields, or map fields with enum values of this type are ignored. -// Enums are matched based on their full name. -// -// • MessageDescriptor: Messages of this type or messages containing -// singular fields, list fields, or map fields with message values of this type -// are ignored. Messages are matched based on their full name. -// -// • ExtensionDescriptor: Extensions fields that match the given descriptor -// by full name are ignored. -// -// • FieldDescriptor: Message fields that match the given descriptor -// by full name are ignored. -// -// • OneofDescriptor: Message fields that match the set of fields in the given -// oneof descriptor by full name are ignored. +// It is equivalent to FilterDescriptor(desc, cmp.Ignore()) for each descriptor. // // This must be used in conjunction with Transform. func IgnoreDescriptors(descs ...protoreflect.Descriptor) cmp.Option { @@ -192,6 +257,12 @@ func (f *nameFilters) Filter(p cmp.Path) bool { } func (f *nameFilters) filterFields(p cmp.Path) bool { + // Trim off trailing type-assertions so that the filter can match on the + // concrete value held within an interface value. + if _, ok := p.Last().(cmp.TypeAssertion); ok { + p = p[:len(p)-1] + } + // Filter for Message maps. mi, ok := p.Index(-1).(cmp.MapIndex) if !ok { diff --git a/testing/protocmp/util_test.go b/testing/protocmp/util_test.go index 12065920..04e0d1a2 100644 --- a/testing/protocmp/util_test.go +++ b/testing/protocmp/util_test.go @@ -368,8 +368,6 @@ func TestEqual(t *testing.T) { y: &testpb.TestAllTypes{RepeatedForeignMessage: []*testpb.ForeignMessage{{}, {}, nil, {}, {C: proto.Int32(5)}, {}}}, opts: cmp.Options{Transform(), IgnoreEmptyMessages()}, want: true, - - // TODO }, { x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{}}, y: &testpb.TestAllTypes{MapStringNestedMessage: nil}, @@ -531,6 +529,436 @@ func TestEqual(t *testing.T) { want: false, }}...) + // Test FilterEnum. + tests = append(tests, []test{{ + x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()}, + y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()}, + y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.ForeignEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter type + }, { + x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()}, + y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y int) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()}, + y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y testpb.TestAllTypes_NestedEnum) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()}, + y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()}, + y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y Enum) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.ForeignEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter type + }, { + x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y int) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y []testpb.TestAllTypes_NestedEnum) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{RepeatedNestedEnum: []testpb.TestAllTypes_NestedEnum{testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y []Enum) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.ForeignEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter type + }, { + x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y int) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y map[string]testpb.TestAllTypes_NestedEnum) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_FOO}}, + y: &testpb.TestAllTypes{MapStringNestedEnum: map[string]testpb.TestAllTypes_NestedEnum{"k": testpb.TestAllTypes_BAR}}, + opts: cmp.Options{ + Transform(), + FilterEnum(testpb.TestAllTypes_NestedEnum(0), cmp.Comparer(func(x, y map[string]Enum) bool { return true })), + }, + want: true, + }}...) + + // Test FilterMessage. + tests = append(tests, []test{{ + x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}}, + y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}}, + y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllExtensions), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter type + }, { + x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}}, + y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y int) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}}, + y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y *testpb.TestAllTypes_NestedMessage) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}}, + y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(1)}}, + y: &testpb.TestAllTypes{OptionalNestedMessage: &testpb.TestAllTypes_NestedMessage{A: proto.Int32(2)}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y Message) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllExtensions), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter type + }, { + x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y int) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y []*testpb.TestAllTypes_NestedMessage) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{RepeatedNestedMessage: []*testpb.TestAllTypes_NestedMessage{{A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y []Message) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllExtensions), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter type + }, { + x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y int) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y map[string]*testpb.TestAllTypes_NestedMessage) bool { return true })), + }, + want: false, // matching filter type, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(1)}}}, + y: &testpb.TestAllTypes{MapStringNestedMessage: map[string]*testpb.TestAllTypes_NestedMessage{"k": {A: proto.Int32(2)}}}, + opts: cmp.Options{ + Transform(), + FilterMessage(new(testpb.TestAllTypes_NestedMessage), cmp.Comparer(func(x, y map[string]Message) bool { return true })), + }, + want: true, + }}...) + + // Test FilterField. + tests = append(tests, []test{{ + x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)}, + y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)}, + y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "optional_int64", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter name + }, { + x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)}, + y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "optional_int32", cmp.Comparer(func(x, y int64) bool { return true })), + }, + want: false, // matching filter name, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)}, + y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "optional_int32", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{OptionalInt32: proto.Int32(1)}, + y: &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "optional_int32", cmp.Comparer(func(x, y int32) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}}, + y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}}, + y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "repeated_int64", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter name + }, { + x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}}, + y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "repeated_int32", cmp.Comparer(func(x, y []int64) bool { return true })), + }, + want: false, // matching filter name, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}}, + y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "repeated_int32", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{RepeatedInt32: []int32{1}}, + y: &testpb.TestAllTypes{RepeatedInt32: []int32{2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "repeated_int32", cmp.Comparer(func(x, y []int32) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}}, + y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}}, + y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "map_int64_int64", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter name + }, { + x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}}, + y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "map_int32_int32", cmp.Comparer(func(x, y map[int64]int64) bool { return true })), + }, + want: false, // matching filter name, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}}, + y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "map_int32_int32", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{1: 1}}, + y: &testpb.TestAllTypes{MapInt32Int32: map[int32]int32{2: 2}}, + opts: cmp.Options{ + Transform(), + FilterField(new(testpb.TestAllTypes), "map_int32_int32", cmp.Comparer(func(x, y map[int32]int32) bool { return true })), + }, + want: true, + }}...) + + // Test FilterOneof + tests = append(tests, []test{{ + x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}}, + y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}}, + opts: cmp.Options{Transform()}, + want: false, + }, { + x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}}, + y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}}, + opts: cmp.Options{ + Transform(), + FilterOneof(new(testpb.TestAllTypes), "oneof_optional", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: false, // mismatching filter name + }, { + x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}}, + y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}}, + opts: cmp.Options{ + Transform(), + FilterOneof(new(testpb.TestAllTypes), "oneof_field", cmp.Comparer(func(x, y string) bool { return true })), + }, + want: false, // matching filter name, but mismatching comparer type + }, { + x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}}, + y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}}, + opts: cmp.Options{ + Transform(), + FilterOneof(new(testpb.TestAllTypes), "oneof_field", cmp.Comparer(func(x, y uint32) bool { return true })), + }, + want: true, + }, { + x: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{1}}, + y: &testpb.TestAllTypes{OneofField: &testpb.TestAllTypes_OneofUint32{2}}, + opts: cmp.Options{ + Transform(), + FilterOneof(new(testpb.TestAllTypes), "oneof_field", cmp.Comparer(func(x, y interface{}) bool { return true })), + }, + want: true, + }}...) + for _, tt := range tests { t.Run("", func(t *testing.T) { got := cmp.Equal(tt.x, tt.y, tt.opts)