1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package prototext_test
6
7import (
8	"testing"
9
10	"google.golang.org/protobuf/encoding/prototext"
11	"google.golang.org/protobuf/proto"
12	preg "google.golang.org/protobuf/reflect/protoregistry"
13
14	pb2 "google.golang.org/protobuf/internal/testprotos/textpb2"
15	"google.golang.org/protobuf/types/known/anypb"
16	"google.golang.org/protobuf/types/known/durationpb"
17	"google.golang.org/protobuf/types/known/emptypb"
18	"google.golang.org/protobuf/types/known/structpb"
19	"google.golang.org/protobuf/types/known/timestamppb"
20	"google.golang.org/protobuf/types/known/wrapperspb"
21)
22
23func TestRoundTrip(t *testing.T) {
24	tests := []struct {
25		desc     string
26		resolver *preg.Types
27		message  proto.Message
28	}{{
29		desc: "well-known type fields set to empty messages",
30		message: &pb2.KnownTypes{
31			OptBool:      &wrapperspb.BoolValue{},
32			OptInt32:     &wrapperspb.Int32Value{},
33			OptInt64:     &wrapperspb.Int64Value{},
34			OptUint32:    &wrapperspb.UInt32Value{},
35			OptUint64:    &wrapperspb.UInt64Value{},
36			OptFloat:     &wrapperspb.FloatValue{},
37			OptDouble:    &wrapperspb.DoubleValue{},
38			OptString:    &wrapperspb.StringValue{},
39			OptBytes:     &wrapperspb.BytesValue{},
40			OptDuration:  &durationpb.Duration{},
41			OptTimestamp: &timestamppb.Timestamp{},
42			OptStruct:    &structpb.Struct{},
43			OptList:      &structpb.ListValue{},
44			OptValue:     &structpb.Value{},
45			OptEmpty:     &emptypb.Empty{},
46			OptAny:       &anypb.Any{},
47		},
48	}, {
49		desc: "well-known type scalar fields",
50		message: &pb2.KnownTypes{
51			OptBool: &wrapperspb.BoolValue{
52				Value: true,
53			},
54			OptInt32: &wrapperspb.Int32Value{
55				Value: -42,
56			},
57			OptInt64: &wrapperspb.Int64Value{
58				Value: -42,
59			},
60			OptUint32: &wrapperspb.UInt32Value{
61				Value: 0xff,
62			},
63			OptUint64: &wrapperspb.UInt64Value{
64				Value: 0xffff,
65			},
66			OptFloat: &wrapperspb.FloatValue{
67				Value: 1.234,
68			},
69			OptDouble: &wrapperspb.DoubleValue{
70				Value: 1.23e308,
71			},
72			OptString: &wrapperspb.StringValue{
73				Value: "谷歌",
74			},
75			OptBytes: &wrapperspb.BytesValue{
76				Value: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
77			},
78		},
79	}, {
80		desc: "well-known type time-related fields",
81		message: &pb2.KnownTypes{
82			OptDuration: &durationpb.Duration{
83				Seconds: -3600,
84				Nanos:   -123,
85			},
86			OptTimestamp: &timestamppb.Timestamp{
87				Seconds: 1257894000,
88				Nanos:   123,
89			},
90		},
91	}, {
92		desc: "Struct field and different Value types",
93		message: &pb2.KnownTypes{
94			OptStruct: &structpb.Struct{
95				Fields: map[string]*structpb.Value{
96					"bool": &structpb.Value{
97						Kind: &structpb.Value_BoolValue{
98							BoolValue: true,
99						},
100					},
101					"double": &structpb.Value{
102						Kind: &structpb.Value_NumberValue{
103							NumberValue: 3.1415,
104						},
105					},
106					"null": &structpb.Value{
107						Kind: &structpb.Value_NullValue{
108							NullValue: structpb.NullValue_NULL_VALUE,
109						},
110					},
111					"string": &structpb.Value{
112						Kind: &structpb.Value_StringValue{
113							StringValue: "string",
114						},
115					},
116					"struct": &structpb.Value{
117						Kind: &structpb.Value_StructValue{
118							StructValue: &structpb.Struct{
119								Fields: map[string]*structpb.Value{
120									"bool": &structpb.Value{
121										Kind: &structpb.Value_BoolValue{
122											BoolValue: false,
123										},
124									},
125								},
126							},
127						},
128					},
129					"list": &structpb.Value{
130						Kind: &structpb.Value_ListValue{
131							ListValue: &structpb.ListValue{
132								Values: []*structpb.Value{
133									{
134										Kind: &structpb.Value_BoolValue{
135											BoolValue: false,
136										},
137									},
138									{
139										Kind: &structpb.Value_StringValue{
140											StringValue: "hello",
141										},
142									},
143								},
144							},
145						},
146					},
147				},
148			},
149		},
150	}, {
151		desc:     "Any field without registered type",
152		resolver: new(preg.Types),
153		message: func() proto.Message {
154			m := &pb2.Nested{
155				OptString: proto.String("embedded inside Any"),
156				OptNested: &pb2.Nested{
157					OptString: proto.String("inception"),
158				},
159			}
160			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
161			if err != nil {
162				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
163			}
164			return &pb2.KnownTypes{
165				OptAny: &anypb.Any{
166					TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
167					Value:   b,
168				},
169			}
170		}(),
171	}, {
172		desc: "Any field with registered type",
173		message: func() *pb2.KnownTypes {
174			m := &pb2.Nested{
175				OptString: proto.String("embedded inside Any"),
176				OptNested: &pb2.Nested{
177					OptString: proto.String("inception"),
178				},
179			}
180			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
181			if err != nil {
182				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
183			}
184			return &pb2.KnownTypes{
185				OptAny: &anypb.Any{
186					TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
187					Value:   b,
188				},
189			}
190		}(),
191	}, {
192		desc: "Any field containing Any message",
193		message: func() *pb2.KnownTypes {
194			m1 := &pb2.Nested{
195				OptString: proto.String("message inside Any of another Any field"),
196			}
197			b1, err := proto.MarshalOptions{Deterministic: true}.Marshal(m1)
198			if err != nil {
199				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
200			}
201			m2 := &anypb.Any{
202				TypeUrl: "pb2.Nested",
203				Value:   b1,
204			}
205			b2, err := proto.MarshalOptions{Deterministic: true}.Marshal(m2)
206			if err != nil {
207				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
208			}
209			return &pb2.KnownTypes{
210				OptAny: &anypb.Any{
211					TypeUrl: "google.protobuf.Any",
212					Value:   b2,
213				},
214			}
215		}(),
216	}}
217
218	for _, tt := range tests {
219		tt := tt
220		t.Run(tt.desc, func(t *testing.T) {
221			t.Parallel()
222			b, err := prototext.MarshalOptions{Resolver: tt.resolver}.Marshal(tt.message)
223			if err != nil {
224				t.Errorf("Marshal() returned error: %v\n\n", err)
225			}
226
227			gotMessage := new(pb2.KnownTypes)
228			err = prototext.UnmarshalOptions{Resolver: tt.resolver}.Unmarshal(b, gotMessage)
229			if err != nil {
230				t.Errorf("Unmarshal() returned error: %v\n\n", err)
231			}
232
233			if !proto.Equal(gotMessage, tt.message) {
234				t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", gotMessage, tt.message)
235			}
236		})
237	}
238}
239