1
2
3
4
5 package prototext_test
6
7 import (
8 "testing"
9
10 "google.golang.org/protobuf/encoding/prototext"
11 "google.golang.org/protobuf/proto"
12 "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
23 func TestRoundTrip(t *testing.T) {
24 tests := []struct {
25 desc string
26 resolver *protoregistry.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: ×tamppb.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: ×tamppb.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(protoregistry.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
View as plain text