encoding/textpb: unmarshal Any

Also fix marshaling Any in expanded form to contain the correct type_url
value.

Change-Id: I4b467e74bb1d73255effd9cc4cfff9cf4558940f
Reviewed-on: https://go-review.googlesource.com/c/156342
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Herbie Ong 2019-01-04 14:08:41 -08:00
parent 0dcfb9aa6a
commit 66c365cf72
5 changed files with 376 additions and 125 deletions

View File

@ -8,10 +8,12 @@ import (
"fmt"
"strings"
protoV1 "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/v2/internal/encoding/text"
"github.com/golang/protobuf/v2/internal/errors"
"github.com/golang/protobuf/v2/internal/pragma"
"github.com/golang/protobuf/v2/internal/set"
pvalue "github.com/golang/protobuf/v2/internal/value"
"github.com/golang/protobuf/v2/proto"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
"github.com/golang/protobuf/v2/reflect/protoregistry"
@ -87,9 +89,15 @@ func (o UnmarshalOptions) unmarshalMessage(tmsg [][2]text.Value, m pref.Message)
var nerr errors.NonFatal
msgType := m.Type()
knownFields := m.KnownFields()
// Handle expanded Any message.
if msgType.FullName() == "google.protobuf.Any" && isExpandedAny(tmsg) {
return o.unmarshalAny(tmsg[0], knownFields)
}
fieldDescs := msgType.Fields()
reservedNames := msgType.ReservedNames()
knownFields := m.KnownFields()
xtTypes := knownFields.ExtensionTypes()
var reqNums set.Ints
var seenNums set.Ints
@ -109,18 +117,21 @@ func (o UnmarshalOptions) unmarshalMessage(tmsg [][2]text.Value, m pref.Message)
fd = fieldDescs.ByName(pref.Name(strings.ToLower(string(name))))
}
case text.String:
// TODO: Handle Any expansions here as well.
// Handle extensions. Extensions have to be registered first in the message's
// Handle extensions only. This code path is not for Any.
if msgType.FullName() == "google.protobuf.Any" {
break
}
// Extensions have to be registered first in the message's
// ExtensionTypes before setting a value to it.
xtName := pref.FullName(tkey.String())
// Check first if it is already registered. This is the case for repeated fields.
// Check first if it is already registered. This is the case for
// repeated fields.
xt := xtTypes.ByName(xtName)
if xt == nil {
var err error
xt, err = o.Resolver.FindExtensionByName(xtName)
if err != nil && err != protoregistry.NotFound {
return err
return errors.New("unable to resolve [%v]: %v", xtName, err)
}
if xt != nil {
xtTypes.Register(xt)
@ -274,12 +285,11 @@ func unmarshalScalar(input text.Value, fd pref.FieldDescriptor) (pref.Value, err
// If input is int32, use directly.
if n, ok := input.Int(b32); ok {
return pref.ValueOf(pref.EnumNumber(n)), nil
} else {
if name, ok := input.Name(); ok {
// Lookup EnumNumber based on name.
if enumVal := fd.EnumType().Values().ByName(name); enumVal != nil {
return pref.ValueOf(enumVal.Number()), nil
}
}
if name, ok := input.Name(); ok {
// Lookup EnumNumber based on name.
if enumVal := fd.EnumType().Values().ByName(name); enumVal != nil {
return pref.ValueOf(enumVal.Number()), nil
}
}
default:
@ -327,7 +337,7 @@ func (o UnmarshalOptions) unmarshalMap(input []text.Value, fd pref.FieldDescript
// Determine ahead whether map entry is a scalar type or a message type in order to call the
// appropriate unmarshalMapValue func inside the for loop below.
unmarshalMapValue := o.unmarshalMapScalarValue
unmarshalMapValue := unmarshalMapScalarValue
switch valDesc.Kind() {
case pref.MessageKind, pref.GroupKind:
unmarshalMapValue = o.unmarshalMapMessageValue
@ -418,7 +428,7 @@ func (o UnmarshalOptions) unmarshalMapMessageValue(input text.Value, pkey pref.M
// unmarshalMapScalarValue unmarshals given scalar-type text.Value into a protoreflect.Map
// for the given MapKey.
func (o UnmarshalOptions) unmarshalMapScalarValue(input text.Value, pkey pref.MapKey, fd pref.FieldDescriptor, mmap pref.Map) error {
func unmarshalMapScalarValue(input text.Value, pkey pref.MapKey, fd pref.FieldDescriptor, mmap pref.Map) error {
var val pref.Value
if input.Type() == 0 {
val = fd.Default()
@ -432,3 +442,51 @@ func (o UnmarshalOptions) unmarshalMapScalarValue(input text.Value, pkey pref.Ma
mmap.Set(pkey, val)
return nil
}
// isExpandedAny returns true if given [][2]text.Value may be an expanded Any that contains only one
// field with key type of text.String type and value type of text.Message.
func isExpandedAny(tmsg [][2]text.Value) bool {
if len(tmsg) != 1 {
return false
}
field := tmsg[0]
return field[0].Type() == text.String && field[1].Type() == text.Message
}
// unmarshalAny unmarshals an expanded Any textproto. This method assumes that the given
// tfield has key type of text.String and value type of text.Message.
func (o UnmarshalOptions) unmarshalAny(tfield [2]text.Value, knownFields pref.KnownFields) error {
var nerr errors.NonFatal
typeURL := tfield[0].String()
value := tfield[1].Message()
mt, err := o.Resolver.FindMessageByURL(typeURL)
if !nerr.Merge(err) {
return errors.New("unable to resolve message [%v]: %v", typeURL, err)
}
// Create new message for the embedded message type and unmarshal the
// value into it.
m := mt.New()
if err := o.unmarshalMessage(value, m); !nerr.Merge(err) {
return err
}
// Serialize the embedded message and assign the resulting bytes to the value field.
// TODO: Switch to V2 marshal and enable deterministic option when ready.
var mv1 protoV1.Message
if mtmp, ok := m.(pvalue.Unwrapper); ok {
mv1 = mtmp.ProtoUnwrap().(protoV1.Message)
} else {
mv1 = m.Interface().(protoV1.Message)
}
b, err := protoV1.Marshal(mv1)
if !nerr.Merge(err) {
return err
}
knownFields.Set(pref.FieldNumber(1), pref.ValueOf(typeURL))
knownFields.Set(pref.FieldNumber(2), pref.ValueOf(b))
return nerr.E
}

View File

@ -10,6 +10,7 @@ import (
protoV1 "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoapi"
anypb "github.com/golang/protobuf/ptypes/any"
"github.com/golang/protobuf/v2/encoding/textpb"
"github.com/golang/protobuf/v2/internal/legacy"
"github.com/golang/protobuf/v2/internal/scalar"
@ -49,6 +50,7 @@ func registerExtension(xd *protoapi.ExtensionDesc) {
func TestUnmarshal(t *testing.T) {
tests := []struct {
desc string
umo textpb.UnmarshalOptions
inputMessage proto.Message
inputText string
wantMessage proto.Message
@ -1122,13 +1124,186 @@ opt_int32: 42
inputMessage: &pb2.Extensions{},
inputText: "[pb2.invalid_message_field]: true",
wantErr: true,
}, {
desc: "Any not expanded",
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
type_url: "pb2.Nested"
value: "some bytes"
}
`,
wantMessage: &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: "pb2.Nested",
Value: []byte("some bytes"),
},
},
}, {
desc: "Any not expanded missing value",
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
type_url: "pb2.Nested"
}
`,
wantMessage: &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: "pb2.Nested",
},
},
}, {
desc: "Any not expanded missing type_url",
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
value: "some bytes"
}
`,
wantMessage: &pb2.KnownTypes{
OptAny: &anypb.Any{
Value: []byte("some bytes"),
},
},
}, {
desc: "Any expanded",
umo: func() textpb.UnmarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.UnmarshalOptions{Resolver: resolver}
}(),
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[foobar/pb2.Nested]: {
opt_string: "embedded inside Any"
opt_nested: {
opt_string: "inception"
}
}
}
`,
wantMessage: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
OptNested: &pb2.Nested{
OptString: scalar.String("inception"),
},
}
// TODO: Switch to V2 marshal when ready.
b, err := protoV1.Marshal(m)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: "foobar/pb2.Nested",
Value: b,
},
}
}(),
}, {
desc: "Any expanded with empty value",
umo: func() textpb.UnmarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.UnmarshalOptions{Resolver: resolver}
}(),
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[foo.com/pb2.Nested]: {}
}
`,
wantMessage: &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: "foo.com/pb2.Nested",
},
},
}, {
desc: "Any expanded with missing required error",
umo: func() textpb.UnmarshalOptions {
m := &pb2.PartialRequired{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.UnmarshalOptions{Resolver: resolver}
}(),
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[pb2.PartialRequired]: {
opt_string: "embedded inside Any"
}
}
`,
wantMessage: func() proto.Message {
m := &pb2.PartialRequired{
OptString: scalar.String("embedded inside Any"),
}
// TODO: Switch to V2 marshal when ready.
b, err := protoV1.Marshal(m)
// Ignore required not set error.
if _, ok := err.(*protoV1.RequiredNotSetError); !ok {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: "pb2.PartialRequired",
Value: b,
},
}
}(),
wantErr: true,
}, {
desc: "Any expanded with unregistered type",
umo: textpb.UnmarshalOptions{Resolver: preg.NewTypes()},
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[SomeMessage]: {}
}
`,
wantErr: true,
}, {
desc: "Any expanded with invalid value",
umo: func() textpb.UnmarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.UnmarshalOptions{Resolver: resolver}
}(),
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[pb2.Nested]: 123
}
`,
wantErr: true,
}, {
desc: "Any expanded with unknown fields",
umo: func() textpb.UnmarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.UnmarshalOptions{Resolver: resolver}
}(),
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[pb2.Nested]: {}
unknown: ""
}
`,
wantErr: true,
}, {
desc: "Any contains expanded and unexpanded fields",
umo: func() textpb.UnmarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.UnmarshalOptions{Resolver: resolver}
}(),
inputMessage: &pb2.KnownTypes{},
inputText: `opt_any: {
[pb2.Nested]: {}
type_url: "pb2.Nested"
}
`,
wantErr: true,
}}
for _, tt := range tests {
tt := tt
t.Run(tt.desc, func(t *testing.T) {
t.Parallel()
err := textpb.Unmarshal(tt.inputMessage, []byte(tt.inputText))
err := tt.umo.Unmarshal(tt.inputMessage, []byte(tt.inputText))
if err != nil && !tt.wantErr {
t.Errorf("Unmarshal() returned error: %v\n\n", err)
}

View File

@ -375,10 +375,10 @@ func (o MarshalOptions) marshalAny(m pref.Message) (text.Value, error) {
}
knownFields := m.KnownFields()
typeURL := knownFields.Get(tfd.Number())
typeURL := knownFields.Get(tfd.Number()).String()
value := knownFields.Get(vfd.Number())
emt, err := o.Resolver.FindMessageByURL(typeURL.String())
emt, err := o.Resolver.FindMessageByURL(typeURL)
if !nerr.Merge(err) {
return text.Value{}, err
}
@ -393,12 +393,11 @@ func (o MarshalOptions) marshalAny(m pref.Message) (text.Value, error) {
if !nerr.Merge(err) {
return text.Value{}, err
}
// Expanded Any field value contains only a single field with the embedded
// message type as the field name in [] and a text marshaled field value of
// the embedded message.
// Expanded Any field value contains only a single field with the type_url field value as the
// field name in [] and a text marshaled field value of the embedded message.
msgFields := [][2]text.Value{
{
text.ValueOf(string(emt.FullName())),
text.ValueOf(typeURL),
msg,
},
}

View File

@ -67,6 +67,10 @@ func setExtension(m proto.Message, xd *protoapi.ExtensionDesc, val interface{})
knownFields.Set(wire.Number(xd.Field), pval)
}
func wrapAnyPB(any *anypb.Any) proto.Message {
return impl.Export{}.MessageOf(any).Interface()
}
// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
func dhex(s string) []byte {
b, err := hex.DecodeString(s)
@ -980,8 +984,10 @@ opt_int32: 42
`,
*/
}, {
desc: "google.protobuf.Any message not expanded",
mo: textpb.MarshalOptions{Resolver: preg.NewTypes()},
desc: "Any message not expanded",
mo: textpb.MarshalOptions{
Resolver: preg.NewTypes(),
},
input: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
@ -994,21 +1000,19 @@ opt_int32: 42
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return impl.Export{}.MessageOf(&anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
return wrapAnyPB(&anypb.Any{
TypeUrl: "pb2.Nested",
Value: b,
}).Interface()
})
}(),
want: `type_url: "pb2.Nested"
value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
`,
}, {
desc: "google.protobuf.Any message expanded",
mo: func() textpb.MarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.MarshalOptions{Resolver: resolver}
}(),
desc: "Any message expanded",
mo: textpb.MarshalOptions{
Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
},
input: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
@ -1021,12 +1025,12 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return impl.Export{}.MessageOf(&anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
return wrapAnyPB(&anypb.Any{
TypeUrl: "foo/pb2.Nested",
Value: b,
}).Interface()
})
}(),
want: `[pb2.Nested]: {
want: `[foo/pb2.Nested]: {
opt_string: "embedded inside Any"
opt_nested: {
opt_string: "inception"
@ -1034,12 +1038,10 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
}
`,
}, {
desc: "google.protobuf.Any message expanded with missing required error",
mo: func() textpb.MarshalOptions {
m := &pb2.PartialRequired{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.MarshalOptions{Resolver: resolver}
}(),
desc: "Any message expanded with missing required error",
mo: textpb.MarshalOptions{
Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
},
input: func() proto.Message {
m := &pb2.PartialRequired{
OptString: scalar.String("embedded inside Any"),
@ -1050,10 +1052,10 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
if _, ok := err.(*protoV1.RequiredNotSetError); !ok {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return impl.Export{}.MessageOf(&anypb.Any{
return wrapAnyPB(&anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
Value: b,
}).Interface()
})
}(),
want: `[pb2.PartialRequired]: {
opt_string: "embedded inside Any"
@ -1061,83 +1063,16 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
`,
wantErr: true,
}, {
desc: "google.protobuf.Any message with invalid value",
mo: func() textpb.MarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.MarshalOptions{Resolver: resolver}
}(),
input: func() proto.Message {
m := &pb2.Nested{}
return impl.Export{}.MessageOf(&anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
Value: dhex("80"),
}).Interface()
}(),
want: `type_url: "pb2.Nested"
desc: "Any message with invalid value",
mo: textpb.MarshalOptions{
Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
},
input: wrapAnyPB(&anypb.Any{
TypeUrl: "foo/pb2.Nested",
Value: dhex("80"),
}),
want: `type_url: "foo/pb2.Nested"
value: "\x80"
`,
}, {
desc: "google.protobuf.Any field",
mo: textpb.MarshalOptions{Resolver: preg.NewTypes()},
input: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
OptNested: &pb2.Nested{
OptString: scalar.String("inception"),
},
}
// TODO: Switch to V2 marshal when ready.
b, err := protoV1.Marshal(m)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
Value: b,
},
}
}(),
want: `opt_any: {
type_url: "pb2.Nested"
value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
}
`,
}, {
desc: "google.protobuf.Any field expanded using given types registry",
mo: func() textpb.MarshalOptions {
m := &pb2.Nested{}
resolver := preg.NewTypes(m.ProtoReflect().Type())
return textpb.MarshalOptions{Resolver: resolver}
}(),
input: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
OptNested: &pb2.Nested{
OptString: scalar.String("inception"),
},
}
// TODO: Switch to V2 marshal when ready.
b, err := protoV1.Marshal(m)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
Value: b,
},
}
}(),
want: `opt_any: {
[pb2.Nested]: {
opt_string: "embedded inside Any"
opt_nested: {
opt_string: "inception"
}
}
}
`,
}}

