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>
diff --git a/encoding/textpb/decode.go b/encoding/textpb/decode.go
index f7b10ad..f906e18 100644
--- a/encoding/textpb/decode.go
+++ b/encoding/textpb/decode.go
@@ -8,10 +8,12 @@
"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 @@
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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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
+}
diff --git a/encoding/textpb/decode_test.go b/encoding/textpb/decode_test.go
index 9911362..90a9e78 100644
--- a/encoding/textpb/decode_test.go
+++ b/encoding/textpb/decode_test.go
@@ -10,6 +10,7 @@
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 TestUnmarshal(t *testing.T) {
tests := []struct {
desc string
+ umo textpb.UnmarshalOptions
inputMessage proto.Message
inputText string
wantMessage proto.Message
@@ -1122,13 +1124,186 @@
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)
}
diff --git a/encoding/textpb/encode.go b/encoding/textpb/encode.go
index 57a35c6..77606da 100644
--- a/encoding/textpb/encode.go
+++ b/encoding/textpb/encode.go
@@ -375,10 +375,10 @@
}
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 @@
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,
},
}
diff --git a/encoding/textpb/encode_test.go b/encoding/textpb/encode_test.go
index 9871e75..15a5492 100644
--- a/encoding/textpb/encode_test.go
+++ b/encoding/textpb/encode_test.go
@@ -67,6 +67,10 @@
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 @@
`,
*/
}, {
- 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 @@
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 @@
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 @@
}
`,
}, {
- 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 @@
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,84 +1063,17 @@
`,
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"
- }
- }
-}
-`,
}}
for _, tt := range tests {
diff --git a/encoding/textpb/other_test.go b/encoding/textpb/other_test.go
index 4092976..4200ac1 100644
--- a/encoding/textpb/other_test.go
+++ b/encoding/textpb/other_test.go
@@ -7,10 +7,13 @@
"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 @@
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 @@
},
},
}, {
- 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 @@
},
},
},
+ }, {
+ 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)
}