mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-03-25 16:43:37 +00:00
testing/protocmp: automatically promote message values
For user convenience, automatically transform message values by shallow copying them if necessary. Storing messages as values is frowned upon, but is sometimes done by APIs that a user does not own. Change-Id: I7e927d1a1e050bf4cea1aa889f56d23e99355f26 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/221423 Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
a89afdc06f
commit
ca0b25e48f
testing/protocmp
@ -145,6 +145,65 @@ func TestEqual(t *testing.T) {
|
||||
want: true,
|
||||
}}...)
|
||||
|
||||
// Test message values.
|
||||
tests = append(tests, []test{{
|
||||
x: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)},
|
||||
y: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: true,
|
||||
}, {
|
||||
x: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)},
|
||||
y: testpb.TestAllTypes{OptionalSint64: proto.Int64(2)},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: false,
|
||||
}, {
|
||||
x: struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)}},
|
||||
y: struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)}},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: true,
|
||||
}, {
|
||||
x: struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(1)}},
|
||||
y: struct{ M testpb.TestAllTypes }{M: testpb.TestAllTypes{OptionalSint64: proto.Int64(2)}},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: false,
|
||||
}, {
|
||||
x: struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(1)}}},
|
||||
y: struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(1)}}},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: true,
|
||||
}, {
|
||||
x: struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(1)}}},
|
||||
y: struct{ M []testpb.TestAllTypes }{M: []testpb.TestAllTypes{{OptionalSint64: proto.Int64(2)}}},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: false,
|
||||
}, {
|
||||
x: struct {
|
||||
M map[string]testpb.TestAllTypes
|
||||
}{
|
||||
M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(1)}},
|
||||
},
|
||||
y: struct {
|
||||
M map[string]testpb.TestAllTypes
|
||||
}{
|
||||
M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(1)}},
|
||||
},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: true,
|
||||
}, {
|
||||
x: struct {
|
||||
M map[string]testpb.TestAllTypes
|
||||
}{
|
||||
M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(1)}},
|
||||
},
|
||||
y: struct {
|
||||
M map[string]testpb.TestAllTypes
|
||||
}{
|
||||
M: map[string]testpb.TestAllTypes{"k": {OptionalSint64: proto.Int64(2)}},
|
||||
},
|
||||
opts: cmp.Options{Transform()},
|
||||
want: false,
|
||||
}}...)
|
||||
|
||||
// Test IgnoreUnknown.
|
||||
raw := pack.Message{
|
||||
pack.Tag{1, pack.BytesType}, pack.String("Hello, goodbye!"),
|
||||
|
@ -161,10 +161,18 @@ func Transform(...option) cmp.Option {
|
||||
// NOTE: There are currently no custom options for Transform,
|
||||
// but the use of an unexported type keeps the future open.
|
||||
|
||||
// addrType returns a pointer to t if t isn't a pointer or interface.
|
||||
addrType := func(t reflect.Type) reflect.Type {
|
||||
if k := t.Kind(); k == reflect.Interface || k == reflect.Ptr {
|
||||
return t
|
||||
}
|
||||
return reflect.PtrTo(t)
|
||||
}
|
||||
|
||||
// TODO: Should this transform protoreflect.Enum types to Enum as well?
|
||||
return cmp.FilterPath(func(p cmp.Path) bool {
|
||||
ps := p.Last()
|
||||
if isMessageType(ps.Type()) {
|
||||
if isMessageType(addrType(ps.Type())) {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -175,11 +183,19 @@ func Transform(...option) cmp.Option {
|
||||
if !vx.IsValid() || vx.IsNil() || !vy.IsValid() || vy.IsNil() {
|
||||
return false
|
||||
}
|
||||
return isMessageType(vx.Elem().Type()) && isMessageType(vy.Elem().Type())
|
||||
return isMessageType(addrType(vx.Elem().Type())) && isMessageType(addrType(vy.Elem().Type()))
|
||||
}
|
||||
|
||||
return false
|
||||
}, cmp.Transformer("protocmp.Transform", func(v interface{}) Message {
|
||||
// For user convenience, shallow copy the message value if necessary
|
||||
// in order for it to implement the message interface.
|
||||
if rv := reflect.ValueOf(v); rv.IsValid() && rv.Kind() != reflect.Ptr && !isMessageType(rv.Type()) {
|
||||
pv := reflect.New(rv.Type())
|
||||
pv.Elem().Set(rv)
|
||||
v = pv.Interface()
|
||||
}
|
||||
|
||||
m := protoimpl.X.MessageOf(v)
|
||||
switch {
|
||||
case m == nil:
|
||||
|
Loading…
x
Reference in New Issue
Block a user