encoding/textpb: marshal google.protobuf.Any in expanded form
Marshal well-known type Any in expanded form by default, else fallback
to marshaling it as a regular message.
Change-Id: Ic7e9e37b47042a163941f8849dc366ffe48103ca
Reviewed-on: https://go-review.googlesource.com/c/156097
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/textpb/encode_test.go b/encoding/textpb/encode_test.go
index 8b7974e..a439df0 100644
--- a/encoding/textpb/encode_test.go
+++ b/encoding/textpb/encode_test.go
@@ -9,14 +9,17 @@
"strings"
"testing"
+ protoV1 "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/protoapi"
"github.com/golang/protobuf/v2/encoding/textpb"
"github.com/golang/protobuf/v2/internal/detrand"
"github.com/golang/protobuf/v2/internal/encoding/pack"
"github.com/golang/protobuf/v2/internal/encoding/wire"
+ "github.com/golang/protobuf/v2/internal/impl"
"github.com/golang/protobuf/v2/internal/legacy"
"github.com/golang/protobuf/v2/internal/scalar"
"github.com/golang/protobuf/v2/proto"
+ preg "github.com/golang/protobuf/v2/reflect/protoregistry"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
@@ -24,6 +27,7 @@
// TODO: Remove this when protoV1 registers these hooks for you.
_ "github.com/golang/protobuf/v2/internal/legacy"
+ anypb "github.com/golang/protobuf/ptypes/any"
"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
)
@@ -65,6 +69,7 @@
func TestMarshal(t *testing.T) {
tests := []struct {
desc string
+ mo textpb.MarshalOptions
input proto.Message
want string
wantErr bool
@@ -964,18 +969,161 @@
}
`,
*/
+ }, {
+ desc: "google.protobuf.Any message not expanded",
+ 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 impl.Export{}.MessageOf(&anypb.Any{
+ TypeUrl: string(m.ProtoReflect().Type().FullName()),
+ 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}
+ }(),
+ 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 impl.Export{}.MessageOf(&anypb.Any{
+ TypeUrl: string(m.ProtoReflect().Type().FullName()),
+ Value: b,
+ }).Interface()
+ }(),
+ want: `[pb2.Nested]: {
+ opt_string: "embedded inside Any"
+ opt_nested: {
+ opt_string: "inception"
+ }
+}
+`,
+ }, {
+ 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}
+ }(),
+ input: 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 impl.Export{}.MessageOf(&anypb.Any{
+ TypeUrl: string(m.ProtoReflect().Type().FullName()),
+ Value: b,
+ }).Interface()
+ }(),
+ want: `[pb2.PartialRequired]: {
+ opt_string: "embedded inside Any"
+}
+`,
+ wantErr: true,
+ }, {
+ 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 {
tt := tt
t.Run(tt.desc, func(t *testing.T) {
t.Parallel()
- b, err := textpb.Marshal(tt.input)
+ b, err := tt.mo.Marshal(tt.input)
if err != nil && !tt.wantErr {
- t.Errorf("Marshal() returned error: %v\n\n", err)
+ t.Errorf("Marshal() returned error: %v\n", err)
}
if err == nil && tt.wantErr {
- t.Error("Marshal() got nil error, want error\n\n")
+ t.Error("Marshal() got nil error, want error\n")
}
got := string(b)
if tt.want != "" && got != tt.want {