View File

@ -7,10 +7,13 @@ import (
"github.com/golang/protobuf/v2/encoding/textpb"
"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
"github.com/golang/protobuf/v2/proto"
preg "github.com/golang/protobuf/v2/reflect/protoregistry"
// The legacy package must be imported prior to use of any legacy messages.
// TODO: Remove this when protoV1 registers these hooks for you.
"github.com/golang/protobuf/v2/internal/impl"
_ "github.com/golang/protobuf/v2/internal/legacy"
"github.com/golang/protobuf/v2/internal/scalar"
anypb "github.com/golang/protobuf/ptypes/any"
durpb "github.com/golang/protobuf/ptypes/duration"
@ -22,8 +25,9 @@ import (
func TestRoundTrip(t *testing.T) {
tests := []struct {
desc string
message proto.Message
desc string
resolver *preg.Types
message proto.Message
}{{
desc: "well-known type fields set to empty messages",
message: &pb2.KnownTypes{
@ -88,7 +92,7 @@ func TestRoundTrip(t *testing.T) {
},
},
}, {
desc: "well-known type struct field and different Value types",
desc: "Struct field and different Value types",
message: &pb2.KnownTypes{
OptStruct: &stpb.Struct{
Fields: map[string]*stpb.Value{
@ -146,21 +150,101 @@ func TestRoundTrip(t *testing.T) {
},
},
},
}, {
desc: "Any field without registered type",
resolver: preg.NewTypes(),
message: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
OptNested: &pb2.Nested{
OptString: scalar.String("inception"),
},
}
// TODO: Switch to V2 marshal when ready.
b, err := protoV1.Marshal(m)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
Value: b,
},
}
}(),
}, {
desc: "Any field with registered type",
resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
message: func() proto.Message {
m := &pb2.Nested{
OptString: scalar.String("embedded inside Any"),
OptNested: &pb2.Nested{
OptString: scalar.String("inception"),
},
}
// TODO: Switch to V2 marshal when ready.
b, err := protoV1.Marshal(m)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: string(m.ProtoReflect().Type().FullName()),
Value: b,
},
}
}(),
}, {
desc: "Any field containing Any message",
resolver: func() *preg.Types {
mt1 := (&pb2.Nested{}).ProtoReflect().Type()
mt2 := impl.Export{}.MessageTypeOf(&anypb.Any{})
return preg.NewTypes(mt1, mt2)
}(),
message: func() proto.Message {
m1 := &pb2.Nested{
OptString: scalar.String("message inside Any of another Any field"),
}
// TODO: Switch to V2 marshal when ready.
b1, err := protoV1.Marshal(m1)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
m2 := &anypb.Any{
TypeUrl: "pb2.Nested",
Value: b1,
}
// TODO: Switch to V2 marshal when ready.
b2, err := protoV1.Marshal(m2)
if err != nil {
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
}
return &pb2.KnownTypes{
OptAny: &anypb.Any{
TypeUrl: "google.protobuf.Any",
Value: b2,
},
}
}(),
}}
for _, tt := range tests {
tt := tt
t.Run(tt.desc, func(t *testing.T) {
t.Parallel()
b, err := textpb.Marshal(tt.message)
mo := textpb.MarshalOptions{Resolver: tt.resolver}
umo := textpb.UnmarshalOptions{Resolver: tt.resolver}
b, err := mo.Marshal(tt.message)
if err != nil {
t.Errorf("Marshal() returned error: %v\n\n", err)
}
gotMessage := tt.message.ProtoReflect().Type().New().Interface()
err = textpb.Unmarshal(gotMessage, b)
err = umo.Unmarshal(gotMessage, b)
if err != nil {
t.Errorf("Unmarshal() returned error: %v\n\n", err)
}
if !protoV1.Equal(gotMessage.(protoV1.Message), tt.message.(protoV1.Message)) {
t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", gotMessage, tt.message)
}