blob: fe4271d878e66516e94754d8dcd69521b6d69aa5 [file] [log] [blame]
Herbie Ongcddf8192018-11-28 18:25:20 -08001// Copyright 2018 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 textpb_test
6
7import (
Herbie Onga94f78c2019-01-03 15:39:58 -08008 "encoding/hex"
Herbie Ongcddf8192018-11-28 18:25:20 -08009 "math"
10 "strings"
11 "testing"
12
Herbie Ongf42b55f2019-01-02 15:46:07 -080013 protoV1 "github.com/golang/protobuf/proto"
Herbie Ongcf253082018-12-17 17:13:07 -080014 "github.com/golang/protobuf/protoapi"
Herbie Ongcddf8192018-11-28 18:25:20 -080015 "github.com/golang/protobuf/v2/encoding/textpb"
Herbie Ongcddf8192018-11-28 18:25:20 -080016 "github.com/golang/protobuf/v2/internal/detrand"
Herbie Ong20a1d312018-12-11 21:08:58 -080017 "github.com/golang/protobuf/v2/internal/encoding/pack"
Herbie Ongcf253082018-12-17 17:13:07 -080018 "github.com/golang/protobuf/v2/internal/encoding/wire"
Herbie Ongf42b55f2019-01-02 15:46:07 -080019 "github.com/golang/protobuf/v2/internal/impl"
Herbie Ongcf253082018-12-17 17:13:07 -080020 "github.com/golang/protobuf/v2/internal/legacy"
Herbie Ongcddf8192018-11-28 18:25:20 -080021 "github.com/golang/protobuf/v2/internal/scalar"
22 "github.com/golang/protobuf/v2/proto"
Herbie Ongf42b55f2019-01-02 15:46:07 -080023 preg "github.com/golang/protobuf/v2/reflect/protoregistry"
Herbie Ongcddf8192018-11-28 18:25:20 -080024 "github.com/google/go-cmp/cmp"
25 "github.com/google/go-cmp/cmp/cmpopts"
26
Joe Tsai08e00302018-11-26 22:32:06 -080027 // The legacy package must be imported prior to use of any legacy messages.
28 // TODO: Remove this when protoV1 registers these hooks for you.
29 _ "github.com/golang/protobuf/v2/internal/legacy"
30
Herbie Ongf42b55f2019-01-02 15:46:07 -080031 anypb "github.com/golang/protobuf/ptypes/any"
Joe Tsai08e00302018-11-26 22:32:06 -080032 "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
33 "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
Herbie Ongcddf8192018-11-28 18:25:20 -080034)
35
36func init() {
37 // Disable detrand to enable direct comparisons on outputs.
38 detrand.Disable()
39}
40
Herbie Ongcddf8192018-11-28 18:25:20 -080041// splitLines is a cmpopts.Option for comparing strings with line breaks.
42var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
43 return strings.Split(s, "\n")
44})
45
Herbie Ong800c9902018-12-06 15:28:53 -080046func pb2Enum(i int32) *pb2.Enum {
47 p := new(pb2.Enum)
48 *p = pb2.Enum(i)
49 return p
50}
51
52func pb2Enums_NestedEnum(i int32) *pb2.Enums_NestedEnum {
53 p := new(pb2.Enums_NestedEnum)
54 *p = pb2.Enums_NestedEnum(i)
55 return p
56}
57
Herbie Ongcf253082018-12-17 17:13:07 -080058func setExtension(m proto.Message, xd *protoapi.ExtensionDesc, val interface{}) {
59 xt := legacy.Export{}.ExtensionTypeFromDesc(xd)
60 knownFields := m.ProtoReflect().KnownFields()
61 extTypes := knownFields.ExtensionTypes()
62 extTypes.Register(xt)
63 if val == nil {
64 return
65 }
66 pval := xt.ValueOf(val)
67 knownFields.Set(wire.Number(xd.Field), pval)
68}
69
Herbie Ong66c365c2019-01-04 14:08:41 -080070func wrapAnyPB(any *anypb.Any) proto.Message {
71 return impl.Export{}.MessageOf(any).Interface()
72}
73
Herbie Onga94f78c2019-01-03 15:39:58 -080074// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
75func dhex(s string) []byte {
76 b, err := hex.DecodeString(s)
77 if err != nil {
78 panic(err)
79 }
80 return b
81}
82
Herbie Ongcddf8192018-11-28 18:25:20 -080083func TestMarshal(t *testing.T) {
84 tests := []struct {
85 desc string
Herbie Ongf42b55f2019-01-02 15:46:07 -080086 mo textpb.MarshalOptions
Herbie Ong70651952018-12-13 14:19:50 -080087 input proto.Message
Herbie Ongcddf8192018-11-28 18:25:20 -080088 want string
89 wantErr bool
90 }{{
Herbie Ongcddf8192018-11-28 18:25:20 -080091 desc: "proto2 optional scalar fields not set",
Herbie Ong800c9902018-12-06 15:28:53 -080092 input: &pb2.Scalars{},
Herbie Ongcddf8192018-11-28 18:25:20 -080093 want: "\n",
94 }, {
95 desc: "proto3 scalar fields not set",
Herbie Ong800c9902018-12-06 15:28:53 -080096 input: &pb3.Scalars{},
Herbie Ongcddf8192018-11-28 18:25:20 -080097 want: "\n",
98 }, {
99 desc: "proto2 optional scalar fields set to zero values",
Herbie Ong800c9902018-12-06 15:28:53 -0800100 input: &pb2.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800101 OptBool: scalar.Bool(false),
102 OptInt32: scalar.Int32(0),
103 OptInt64: scalar.Int64(0),
104 OptUint32: scalar.Uint32(0),
105 OptUint64: scalar.Uint64(0),
106 OptSint32: scalar.Int32(0),
107 OptSint64: scalar.Int64(0),
108 OptFixed32: scalar.Uint32(0),
109 OptFixed64: scalar.Uint64(0),
110 OptSfixed32: scalar.Int32(0),
111 OptSfixed64: scalar.Int64(0),
112 OptFloat: scalar.Float32(0),
113 OptDouble: scalar.Float64(0),
114 OptBytes: []byte{},
115 OptString: scalar.String(""),
Herbie Ong800c9902018-12-06 15:28:53 -0800116 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800117 want: `opt_bool: false
118opt_int32: 0
119opt_int64: 0
120opt_uint32: 0
121opt_uint64: 0
122opt_sint32: 0
123opt_sint64: 0
124opt_fixed32: 0
125opt_fixed64: 0
126opt_sfixed32: 0
127opt_sfixed64: 0
128opt_float: 0
129opt_double: 0
130opt_bytes: ""
131opt_string: ""
132`,
133 }, {
134 desc: "proto3 scalar fields set to zero values",
Herbie Ong800c9902018-12-06 15:28:53 -0800135 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800136 SBool: false,
137 SInt32: 0,
138 SInt64: 0,
139 SUint32: 0,
140 SUint64: 0,
141 SSint32: 0,
142 SSint64: 0,
143 SFixed32: 0,
144 SFixed64: 0,
145 SSfixed32: 0,
146 SSfixed64: 0,
147 SFloat: 0,
148 SDouble: 0,
149 SBytes: []byte{},
150 SString: "",
Herbie Ong800c9902018-12-06 15:28:53 -0800151 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800152 want: "\n",
153 }, {
154 desc: "proto2 optional scalar fields set to some values",
Herbie Ong800c9902018-12-06 15:28:53 -0800155 input: &pb2.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800156 OptBool: scalar.Bool(true),
157 OptInt32: scalar.Int32(0xff),
158 OptInt64: scalar.Int64(0xdeadbeef),
159 OptUint32: scalar.Uint32(47),
160 OptUint64: scalar.Uint64(0xdeadbeef),
161 OptSint32: scalar.Int32(-1001),
162 OptSint64: scalar.Int64(-0xffff),
163 OptFixed64: scalar.Uint64(64),
164 OptSfixed32: scalar.Int32(-32),
165 // TODO: Update encoder to output same decimals.
166 OptFloat: scalar.Float32(1.02),
167 OptDouble: scalar.Float64(1.23e100),
168 // TODO: Update encoder to not output UTF8 for bytes.
169 OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
170 OptString: scalar.String("谷歌"),
Herbie Ong800c9902018-12-06 15:28:53 -0800171 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800172 want: `opt_bool: true
173opt_int32: 255
174opt_int64: 3735928559
175opt_uint32: 47
176opt_uint64: 3735928559
177opt_sint32: -1001
178opt_sint64: -65535
179opt_fixed64: 64
180opt_sfixed32: -32
181opt_float: 1.0199999809265137
182opt_double: 1.23e+100
183opt_bytes: "谷歌"
184opt_string: "谷歌"
185`,
186 }, {
Herbie Ongcddf8192018-11-28 18:25:20 -0800187 desc: "float32 nan",
Herbie Ong800c9902018-12-06 15:28:53 -0800188 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800189 SFloat: float32(math.NaN()),
Herbie Ong800c9902018-12-06 15:28:53 -0800190 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800191 want: "s_float: nan\n",
192 }, {
193 desc: "float32 positive infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800194 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800195 SFloat: float32(math.Inf(1)),
Herbie Ong800c9902018-12-06 15:28:53 -0800196 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800197 want: "s_float: inf\n",
198 }, {
199 desc: "float32 negative infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800200 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800201 SFloat: float32(math.Inf(-1)),
Herbie Ong800c9902018-12-06 15:28:53 -0800202 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800203 want: "s_float: -inf\n",
204 }, {
205 desc: "float64 nan",
Herbie Ong800c9902018-12-06 15:28:53 -0800206 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800207 SDouble: math.NaN(),
Herbie Ong800c9902018-12-06 15:28:53 -0800208 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800209 want: "s_double: nan\n",
210 }, {
211 desc: "float64 positive infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800212 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800213 SDouble: math.Inf(1),
Herbie Ong800c9902018-12-06 15:28:53 -0800214 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800215 want: "s_double: inf\n",
216 }, {
217 desc: "float64 negative infinity",
Herbie Ong800c9902018-12-06 15:28:53 -0800218 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800219 SDouble: math.Inf(-1),
Herbie Ong800c9902018-12-06 15:28:53 -0800220 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800221 want: "s_double: -inf\n",
222 }, {
223 desc: "proto2 bytes set to empty string",
Herbie Ong800c9902018-12-06 15:28:53 -0800224 input: &pb2.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800225 OptBytes: []byte(""),
Herbie Ong800c9902018-12-06 15:28:53 -0800226 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800227 want: "opt_bytes: \"\"\n",
228 }, {
229 desc: "proto3 bytes set to empty string",
Herbie Ong800c9902018-12-06 15:28:53 -0800230 input: &pb3.Scalars{
Herbie Ongcddf8192018-11-28 18:25:20 -0800231 SBytes: []byte(""),
Herbie Ong800c9902018-12-06 15:28:53 -0800232 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800233 want: "\n",
234 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800235 desc: "proto2 enum not set",
236 input: &pb2.Enums{},
Herbie Ongcddf8192018-11-28 18:25:20 -0800237 want: "\n",
238 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800239 desc: "proto2 enum set to zero value",
240 input: &pb2.Enums{
241 OptEnum: pb2.Enum_UNKNOWN.Enum(),
242 OptNestedEnum: pb2Enums_NestedEnum(0),
243 },
244 want: `opt_enum: UNKNOWN
245opt_nested_enum: 0
246`,
247 }, {
248 desc: "proto2 enum",
249 input: &pb2.Enums{
250 OptEnum: pb2.Enum_FIRST.Enum(),
251 OptNestedEnum: pb2.Enums_UNO.Enum(),
252 },
253 want: `opt_enum: FIRST
254opt_nested_enum: UNO
255`,
256 }, {
257 desc: "proto2 enum set to numeric values",
258 input: &pb2.Enums{
259 OptEnum: pb2Enum(1),
260 OptNestedEnum: pb2Enums_NestedEnum(2),
261 },
262 want: `opt_enum: FIRST
263opt_nested_enum: DOS
264`,
265 }, {
266 desc: "proto2 enum set to unnamed numeric values",
267 input: &pb2.Enums{
268 OptEnum: pb2Enum(101),
269 OptNestedEnum: pb2Enums_NestedEnum(-101),
270 },
271 want: `opt_enum: 101
272opt_nested_enum: -101
273`,
274 }, {
275 desc: "proto3 enum not set",
276 input: &pb3.Enums{},
277 want: "\n",
278 }, {
279 desc: "proto3 enum set to zero value",
280 input: &pb3.Enums{
281 SEnum: pb3.Enum_ZERO,
282 SNestedEnum: pb3.Enums_CERO,
283 },
284 want: "\n",
285 }, {
286 desc: "proto3 enum",
287 input: &pb3.Enums{
288 SEnum: pb3.Enum_ONE,
289 SNestedEnum: pb3.Enums_DIEZ,
290 },
291 want: `s_enum: ONE
292s_nested_enum: DIEZ
293`,
294 }, {
295 desc: "proto3 enum set to numeric values",
296 input: &pb3.Enums{
297 SEnum: 2,
298 SNestedEnum: 1,
299 },
300 want: `s_enum: TWO
301s_nested_enum: UNO
302`,
303 }, {
304 desc: "proto3 enum set to unnamed numeric values",
305 input: &pb3.Enums{
306 SEnum: -47,
307 SNestedEnum: 47,
308 },
309 want: `s_enum: -47
310s_nested_enum: 47
311`,
312 }, {
313 desc: "proto2 nested message not set",
314 input: &pb2.Nests{},
315 want: "\n",
316 }, {
317 desc: "proto2 nested message set to empty",
318 input: &pb2.Nests{
319 OptNested: &pb2.Nested{},
320 Optgroup: &pb2.Nests_OptGroup{},
321 },
322 want: `opt_nested: {}
Herbie Ong0dcfb9a2019-01-14 15:32:26 -0800323OptGroup: {}
Herbie Ong800c9902018-12-06 15:28:53 -0800324`,
325 }, {
326 desc: "proto2 nested messages",
327 input: &pb2.Nests{
328 OptNested: &pb2.Nested{
329 OptString: scalar.String("nested message"),
330 OptNested: &pb2.Nested{
331 OptString: scalar.String("another nested message"),
332 },
333 },
334 },
335 want: `opt_nested: {
336 opt_string: "nested message"
337 opt_nested: {
338 opt_string: "another nested message"
339 }
340}
341`,
342 }, {
343 desc: "proto2 group fields",
344 input: &pb2.Nests{
345 Optgroup: &pb2.Nests_OptGroup{
346 OptBool: scalar.Bool(true),
347 OptString: scalar.String("inside a group"),
348 OptNested: &pb2.Nested{
349 OptString: scalar.String("nested message inside a group"),
350 },
351 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
352 OptEnum: pb2.Enum_TENTH.Enum(),
353 },
354 },
355 },
Herbie Ong0dcfb9a2019-01-14 15:32:26 -0800356 want: `OptGroup: {
Herbie Ong800c9902018-12-06 15:28:53 -0800357 opt_bool: true
358 opt_string: "inside a group"
359 opt_nested: {
360 opt_string: "nested message inside a group"
361 }
Herbie Ong0dcfb9a2019-01-14 15:32:26 -0800362 OptNestedGroup: {
Herbie Ong800c9902018-12-06 15:28:53 -0800363 opt_enum: TENTH
364 }
365}
366`,
367 }, {
368 desc: "proto3 nested message not set",
369 input: &pb3.Nests{},
370 want: "\n",
371 }, {
372 desc: "proto3 nested message",
373 input: &pb3.Nests{
374 SNested: &pb3.Nested{
375 SString: "nested message",
376 SNested: &pb3.Nested{
377 SString: "another nested message",
378 },
379 },
380 },
381 want: `s_nested: {
382 s_string: "nested message"
383 s_nested: {
384 s_string: "another nested message"
385 }
386}
387`,
388 }, {
389 desc: "oneof fields",
390 input: &pb2.Oneofs{},
391 want: "\n",
392 }, {
393 desc: "oneof field set to empty string",
394 input: &pb2.Oneofs{
395 Union: &pb2.Oneofs_Str{},
396 },
397 want: "str: \"\"\n",
398 }, {
399 desc: "oneof field set to string",
400 input: &pb2.Oneofs{
401 Union: &pb2.Oneofs_Str{
402 Str: "hello",
403 },
404 },
405 want: "str: \"hello\"\n",
406 }, {
407 desc: "oneof field set to empty message",
408 input: &pb2.Oneofs{
409 Union: &pb2.Oneofs_Msg{
410 Msg: &pb2.Nested{},
411 },
412 },
413 want: "msg: {}\n",
414 }, {
415 desc: "oneof field set to message",
416 input: &pb2.Oneofs{
417 Union: &pb2.Oneofs_Msg{
418 Msg: &pb2.Nested{
419 OptString: scalar.String("nested message"),
420 },
421 },
422 },
423 want: `msg: {
424 opt_string: "nested message"
425}
426`,
427 }, {
428 desc: "repeated not set",
429 input: &pb2.Repeats{},
430 want: "\n",
431 }, {
432 desc: "repeated set to empty slices",
433 input: &pb2.Repeats{
Herbie Ongcddf8192018-11-28 18:25:20 -0800434 RptBool: []bool{},
435 RptInt32: []int32{},
436 RptInt64: []int64{},
437 RptUint32: []uint32{},
438 RptUint64: []uint64{},
439 RptFloat: []float32{},
440 RptDouble: []float64{},
441 RptBytes: [][]byte{},
Herbie Ong800c9902018-12-06 15:28:53 -0800442 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800443 want: "\n",
444 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800445 desc: "repeated set to some values",
446 input: &pb2.Repeats{
Herbie Ongcddf8192018-11-28 18:25:20 -0800447 RptBool: []bool{true, false, true, true},
448 RptInt32: []int32{1, 6, 0, 0},
449 RptInt64: []int64{-64, 47},
450 RptUint32: []uint32{0xff, 0xffff},
451 RptUint64: []uint64{0xdeadbeef},
452 // TODO: add float32 examples.
453 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
454 RptString: []string{"hello", "世界"},
455 RptBytes: [][]byte{
456 []byte("hello"),
457 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
458 },
Herbie Ong800c9902018-12-06 15:28:53 -0800459 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800460 want: `rpt_bool: true
461rpt_bool: false
462rpt_bool: true
463rpt_bool: true
464rpt_int32: 1
465rpt_int32: 6
466rpt_int32: 0
467rpt_int32: 0
468rpt_int64: -64
469rpt_int64: 47
470rpt_uint32: 255
471rpt_uint32: 65535
472rpt_uint64: 3735928559
473rpt_double: nan
474rpt_double: inf
475rpt_double: -inf
476rpt_double: 1.23e-308
477rpt_string: "hello"
478rpt_string: "世界"
479rpt_bytes: "hello"
480rpt_bytes: "世界"
481`,
482 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800483 desc: "repeated enum",
484 input: &pb2.Enums{
Herbie Ongcddf8192018-11-28 18:25:20 -0800485 RptEnum: []pb2.Enum{pb2.Enum_FIRST, 2, pb2.Enum_TENTH, 42},
Herbie Ongcddf8192018-11-28 18:25:20 -0800486 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
Herbie Ong800c9902018-12-06 15:28:53 -0800487 },
488 want: `rpt_enum: FIRST
Herbie Ongcddf8192018-11-28 18:25:20 -0800489rpt_enum: SECOND
490rpt_enum: TENTH
491rpt_enum: 42
Herbie Ongcddf8192018-11-28 18:25:20 -0800492rpt_nested_enum: DOS
493rpt_nested_enum: 47
494rpt_nested_enum: DIEZ
495`,
496 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800497 desc: "repeated nested message set to empty",
498 input: &pb2.Nests{
Herbie Ongcddf8192018-11-28 18:25:20 -0800499 RptNested: []*pb2.Nested{},
500 Rptgroup: []*pb2.Nests_RptGroup{},
Herbie Ong800c9902018-12-06 15:28:53 -0800501 },
502 want: "\n",
Herbie Ongcddf8192018-11-28 18:25:20 -0800503 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800504 desc: "repeated nested messages",
505 input: &pb2.Nests{
Herbie Ongcddf8192018-11-28 18:25:20 -0800506 RptNested: []*pb2.Nested{
507 {
508 OptString: scalar.String("repeat nested one"),
509 },
510 {
511 OptString: scalar.String("repeat nested two"),
512 OptNested: &pb2.Nested{
513 OptString: scalar.String("inside repeat nested two"),
514 },
515 },
516 {},
517 },
Herbie Ong800c9902018-12-06 15:28:53 -0800518 },
519 want: `rpt_nested: {
Herbie Ongcddf8192018-11-28 18:25:20 -0800520 opt_string: "repeat nested one"
521}
522rpt_nested: {
523 opt_string: "repeat nested two"
524 opt_nested: {
525 opt_string: "inside repeat nested two"
526 }
527}
528rpt_nested: {}
529`,
530 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800531 desc: "repeated group fields",
532 input: &pb2.Nests{
Herbie Ongcddf8192018-11-28 18:25:20 -0800533 Rptgroup: []*pb2.Nests_RptGroup{
534 {
535 RptBool: []bool{true, false},
536 },
537 {},
538 },
Herbie Ong800c9902018-12-06 15:28:53 -0800539 },
Herbie Ongde7313b2019-01-14 19:26:50 -0800540 want: `RptGroup: {
Herbie Ongcddf8192018-11-28 18:25:20 -0800541 rpt_bool: true
542 rpt_bool: false
543}
Herbie Ongde7313b2019-01-14 19:26:50 -0800544RptGroup: {}
Herbie Ongcddf8192018-11-28 18:25:20 -0800545`,
546 }, {
Herbie Ongcddf8192018-11-28 18:25:20 -0800547 desc: "map fields empty",
Herbie Ong800c9902018-12-06 15:28:53 -0800548 input: &pb2.Maps{},
Herbie Ongcddf8192018-11-28 18:25:20 -0800549 want: "\n",
550 }, {
551 desc: "map fields set to empty maps",
Herbie Ong800c9902018-12-06 15:28:53 -0800552 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800553 Int32ToStr: map[int32]string{},
554 Sfixed64ToBool: map[int64]bool{},
555 BoolToUint32: map[bool]uint32{},
556 Uint64ToEnum: map[uint64]pb2.Enum{},
557 StrToNested: map[string]*pb2.Nested{},
558 StrToOneofs: map[string]*pb2.Oneofs{},
Herbie Ong800c9902018-12-06 15:28:53 -0800559 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800560 want: "\n",
561 }, {
562 desc: "map fields 1",
Herbie Ong800c9902018-12-06 15:28:53 -0800563 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800564 Int32ToStr: map[int32]string{
565 -101: "-101",
566 0xff: "0xff",
567 0: "zero",
568 },
569 Sfixed64ToBool: map[int64]bool{
570 0xcafe: true,
571 0: false,
572 },
573 BoolToUint32: map[bool]uint32{
574 true: 42,
575 false: 101,
576 },
Herbie Ong800c9902018-12-06 15:28:53 -0800577 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800578 want: `int32_to_str: {
579 key: -101
580 value: "-101"
581}
582int32_to_str: {
583 key: 0
584 value: "zero"
585}
586int32_to_str: {
587 key: 255
588 value: "0xff"
589}
590sfixed64_to_bool: {
591 key: 0
592 value: false
593}
594sfixed64_to_bool: {
595 key: 51966
596 value: true
597}
598bool_to_uint32: {
599 key: false
600 value: 101
601}
602bool_to_uint32: {
603 key: true
604 value: 42
605}
606`,
607 }, {
608 desc: "map fields 2",
Herbie Ong800c9902018-12-06 15:28:53 -0800609 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800610 Uint64ToEnum: map[uint64]pb2.Enum{
611 1: pb2.Enum_FIRST,
612 2: pb2.Enum_SECOND,
613 10: pb2.Enum_TENTH,
614 },
Herbie Ong800c9902018-12-06 15:28:53 -0800615 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800616 want: `uint64_to_enum: {
617 key: 1
618 value: FIRST
619}
620uint64_to_enum: {
621 key: 2
622 value: SECOND
623}
624uint64_to_enum: {
625 key: 10
626 value: TENTH
627}
628`,
629 }, {
630 desc: "map fields 3",
Herbie Ong800c9902018-12-06 15:28:53 -0800631 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800632 StrToNested: map[string]*pb2.Nested{
633 "nested_one": &pb2.Nested{
634 OptString: scalar.String("nested in a map"),
635 },
636 },
Herbie Ong800c9902018-12-06 15:28:53 -0800637 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800638 want: `str_to_nested: {
639 key: "nested_one"
640 value: {
641 opt_string: "nested in a map"
642 }
643}
644`,
645 }, {
646 desc: "map fields 4",
Herbie Ong800c9902018-12-06 15:28:53 -0800647 input: &pb2.Maps{
Herbie Ongcddf8192018-11-28 18:25:20 -0800648 StrToOneofs: map[string]*pb2.Oneofs{
649 "string": &pb2.Oneofs{
650 Union: &pb2.Oneofs_Str{
651 Str: "hello",
652 },
653 },
654 "nested": &pb2.Oneofs{
655 Union: &pb2.Oneofs_Msg{
656 Msg: &pb2.Nested{
657 OptString: scalar.String("nested oneof in map field value"),
658 },
659 },
660 },
661 },
Herbie Ong800c9902018-12-06 15:28:53 -0800662 },
Herbie Ongcddf8192018-11-28 18:25:20 -0800663 want: `str_to_oneofs: {
664 key: "nested"
665 value: {
666 msg: {
667 opt_string: "nested oneof in map field value"
668 }
669 }
670}
671str_to_oneofs: {
672 key: "string"
673 value: {
674 str: "hello"
675 }
676}
677`,
678 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800679 desc: "proto2 required fields not set",
680 input: &pb2.Requireds{},
681 want: "\n",
682 wantErr: true,
Herbie Ongcddf8192018-11-28 18:25:20 -0800683 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800684 desc: "proto2 required fields partially set",
685 input: &pb2.Requireds{
686 ReqBool: scalar.Bool(false),
687 ReqFixed32: scalar.Uint32(47),
688 ReqSfixed64: scalar.Int64(0xbeefcafe),
689 ReqDouble: scalar.Float64(math.NaN()),
690 ReqString: scalar.String("hello"),
691 ReqEnum: pb2.Enum_FIRST.Enum(),
692 },
693 want: `req_bool: false
694req_fixed32: 47
695req_sfixed64: 3203386110
696req_double: nan
697req_string: "hello"
698req_enum: FIRST
699`,
700 wantErr: true,
701 }, {
702 desc: "proto2 required fields all set",
703 input: &pb2.Requireds{
704 ReqBool: scalar.Bool(false),
705 ReqFixed32: scalar.Uint32(0),
706 ReqFixed64: scalar.Uint64(0),
707 ReqSfixed32: scalar.Int32(0),
708 ReqSfixed64: scalar.Int64(0),
709 ReqFloat: scalar.Float32(0),
710 ReqDouble: scalar.Float64(0),
711 ReqString: scalar.String(""),
712 ReqEnum: pb2.Enum_UNKNOWN.Enum(),
713 ReqBytes: []byte{},
714 ReqNested: &pb2.Nested{},
715 },
716 want: `req_bool: false
717req_fixed32: 0
718req_fixed64: 0
719req_sfixed32: 0
720req_sfixed64: 0
721req_float: 0
722req_double: 0
723req_string: ""
724req_bytes: ""
725req_enum: UNKNOWN
726req_nested: {}
Herbie Ongcddf8192018-11-28 18:25:20 -0800727`,
728 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800729 desc: "indirect required field",
730 input: &pb2.IndirectRequired{
731 OptNested: &pb2.NestedWithRequired{},
732 },
733 want: "opt_nested: {}\n",
734 wantErr: true,
Herbie Ongcddf8192018-11-28 18:25:20 -0800735 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800736 desc: "indirect required field in empty repeated",
737 input: &pb2.IndirectRequired{
738 RptNested: []*pb2.NestedWithRequired{},
739 },
740 want: "\n",
Herbie Ongcddf8192018-11-28 18:25:20 -0800741 }, {
Herbie Ong800c9902018-12-06 15:28:53 -0800742 desc: "indirect required field in repeated",
743 input: &pb2.IndirectRequired{
744 RptNested: []*pb2.NestedWithRequired{
745 &pb2.NestedWithRequired{},
Herbie Ongcddf8192018-11-28 18:25:20 -0800746 },
Herbie Ong800c9902018-12-06 15:28:53 -0800747 },
748 want: "rpt_nested: {}\n",
749 wantErr: true,
750 }, {
751 desc: "indirect required field in empty map",
752 input: &pb2.IndirectRequired{
753 StrToNested: map[string]*pb2.NestedWithRequired{},
754 },
755 want: "\n",
756 }, {
757 desc: "indirect required field in map",
758 input: &pb2.IndirectRequired{
759 StrToNested: map[string]*pb2.NestedWithRequired{
760 "fail": &pb2.NestedWithRequired{},
761 },
762 },
763 want: `str_to_nested: {
764 key: "fail"
765 value: {}
Herbie Ongcddf8192018-11-28 18:25:20 -0800766}
767`,
Herbie Ong800c9902018-12-06 15:28:53 -0800768 wantErr: true,
Herbie Ong20a1d312018-12-11 21:08:58 -0800769 }, {
770 desc: "unknown varint and fixed types",
771 input: &pb2.Scalars{
772 OptString: scalar.String("this message contains unknown fields"),
773 XXX_unrecognized: pack.Message{
774 pack.Tag{101, pack.VarintType}, pack.Bool(true),
775 pack.Tag{102, pack.VarintType}, pack.Varint(0xff),
776 pack.Tag{103, pack.Fixed32Type}, pack.Uint32(47),
777 pack.Tag{104, pack.Fixed64Type}, pack.Int64(0xdeadbeef),
778 }.Marshal(),
779 },
780 want: `opt_string: "this message contains unknown fields"
781101: 1
782102: 255
783103: 47
784104: 3735928559
785`,
786 }, {
787 desc: "unknown length-delimited",
788 input: &pb2.Scalars{
789 XXX_unrecognized: pack.Message{
790 pack.Tag{101, pack.BytesType}, pack.LengthPrefix{pack.Bool(true), pack.Bool(false)},
791 pack.Tag{102, pack.BytesType}, pack.String("hello world"),
792 pack.Tag{103, pack.BytesType}, pack.Bytes("\xe4\xb8\x96\xe7\x95\x8c"),
793 }.Marshal(),
794 },
795 want: `101: "\x01\x00"
796102: "hello world"
797103: "世界"
798`,
799 }, {
800 desc: "unknown group type",
801 input: &pb2.Scalars{
802 XXX_unrecognized: pack.Message{
803 pack.Tag{101, pack.StartGroupType}, pack.Tag{101, pack.EndGroupType},
804 pack.Tag{102, pack.StartGroupType},
805 pack.Tag{101, pack.VarintType}, pack.Bool(false),
806 pack.Tag{102, pack.BytesType}, pack.String("inside a group"),
807 pack.Tag{102, pack.EndGroupType},
808 }.Marshal(),
809 },
810 want: `101: {}
811102: {
812 101: 0
813 102: "inside a group"
814}
815`,
816 }, {
817 desc: "unknown unpack repeated field",
818 input: &pb2.Scalars{
819 XXX_unrecognized: pack.Message{
820 pack.Tag{101, pack.BytesType}, pack.LengthPrefix{pack.Bool(true), pack.Bool(false), pack.Bool(true)},
821 pack.Tag{102, pack.BytesType}, pack.String("hello"),
822 pack.Tag{101, pack.VarintType}, pack.Bool(true),
823 pack.Tag{102, pack.BytesType}, pack.String("世界"),
824 }.Marshal(),
825 },
826 want: `101: "\x01\x00\x01"
827101: 1
828102: "hello"
829102: "世界"
830`,
Herbie Ongcf253082018-12-17 17:13:07 -0800831 }, {
832 desc: "extensions of non-repeated fields",
833 input: func() proto.Message {
834 m := &pb2.Extensions{
835 OptString: scalar.String("non-extension field"),
836 OptBool: scalar.Bool(true),
837 OptInt32: scalar.Int32(42),
838 }
839 setExtension(m, pb2.E_OptExtBool, true)
840 setExtension(m, pb2.E_OptExtString, "extension field")
841 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TENTH)
842 setExtension(m, pb2.E_OptExtNested, &pb2.Nested{
843 OptString: scalar.String("nested in an extension"),
844 OptNested: &pb2.Nested{
845 OptString: scalar.String("another nested in an extension"),
846 },
847 })
848 return m
849 }(),
850 want: `opt_string: "non-extension field"
851opt_bool: true
852opt_int32: 42
853[pb2.opt_ext_bool]: true
854[pb2.opt_ext_enum]: TENTH
855[pb2.opt_ext_nested]: {
856 opt_string: "nested in an extension"
857 opt_nested: {
858 opt_string: "another nested in an extension"
859 }
860}
861[pb2.opt_ext_string]: "extension field"
862`,
863 }, {
864 desc: "registered extension but not set",
865 input: func() proto.Message {
866 m := &pb2.Extensions{}
867 setExtension(m, pb2.E_OptExtNested, nil)
868 return m
869 }(),
870 want: "\n",
871 }, {
872 desc: "extensions of repeated fields",
873 input: func() proto.Message {
874 m := &pb2.Extensions{}
875 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TENTH, 101, pb2.Enum_FIRST})
876 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
877 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
878 &pb2.Nested{OptString: scalar.String("one")},
879 &pb2.Nested{OptString: scalar.String("two")},
880 &pb2.Nested{OptString: scalar.String("three")},
881 })
882 return m
883 }(),
884 want: `[pb2.rpt_ext_enum]: TENTH
885[pb2.rpt_ext_enum]: 101
886[pb2.rpt_ext_enum]: FIRST
887[pb2.rpt_ext_fixed32]: 42
888[pb2.rpt_ext_fixed32]: 47
889[pb2.rpt_ext_nested]: {
890 opt_string: "one"
891}
892[pb2.rpt_ext_nested]: {
893 opt_string: "two"
894}
895[pb2.rpt_ext_nested]: {
896 opt_string: "three"
897}
898`,
899 }, {
900 desc: "extensions of non-repeated fields in another message",
901 input: func() proto.Message {
902 m := &pb2.Extensions{}
903 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
904 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
905 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TENTH)
906 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
907 OptString: scalar.String("nested in an extension"),
908 OptNested: &pb2.Nested{
909 OptString: scalar.String("another nested in an extension"),
910 },
911 })
912 return m
913 }(),
914 want: `[pb2.ExtensionsContainer.opt_ext_bool]: true
915[pb2.ExtensionsContainer.opt_ext_enum]: TENTH
916[pb2.ExtensionsContainer.opt_ext_nested]: {
917 opt_string: "nested in an extension"
918 opt_nested: {
919 opt_string: "another nested in an extension"
920 }
921}
922[pb2.ExtensionsContainer.opt_ext_string]: "extension field"
923`,
924 }, {
925 desc: "extensions of repeated fields in another message",
926 input: func() proto.Message {
927 m := &pb2.Extensions{
928 OptString: scalar.String("non-extension field"),
929 OptBool: scalar.Bool(true),
930 OptInt32: scalar.Int32(42),
931 }
932 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TENTH, 101, pb2.Enum_FIRST})
933 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
934 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
935 &pb2.Nested{OptString: scalar.String("one")},
936 &pb2.Nested{OptString: scalar.String("two")},
937 &pb2.Nested{OptString: scalar.String("three")},
938 })
939 return m
940 }(),
941 want: `opt_string: "non-extension field"
942opt_bool: true
943opt_int32: 42
944[pb2.ExtensionsContainer.rpt_ext_enum]: TENTH
945[pb2.ExtensionsContainer.rpt_ext_enum]: 101
946[pb2.ExtensionsContainer.rpt_ext_enum]: FIRST
947[pb2.ExtensionsContainer.rpt_ext_nested]: {
948 opt_string: "one"
949}
950[pb2.ExtensionsContainer.rpt_ext_nested]: {
951 opt_string: "two"
952}
953[pb2.ExtensionsContainer.rpt_ext_nested]: {
954 opt_string: "three"
955}
956[pb2.ExtensionsContainer.rpt_ext_string]: "hello"
957[pb2.ExtensionsContainer.rpt_ext_string]: "world"
958`,
959 /* TODO: test for MessageSet
960 }, {
961 desc: "MessageSet",
962 input: func() proto.Message {
963 m := &pb2.MessageSet{}
964 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
965 OptString: scalar.String("a messageset extension"),
966 })
967 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
968 OptString: scalar.String("not a messageset extension"),
969 })
970 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
971 OptString: scalar.String("just a regular extension"),
972 })
973 return m
974 }(),
975 want: `[pb2.MessageSetExtension]: {
976 opt_string: "a messageset extension"
977 }
978 [pb2.MessageSetExtension.ext_nested]: {
979 opt_string: "just a regular extension"
980 }
981 [pb2.MessageSetExtension.not_message_set_extension]: {
982 opt_string: "not a messageset extension"
983 }
984 `,
985 */
Herbie Ongf42b55f2019-01-02 15:46:07 -0800986 }, {
Herbie Ong66c365c2019-01-04 14:08:41 -0800987 desc: "Any message not expanded",
988 mo: textpb.MarshalOptions{
989 Resolver: preg.NewTypes(),
990 },
Herbie Ongf42b55f2019-01-02 15:46:07 -0800991 input: func() proto.Message {
992 m := &pb2.Nested{
993 OptString: scalar.String("embedded inside Any"),
994 OptNested: &pb2.Nested{
995 OptString: scalar.String("inception"),
996 },
997 }
998 // TODO: Switch to V2 marshal when ready.
999 b, err := protoV1.Marshal(m)
1000 if err != nil {
1001 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1002 }
Herbie Ong66c365c2019-01-04 14:08:41 -08001003 return wrapAnyPB(&anypb.Any{
1004 TypeUrl: "pb2.Nested",
Herbie Ongf42b55f2019-01-02 15:46:07 -08001005 Value: b,
Herbie Ong66c365c2019-01-04 14:08:41 -08001006 })
Herbie Ongf42b55f2019-01-02 15:46:07 -08001007 }(),
1008 want: `type_url: "pb2.Nested"
1009value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
1010`,
1011 }, {
Herbie Ong66c365c2019-01-04 14:08:41 -08001012 desc: "Any message expanded",
1013 mo: textpb.MarshalOptions{
1014 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1015 },
Herbie Ongf42b55f2019-01-02 15:46:07 -08001016 input: func() proto.Message {
1017 m := &pb2.Nested{
1018 OptString: scalar.String("embedded inside Any"),
1019 OptNested: &pb2.Nested{
1020 OptString: scalar.String("inception"),
1021 },
1022 }
1023 // TODO: Switch to V2 marshal when ready.
1024 b, err := protoV1.Marshal(m)
1025 if err != nil {
1026 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1027 }
Herbie Ong66c365c2019-01-04 14:08:41 -08001028 return wrapAnyPB(&anypb.Any{
1029 TypeUrl: "foo/pb2.Nested",
Herbie Ongf42b55f2019-01-02 15:46:07 -08001030 Value: b,
Herbie Ong66c365c2019-01-04 14:08:41 -08001031 })
Herbie Ongf42b55f2019-01-02 15:46:07 -08001032 }(),
Herbie Ong66c365c2019-01-04 14:08:41 -08001033 want: `[foo/pb2.Nested]: {
Herbie Ongf42b55f2019-01-02 15:46:07 -08001034 opt_string: "embedded inside Any"
1035 opt_nested: {
1036 opt_string: "inception"
1037 }
1038}
1039`,
1040 }, {
Herbie Ong66c365c2019-01-04 14:08:41 -08001041 desc: "Any message expanded with missing required error",
1042 mo: textpb.MarshalOptions{
1043 Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
1044 },
Herbie Ongf42b55f2019-01-02 15:46:07 -08001045 input: func() proto.Message {
1046 m := &pb2.PartialRequired{
1047 OptString: scalar.String("embedded inside Any"),
1048 }
1049 // TODO: Switch to V2 marshal when ready.
1050 b, err := protoV1.Marshal(m)
1051 // Ignore required not set error.
1052 if _, ok := err.(*protoV1.RequiredNotSetError); !ok {
1053 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1054 }
Herbie Ong66c365c2019-01-04 14:08:41 -08001055 return wrapAnyPB(&anypb.Any{
Herbie Ongf42b55f2019-01-02 15:46:07 -08001056 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1057 Value: b,
Herbie Ong66c365c2019-01-04 14:08:41 -08001058 })
Herbie Ongf42b55f2019-01-02 15:46:07 -08001059 }(),
1060 want: `[pb2.PartialRequired]: {
1061 opt_string: "embedded inside Any"
1062}
1063`,
1064 wantErr: true,
1065 }, {
Herbie Ong66c365c2019-01-04 14:08:41 -08001066 desc: "Any message with invalid value",
1067 mo: textpb.MarshalOptions{
1068 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1069 },
1070 input: wrapAnyPB(&anypb.Any{
1071 TypeUrl: "foo/pb2.Nested",
1072 Value: dhex("80"),
1073 }),
1074 want: `type_url: "foo/pb2.Nested"
Herbie Onga94f78c2019-01-03 15:39:58 -08001075value: "\x80"
1076`,
Herbie Ongcddf8192018-11-28 18:25:20 -08001077 }}
1078
1079 for _, tt := range tests {
1080 tt := tt
1081 t.Run(tt.desc, func(t *testing.T) {
1082 t.Parallel()
Herbie Ongf42b55f2019-01-02 15:46:07 -08001083 b, err := tt.mo.Marshal(tt.input)
Herbie Ongcddf8192018-11-28 18:25:20 -08001084 if err != nil && !tt.wantErr {
Herbie Ongf42b55f2019-01-02 15:46:07 -08001085 t.Errorf("Marshal() returned error: %v\n", err)
Herbie Ongcddf8192018-11-28 18:25:20 -08001086 }
Herbie Ong800c9902018-12-06 15:28:53 -08001087 if err == nil && tt.wantErr {
Herbie Ongf42b55f2019-01-02 15:46:07 -08001088 t.Error("Marshal() got nil error, want error\n")
Herbie Ongcddf8192018-11-28 18:25:20 -08001089 }
Herbie Ong800c9902018-12-06 15:28:53 -08001090 got := string(b)
1091 if tt.want != "" && got != tt.want {
1092 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1093 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
Herbie Ongcddf8192018-11-28 18:25:20 -08001094 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1095 }
1096 }
1097 })
1098 }
1099}