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_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)
 			}