1
2
3
4
5 package prototext_test
6
7 import (
8 "bytes"
9 "math"
10 "testing"
11
12 "github.com/google/go-cmp/cmp"
13
14 "google.golang.org/protobuf/encoding/prototext"
15 "google.golang.org/protobuf/internal/detrand"
16 "google.golang.org/protobuf/internal/flags"
17 "google.golang.org/protobuf/proto"
18 "google.golang.org/protobuf/reflect/protoregistry"
19 "google.golang.org/protobuf/testing/protopack"
20
21 pb2 "google.golang.org/protobuf/internal/testprotos/textpb2"
22 pb3 "google.golang.org/protobuf/internal/testprotos/textpb3"
23 pbeditions "google.golang.org/protobuf/internal/testprotos/textpbeditions"
24 "google.golang.org/protobuf/types/known/anypb"
25 )
26
27 func init() {
28
29 detrand.Disable()
30 }
31
32 func TestMarshal(t *testing.T) {
33 tests := []struct {
34 desc string
35 mo prototext.MarshalOptions
36 input proto.Message
37 want string
38 wantErr bool
39 skip bool
40 }{{
41 desc: "proto2 optional scalars not set",
42 input: &pb2.Scalars{},
43 want: "",
44 }, {
45 desc: "protoeditions optional scalars not set",
46 input: &pbeditions.Scalars{},
47 want: "",
48 }, {
49 desc: "proto3 scalars not set",
50 input: &pb3.Scalars{},
51 want: "",
52 }, {
53 desc: "proto3 optional not set",
54 input: &pb3.Proto3Optional{},
55 want: "",
56 }, {
57 desc: "protoeditions implicit not set",
58 input: &pbeditions.ImplicitScalars{},
59 want: "",
60 }, {
61 desc: "proto2 optional scalars set to zero values",
62 input: &pb2.Scalars{
63 OptBool: proto.Bool(false),
64 OptInt32: proto.Int32(0),
65 OptInt64: proto.Int64(0),
66 OptUint32: proto.Uint32(0),
67 OptUint64: proto.Uint64(0),
68 OptSint32: proto.Int32(0),
69 OptSint64: proto.Int64(0),
70 OptFixed32: proto.Uint32(0),
71 OptFixed64: proto.Uint64(0),
72 OptSfixed32: proto.Int32(0),
73 OptSfixed64: proto.Int64(0),
74 OptFloat: proto.Float32(0),
75 OptDouble: proto.Float64(0),
76 OptBytes: []byte{},
77 OptString: proto.String(""),
78 },
79 want: `opt_bool: false
80 opt_int32: 0
81 opt_int64: 0
82 opt_uint32: 0
83 opt_uint64: 0
84 opt_sint32: 0
85 opt_sint64: 0
86 opt_fixed32: 0
87 opt_fixed64: 0
88 opt_sfixed32: 0
89 opt_sfixed64: 0
90 opt_float: 0
91 opt_double: 0
92 opt_bytes: ""
93 opt_string: ""
94 `,
95 }, {
96 desc: "protoeditions optional scalars set to zero values",
97 input: &pbeditions.Scalars{
98 OptBool: proto.Bool(false),
99 OptInt32: proto.Int32(0),
100 OptInt64: proto.Int64(0),
101 OptUint32: proto.Uint32(0),
102 OptUint64: proto.Uint64(0),
103 OptSint32: proto.Int32(0),
104 OptSint64: proto.Int64(0),
105 OptFixed32: proto.Uint32(0),
106 OptFixed64: proto.Uint64(0),
107 OptSfixed32: proto.Int32(0),
108 OptSfixed64: proto.Int64(0),
109 OptFloat: proto.Float32(0),
110 OptDouble: proto.Float64(0),
111 OptBytes: []byte{},
112 OptString: proto.String(""),
113 },
114 want: `opt_bool: false
115 opt_int32: 0
116 opt_int64: 0
117 opt_uint32: 0
118 opt_uint64: 0
119 opt_sint32: 0
120 opt_sint64: 0
121 opt_fixed32: 0
122 opt_fixed64: 0
123 opt_sfixed32: 0
124 opt_sfixed64: 0
125 opt_float: 0
126 opt_double: 0
127 opt_bytes: ""
128 opt_string: ""
129 `,
130 }, {
131 desc: "proto3 optional set to zero values",
132 input: &pb3.Proto3Optional{
133 OptBool: proto.Bool(false),
134 OptInt32: proto.Int32(0),
135 OptInt64: proto.Int64(0),
136 OptUint32: proto.Uint32(0),
137 OptUint64: proto.Uint64(0),
138 OptFloat: proto.Float32(0),
139 OptDouble: proto.Float64(0),
140 OptString: proto.String(""),
141 OptBytes: []byte{},
142 OptEnum: pb3.Enum_ZERO.Enum(),
143 OptMessage: &pb3.Nested{},
144 },
145 want: `opt_bool: false
146 opt_int32: 0
147 opt_int64: 0
148 opt_uint32: 0
149 opt_uint64: 0
150 opt_float: 0
151 opt_double: 0
152 opt_string: ""
153 opt_bytes: ""
154 opt_enum: ZERO
155 opt_message: {}
156 `,
157 }, {
158 desc: "proto3 scalars set to zero values",
159 input: &pb3.Scalars{
160 SBool: false,
161 SInt32: 0,
162 SInt64: 0,
163 SUint32: 0,
164 SUint64: 0,
165 SSint32: 0,
166 SSint64: 0,
167 SFixed32: 0,
168 SFixed64: 0,
169 SSfixed32: 0,
170 SSfixed64: 0,
171 SFloat: 0,
172 SDouble: 0,
173 SBytes: []byte{},
174 SString: "",
175 },
176 want: "",
177 }, {
178 desc: "protoeditions implicit scalars set to zero values",
179 input: &pbeditions.ImplicitScalars{
180 SBool: false,
181 SInt32: 0,
182 SInt64: 0,
183 SUint32: 0,
184 SUint64: 0,
185 SSint32: 0,
186 SSint64: 0,
187 SFixed32: 0,
188 SFixed64: 0,
189 SSfixed32: 0,
190 SSfixed64: 0,
191 SFloat: 0,
192 SDouble: 0,
193 SBytes: []byte{},
194 SString: "",
195 },
196 want: "",
197 }, {
198 desc: "proto2 optional scalars set to some values",
199 input: &pb2.Scalars{
200 OptBool: proto.Bool(true),
201 OptInt32: proto.Int32(0xff),
202 OptInt64: proto.Int64(0xdeadbeef),
203 OptUint32: proto.Uint32(47),
204 OptUint64: proto.Uint64(0xdeadbeef),
205 OptSint32: proto.Int32(-1001),
206 OptSint64: proto.Int64(-0xffff),
207 OptFixed64: proto.Uint64(64),
208 OptSfixed32: proto.Int32(-32),
209 OptFloat: proto.Float32(1.02),
210 OptDouble: proto.Float64(1.0199999809265137),
211 OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
212 OptString: proto.String("谷歌"),
213 },
214 want: `opt_bool: true
215 opt_int32: 255
216 opt_int64: 3735928559
217 opt_uint32: 47
218 opt_uint64: 3735928559
219 opt_sint32: -1001
220 opt_sint64: -65535
221 opt_fixed64: 64
222 opt_sfixed32: -32
223 opt_float: 1.02
224 opt_double: 1.0199999809265137
225 opt_bytes: "谷歌"
226 opt_string: "谷歌"
227 `,
228 }, {
229 desc: "proto editions optional scalars set to some values",
230 input: &pbeditions.Scalars{
231 OptBool: proto.Bool(true),
232 OptInt32: proto.Int32(0xff),
233 OptInt64: proto.Int64(0xdeadbeef),
234 OptUint32: proto.Uint32(47),
235 OptUint64: proto.Uint64(0xdeadbeef),
236 OptSint32: proto.Int32(-1001),
237 OptSint64: proto.Int64(-0xffff),
238 OptFixed64: proto.Uint64(64),
239 OptSfixed32: proto.Int32(-32),
240 OptFloat: proto.Float32(1.02),
241 OptDouble: proto.Float64(1.0199999809265137),
242 OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
243 OptString: proto.String("谷歌"),
244 },
245 want: `opt_bool: true
246 opt_int32: 255
247 opt_int64: 3735928559
248 opt_uint32: 47
249 opt_uint64: 3735928559
250 opt_sint32: -1001
251 opt_sint64: -65535
252 opt_fixed64: 64
253 opt_sfixed32: -32
254 opt_float: 1.02
255 opt_double: 1.0199999809265137
256 opt_bytes: "谷歌"
257 opt_string: "谷歌"
258 `,
259 }, {
260 desc: "proto2 string with invalid UTF-8",
261 input: &pb2.Scalars{
262 OptString: proto.String("abc\xff"),
263 },
264 want: `opt_string: "abc\xff"
265 `,
266 }, {
267 desc: "protoeditions string with invalid UTF-8",
268 input: &pbeditions.Scalars{
269 OptString: proto.String("abc\xff"),
270 },
271 want: `opt_string: "abc\xff"
272 `,
273 }, {
274 desc: "proto3 string with invalid UTF-8",
275 input: &pb3.Scalars{
276 SString: "abc\xff",
277 },
278 wantErr: true,
279 }, {
280 desc: "protoeditions utf8 validated string with invalid UTF-8",
281 input: &pbeditions.UTF8Validated{
282 ValidatedString: "abc\xff",
283 },
284 wantErr: true,
285 }, {
286 desc: "float nan",
287 input: &pb3.Scalars{
288 SFloat: float32(math.NaN()),
289 },
290 want: "s_float: nan\n",
291 }, {
292 desc: "float positive infinity",
293 input: &pb3.Scalars{
294 SFloat: float32(math.Inf(1)),
295 },
296 want: "s_float: inf\n",
297 }, {
298 desc: "float negative infinity",
299 input: &pb3.Scalars{
300 SFloat: float32(math.Inf(-1)),
301 },
302 want: "s_float: -inf\n",
303 }, {
304 desc: "double nan",
305 input: &pb3.Scalars{
306 SDouble: math.NaN(),
307 },
308 want: "s_double: nan\n",
309 }, {
310 desc: "double positive infinity",
311 input: &pb3.Scalars{
312 SDouble: math.Inf(1),
313 },
314 want: "s_double: inf\n",
315 }, {
316 desc: "double negative infinity",
317 input: &pb3.Scalars{
318 SDouble: math.Inf(-1),
319 },
320 want: "s_double: -inf\n",
321 }, {
322 desc: "proto2 enum not set",
323 input: &pb2.Enums{},
324 want: "",
325 }, {
326 desc: "proto2 enum set to zero value",
327 input: &pb2.Enums{
328 OptEnum: pb2.Enum(0).Enum(),
329 OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
330 },
331 want: `opt_enum: 0
332 opt_nested_enum: 0
333 `,
334 }, {
335 desc: "protoeditions enum set to zero value",
336 input: &pbeditions.Enums{
337 OptEnum: pbeditions.Enum(0).Enum(),
338 OptNestedEnum: pbeditions.Enums_NestedEnum(0).Enum(),
339 },
340 want: `opt_enum: 0
341 opt_nested_enum: 0
342 `,
343 }, {
344 desc: "proto2 enum",
345 input: &pb2.Enums{
346 OptEnum: pb2.Enum_ONE.Enum(),
347 OptNestedEnum: pb2.Enums_UNO.Enum(),
348 },
349 want: `opt_enum: ONE
350 opt_nested_enum: UNO
351 `,
352 }, {
353 desc: "protoeditions enum",
354 input: &pbeditions.Enums{
355 OptEnum: pbeditions.Enum_ONE.Enum(),
356 OptNestedEnum: pbeditions.Enums_UNO.Enum(),
357 },
358 want: `opt_enum: ONE
359 opt_nested_enum: UNO
360 `,
361 }, {
362 desc: "proto2 enum set to numeric values",
363 input: &pb2.Enums{
364 OptEnum: pb2.Enum(2).Enum(),
365 OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
366 },
367 want: `opt_enum: TWO
368 opt_nested_enum: DOS
369 `,
370 }, {
371 desc: "proto2 enum set to unnamed numeric values",
372 input: &pb2.Enums{
373 OptEnum: pb2.Enum(101).Enum(),
374 OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
375 },
376 want: `opt_enum: 101
377 opt_nested_enum: -101
378 `,
379 }, {
380 desc: "protoeditions enum set to unnamed numeric values",
381 input: &pbeditions.Enums{
382 OptEnum: pbeditions.Enum(101).Enum(),
383 OptNestedEnum: pbeditions.Enums_NestedEnum(-101).Enum(),
384 },
385 want: `opt_enum: 101
386 opt_nested_enum: -101
387 `,
388 }, {
389 desc: "proto3 enum not set",
390 input: &pb3.Enums{},
391 want: "",
392 }, {
393 desc: "proto3 enum set to zero value",
394 input: &pb3.Enums{
395 SEnum: pb3.Enum_ZERO,
396 SNestedEnum: pb3.Enums_CERO,
397 },
398 want: "",
399 }, {
400 desc: "protoeditions implicit enum field set to default value",
401 input: &pbeditions.Enums{
402 ImplicitEnum: pbeditions.OpenEnum_UNKNOWN,
403 },
404 want: "",
405 }, {
406 desc: "protoeditions implicit enum field",
407 input: &pbeditions.Enums{
408 ImplicitEnum: pbeditions.OpenEnum_EINS,
409 ImplicitNestedEnum: pbeditions.Enums_ZEHN,
410 },
411 want: `implicit_enum: EINS
412 implicit_nested_enum: ZEHN
413 `,
414 }, {
415 desc: "proto3 enum",
416 input: &pb3.Enums{
417 SEnum: pb3.Enum_ONE,
418 SNestedEnum: pb3.Enums_UNO,
419 },
420 want: `s_enum: ONE
421 s_nested_enum: UNO
422 `,
423 }, {
424 desc: "proto3 enum set to numeric values",
425 input: &pb3.Enums{
426 SEnum: 2,
427 SNestedEnum: 2,
428 },
429 want: `s_enum: TWO
430 s_nested_enum: DOS
431 `,
432 }, {
433 desc: "protoeditions implicit enum set to unnamed numeric values",
434 input: &pbeditions.Enums{
435 ImplicitEnum: -47,
436 ImplicitNestedEnum: 47,
437 },
438 want: `implicit_enum: -47
439 implicit_nested_enum: 47
440 `,
441 }, {
442 desc: "proto3 enum set to unnamed numeric values",
443 input: &pb3.Enums{
444 SEnum: -47,
445 SNestedEnum: 47,
446 },
447 want: `s_enum: -47
448 s_nested_enum: 47
449 `,
450 }, {
451 desc: "proto2 nested message not set",
452 input: &pb2.Nests{},
453 want: "",
454 }, {
455 desc: "proto2 nested message set to empty",
456 input: &pb2.Nests{
457 OptNested: &pb2.Nested{},
458 Optgroup: &pb2.Nests_OptGroup{},
459 },
460 want: `opt_nested: {}
461 OptGroup: {}
462 `,
463 }, {
464 desc: "protoeditions nested message set to empty",
465 input: &pbeditions.Nests{
466 OptNested: &pbeditions.Nested{},
467 Optgroup: &pbeditions.Nests_OptGroup{},
468 },
469 want: `opt_nested: {}
470 OptGroup: {}
471 `,
472 }, {
473 desc: "proto2 nested messages",
474 input: &pb2.Nests{
475 OptNested: &pb2.Nested{
476 OptString: proto.String("nested message"),
477 OptNested: &pb2.Nested{
478 OptString: proto.String("another nested message"),
479 },
480 },
481 },
482 want: `opt_nested: {
483 opt_string: "nested message"
484 opt_nested: {
485 opt_string: "another nested message"
486 }
487 }
488 `,
489 }, {
490 desc: "protoeditions nested messages",
491 input: &pbeditions.Nests{
492 OptNested: &pbeditions.Nested{
493 OptString: proto.String("nested message"),
494 OptNested: &pbeditions.Nested{
495 OptString: proto.String("another nested message"),
496 },
497 },
498 },
499 want: `opt_nested: {
500 opt_string: "nested message"
501 opt_nested: {
502 opt_string: "another nested message"
503 }
504 }
505 `,
506 }, {
507 desc: "proto2 groups",
508 input: &pb2.Nests{
509 Optgroup: &pb2.Nests_OptGroup{
510 OptString: proto.String("inside a group"),
511 OptNested: &pb2.Nested{
512 OptString: proto.String("nested message inside a group"),
513 },
514 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
515 OptFixed32: proto.Uint32(47),
516 },
517 },
518 },
519 want: `OptGroup: {
520 opt_string: "inside a group"
521 opt_nested: {
522 opt_string: "nested message inside a group"
523 }
524 OptNestedGroup: {
525 opt_fixed32: 47
526 }
527 }
528 `,
529 }, {
530 desc: "protoeditions group-like delimited encoded message field",
531 input: &pbeditions.Nests{
532 Optgroup: &pbeditions.Nests_OptGroup{
533 OptString: proto.String("inside a group"),
534 OptNested: &pbeditions.Nested{
535 OptString: proto.String("nested message inside a group"),
536 },
537 Optnestedgroup: &pbeditions.Nests_OptGroup_OptNestedGroup{
538 OptFixed32: proto.Uint32(47),
539 },
540 },
541 },
542 want: `OptGroup: {
543 opt_string: "inside a group"
544 opt_nested: {
545 opt_string: "nested message inside a group"
546 }
547 OptNestedGroup: {
548 opt_fixed32: 47
549 }
550 }
551 `,
552 }, {
553 desc: "protoeditions delimited encoded message field",
554 input: &pbeditions.Nests{
555 DelimitedField: &pbeditions.Nests_OptGroup{
556 OptString: proto.String("second group"),
557 OptNested: &pbeditions.Nested{
558 OptString: proto.String("second nested message inside a group"),
559 },
560 NestedDelimitedField: &pbeditions.Nests_OptGroup_OptNestedGroup{
561 OptFixed32: proto.Uint32(47),
562 },
563 },
564 },
565 want: `delimited_field: {
566 opt_string: "second group"
567 opt_nested: {
568 opt_string: "second nested message inside a group"
569 }
570 nested_delimited_field: {
571 opt_fixed32: 47
572 }
573 }
574 `,
575 }, {
576 desc: "proto3 nested message not set",
577 input: &pb3.Nests{},
578 want: "",
579 }, {
580 desc: "proto3 nested message set to empty",
581 input: &pb3.Nests{
582 SNested: &pb3.Nested{},
583 },
584 want: "s_nested: {}\n",
585 }, {
586 desc: "proto3 nested message",
587 input: &pb3.Nests{
588 SNested: &pb3.Nested{
589 SString: "nested message",
590 SNested: &pb3.Nested{
591 SString: "another nested message",
592 },
593 },
594 },
595 want: `s_nested: {
596 s_string: "nested message"
597 s_nested: {
598 s_string: "another nested message"
599 }
600 }
601 `,
602 }, {
603 desc: "proto3 nested message contains invalid UTF-8",
604 input: &pb3.Nests{
605 SNested: &pb3.Nested{
606 SString: "abc\xff",
607 },
608 },
609 wantErr: true,
610 }, {
611 desc: "protoeditions nested message contains invalid UTF-8",
612 input: &pbeditions.NestsUTF8Validated{
613 ValidatedMessage: &pbeditions.UTF8Validated{
614 ValidatedString: "abc\xff",
615 },
616 },
617 wantErr: true,
618 }, {
619 desc: "oneof not set",
620 input: &pb3.Oneofs{},
621 want: "",
622 }, {
623 desc: "oneof set to empty string",
624 input: &pb3.Oneofs{
625 Union: &pb3.Oneofs_OneofString{},
626 },
627 want: `oneof_string: ""
628 `,
629 }, {
630 desc: "oneof set to string",
631 input: &pb3.Oneofs{
632 Union: &pb3.Oneofs_OneofString{
633 OneofString: "hello",
634 },
635 },
636 want: `oneof_string: "hello"
637 `,
638 }, {
639 desc: "oneof set to enum",
640 input: &pb3.Oneofs{
641 Union: &pb3.Oneofs_OneofEnum{
642 OneofEnum: pb3.Enum_ZERO,
643 },
644 },
645 want: `oneof_enum: ZERO
646 `,
647 }, {
648 desc: "oneof set to empty message",
649 input: &pb3.Oneofs{
650 Union: &pb3.Oneofs_OneofNested{
651 OneofNested: &pb3.Nested{},
652 },
653 },
654 want: "oneof_nested: {}\n",
655 }, {
656 desc: "oneof set to message",
657 input: &pb3.Oneofs{
658 Union: &pb3.Oneofs_OneofNested{
659 OneofNested: &pb3.Nested{
660 SString: "nested message",
661 },
662 },
663 },
664 want: `oneof_nested: {
665 s_string: "nested message"
666 }
667 `,
668 }, {
669 desc: "repeated fields not set",
670 input: &pb2.Repeats{},
671 want: "",
672 }, {
673 desc: "repeated fields set to empty slices",
674 input: &pb2.Repeats{
675 RptBool: []bool{},
676 RptInt32: []int32{},
677 RptInt64: []int64{},
678 RptUint32: []uint32{},
679 RptUint64: []uint64{},
680 RptFloat: []float32{},
681 RptDouble: []float64{},
682 RptBytes: [][]byte{},
683 },
684 want: "",
685 }, {
686 desc: "repeated fields set to some values",
687 input: &pb2.Repeats{
688 RptBool: []bool{true, false, true, true},
689 RptInt32: []int32{1, 6, 0, 0},
690 RptInt64: []int64{-64, 47},
691 RptUint32: []uint32{0xff, 0xffff},
692 RptUint64: []uint64{0xdeadbeef},
693 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
694 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
695 RptString: []string{"hello", "世界"},
696 RptBytes: [][]byte{
697 []byte("hello"),
698 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
699 },
700 },
701 want: `rpt_bool: true
702 rpt_bool: false
703 rpt_bool: true
704 rpt_bool: true
705 rpt_int32: 1
706 rpt_int32: 6
707 rpt_int32: 0
708 rpt_int32: 0
709 rpt_int64: -64
710 rpt_int64: 47
711 rpt_uint32: 255
712 rpt_uint32: 65535
713 rpt_uint64: 3735928559
714 rpt_float: nan
715 rpt_float: inf
716 rpt_float: -inf
717 rpt_float: 1.034
718 rpt_double: nan
719 rpt_double: inf
720 rpt_double: -inf
721 rpt_double: 1.23e-308
722 rpt_string: "hello"
723 rpt_string: "世界"
724 rpt_bytes: "hello"
725 rpt_bytes: "世界"
726 `,
727 }, {
728 desc: "repeated proto2 contains invalid UTF-8",
729 input: &pb2.Repeats{
730 RptString: []string{"abc\xff"},
731 },
732 want: `rpt_string: "abc\xff"
733 `,
734 }, {
735 desc: "repeated proto3 contains invalid UTF-8",
736 input: &pb3.Repeats{
737 RptString: []string{"abc\xff"},
738 },
739 wantErr: true,
740 }, {
741 desc: "repeated enums",
742 input: &pb2.Enums{
743 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
744 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
745 },
746 want: `rpt_enum: ONE
747 rpt_enum: TWO
748 rpt_enum: TEN
749 rpt_enum: 42
750 rpt_nested_enum: DOS
751 rpt_nested_enum: 47
752 rpt_nested_enum: DIEZ
753 `,
754 }, {
755 desc: "repeated messages set to empty",
756 input: &pb2.Nests{
757 RptNested: []*pb2.Nested{},
758 Rptgroup: []*pb2.Nests_RptGroup{},
759 },
760 want: "",
761 }, {
762 desc: "repeated messages",
763 input: &pb2.Nests{
764 RptNested: []*pb2.Nested{
765 {
766 OptString: proto.String("repeat nested one"),
767 },
768 {
769 OptString: proto.String("repeat nested two"),
770 OptNested: &pb2.Nested{
771 OptString: proto.String("inside repeat nested two"),
772 },
773 },
774 {},
775 },
776 },
777 want: `rpt_nested: {
778 opt_string: "repeat nested one"
779 }
780 rpt_nested: {
781 opt_string: "repeat nested two"
782 opt_nested: {
783 opt_string: "inside repeat nested two"
784 }
785 }
786 rpt_nested: {}
787 `,
788 }, {
789 desc: "repeated messages contains nil value",
790 input: &pb2.Nests{
791 RptNested: []*pb2.Nested{nil, {}},
792 },
793 want: `rpt_nested: {}
794 rpt_nested: {}
795 `,
796 }, {
797 desc: "repeated groups",
798 input: &pb2.Nests{
799 Rptgroup: []*pb2.Nests_RptGroup{
800 {
801 RptString: []string{"hello", "world"},
802 },
803 {},
804 nil,
805 },
806 },
807 want: `RptGroup: {
808 rpt_string: "hello"
809 rpt_string: "world"
810 }
811 RptGroup: {}
812 RptGroup: {}
813 `,
814 }, {
815 desc: "map fields not set",
816 input: &pb3.Maps{},
817 want: "",
818 }, {
819 desc: "map fields set to empty",
820 input: &pb3.Maps{
821 Int32ToStr: map[int32]string{},
822 BoolToUint32: map[bool]uint32{},
823 Uint64ToEnum: map[uint64]pb3.Enum{},
824 StrToNested: map[string]*pb3.Nested{},
825 StrToOneofs: map[string]*pb3.Oneofs{},
826 },
827 want: "",
828 }, {
829 desc: "map fields 1",
830 input: &pb3.Maps{
831 Int32ToStr: map[int32]string{
832 -101: "-101",
833 0xff: "0xff",
834 0: "zero",
835 },
836 BoolToUint32: map[bool]uint32{
837 true: 42,
838 false: 101,
839 },
840 },
841 want: `int32_to_str: {
842 key: -101
843 value: "-101"
844 }
845 int32_to_str: {
846 key: 0
847 value: "zero"
848 }
849 int32_to_str: {
850 key: 255
851 value: "0xff"
852 }
853 bool_to_uint32: {
854 key: false
855 value: 101
856 }
857 bool_to_uint32: {
858 key: true
859 value: 42
860 }
861 `,
862 }, {
863 desc: "map fields 2",
864 input: &pb3.Maps{
865 Uint64ToEnum: map[uint64]pb3.Enum{
866 1: pb3.Enum_ONE,
867 2: pb3.Enum_TWO,
868 10: pb3.Enum_TEN,
869 47: 47,
870 },
871 },
872 want: `uint64_to_enum: {
873 key: 1
874 value: ONE
875 }
876 uint64_to_enum: {
877 key: 2
878 value: TWO
879 }
880 uint64_to_enum: {
881 key: 10
882 value: TEN
883 }
884 uint64_to_enum: {
885 key: 47
886 value: 47
887 }
888 `,
889 }, {
890 desc: "map fields 3",
891 input: &pb3.Maps{
892 StrToNested: map[string]*pb3.Nested{
893 "nested": &pb3.Nested{
894 SString: "nested in a map",
895 },
896 },
897 },
898 want: `str_to_nested: {
899 key: "nested"
900 value: {
901 s_string: "nested in a map"
902 }
903 }
904 `,
905 }, {
906 desc: "map fields 4",
907 input: &pb3.Maps{
908 StrToOneofs: map[string]*pb3.Oneofs{
909 "string": &pb3.Oneofs{
910 Union: &pb3.Oneofs_OneofString{
911 OneofString: "hello",
912 },
913 },
914 "nested": &pb3.Oneofs{
915 Union: &pb3.Oneofs_OneofNested{
916 OneofNested: &pb3.Nested{
917 SString: "nested oneof in map field value",
918 },
919 },
920 },
921 },
922 },
923 want: `str_to_oneofs: {
924 key: "nested"
925 value: {
926 oneof_nested: {
927 s_string: "nested oneof in map field value"
928 }
929 }
930 }
931 str_to_oneofs: {
932 key: "string"
933 value: {
934 oneof_string: "hello"
935 }
936 }
937 `,
938 }, {
939 desc: "proto2 map field value contains invalid UTF-8",
940 input: &pb2.Maps{
941 Int32ToStr: map[int32]string{
942 101: "abc\xff",
943 },
944 },
945 want: `int32_to_str: {
946 key: 101
947 value: "abc\xff"
948 }
949 `,
950 }, {
951 desc: "proto2 map field key contains invalid UTF-8",
952 input: &pb2.Maps{
953 StrToNested: map[string]*pb2.Nested{
954 "abc\xff": {},
955 },
956 },
957 want: `str_to_nested: {
958 key: "abc\xff"
959 value: {}
960 }
961 `,
962 }, {
963 desc: "proto3 map field value contains invalid UTF-8",
964 input: &pb3.Maps{
965 Int32ToStr: map[int32]string{
966 101: "abc\xff",
967 },
968 },
969 wantErr: true,
970 }, {
971 desc: "proto3 map field key contains invalid UTF-8",
972 input: &pb3.Maps{
973 StrToNested: map[string]*pb3.Nested{
974 "abc\xff": {},
975 },
976 },
977 wantErr: true,
978 }, {
979 desc: "map field contains nil value",
980 input: &pb3.Maps{
981 StrToNested: map[string]*pb3.Nested{
982 "nil": nil,
983 },
984 },
985 want: `str_to_nested: {
986 key: "nil"
987 value: {}
988 }
989 `,
990 }, {
991 desc: "required fields not set",
992 input: &pb2.Requireds{},
993 want: "",
994 wantErr: true,
995 }, {
996 desc: "required fields partially set",
997 input: &pb2.Requireds{
998 ReqBool: proto.Bool(false),
999 ReqSfixed64: proto.Int64(0xbeefcafe),
1000 ReqDouble: proto.Float64(math.NaN()),
1001 ReqString: proto.String("hello"),
1002 ReqEnum: pb2.Enum_ONE.Enum(),
1003 },
1004 want: `req_bool: false
1005 req_sfixed64: 3203386110
1006 req_double: nan
1007 req_string: "hello"
1008 req_enum: ONE
1009 `,
1010 wantErr: true,
1011 }, {
1012 desc: "required fields not set with AllowPartial",
1013 mo: prototext.MarshalOptions{AllowPartial: true},
1014 input: &pb2.Requireds{
1015 ReqBool: proto.Bool(false),
1016 ReqSfixed64: proto.Int64(0xbeefcafe),
1017 ReqDouble: proto.Float64(math.NaN()),
1018 ReqString: proto.String("hello"),
1019 ReqEnum: pb2.Enum_ONE.Enum(),
1020 },
1021 want: `req_bool: false
1022 req_sfixed64: 3203386110
1023 req_double: nan
1024 req_string: "hello"
1025 req_enum: ONE
1026 `,
1027 }, {
1028 desc: "required fields all set",
1029 input: &pb2.Requireds{
1030 ReqBool: proto.Bool(false),
1031 ReqSfixed64: proto.Int64(0),
1032 ReqDouble: proto.Float64(1.23),
1033 ReqString: proto.String(""),
1034 ReqEnum: pb2.Enum_ONE.Enum(),
1035 ReqNested: &pb2.Nested{},
1036 },
1037 want: `req_bool: false
1038 req_sfixed64: 0
1039 req_double: 1.23
1040 req_string: ""
1041 req_enum: ONE
1042 req_nested: {}
1043 `,
1044 }, {
1045 desc: "indirect required field",
1046 input: &pb2.IndirectRequired{
1047 OptNested: &pb2.NestedWithRequired{},
1048 },
1049 want: "opt_nested: {}\n",
1050 wantErr: true,
1051 }, {
1052 desc: "indirect required field with AllowPartial",
1053 mo: prototext.MarshalOptions{AllowPartial: true},
1054 input: &pb2.IndirectRequired{
1055 OptNested: &pb2.NestedWithRequired{},
1056 },
1057 want: "opt_nested: {}\n",
1058 }, {
1059 desc: "indirect required field in empty repeated",
1060 input: &pb2.IndirectRequired{
1061 RptNested: []*pb2.NestedWithRequired{},
1062 },
1063 want: "",
1064 }, {
1065 desc: "indirect required field in repeated",
1066 input: &pb2.IndirectRequired{
1067 RptNested: []*pb2.NestedWithRequired{
1068 &pb2.NestedWithRequired{},
1069 },
1070 },
1071 want: "rpt_nested: {}\n",
1072 wantErr: true,
1073 }, {
1074 desc: "indirect required field in repeated with AllowPartial",
1075 mo: prototext.MarshalOptions{AllowPartial: true},
1076 input: &pb2.IndirectRequired{
1077 RptNested: []*pb2.NestedWithRequired{
1078 &pb2.NestedWithRequired{},
1079 },
1080 },
1081 want: "rpt_nested: {}\n",
1082 }, {
1083 desc: "indirect required field in empty map",
1084 input: &pb2.IndirectRequired{
1085 StrToNested: map[string]*pb2.NestedWithRequired{},
1086 },
1087 want: "",
1088 }, {
1089 desc: "indirect required field in map",
1090 input: &pb2.IndirectRequired{
1091 StrToNested: map[string]*pb2.NestedWithRequired{
1092 "fail": &pb2.NestedWithRequired{},
1093 },
1094 },
1095 want: `str_to_nested: {
1096 key: "fail"
1097 value: {}
1098 }
1099 `,
1100 wantErr: true,
1101 }, {
1102 desc: "indirect required field in map with AllowPartial",
1103 mo: prototext.MarshalOptions{AllowPartial: true},
1104 input: &pb2.IndirectRequired{
1105 StrToNested: map[string]*pb2.NestedWithRequired{
1106 "fail": &pb2.NestedWithRequired{},
1107 },
1108 },
1109 want: `str_to_nested: {
1110 key: "fail"
1111 value: {}
1112 }
1113 `,
1114 }, {
1115 desc: "indirect required field in oneof",
1116 input: &pb2.IndirectRequired{
1117 Union: &pb2.IndirectRequired_OneofNested{
1118 OneofNested: &pb2.NestedWithRequired{},
1119 },
1120 },
1121 want: "oneof_nested: {}\n",
1122 wantErr: true,
1123 }, {
1124 desc: "indirect required field in oneof with AllowPartial",
1125 mo: prototext.MarshalOptions{AllowPartial: true},
1126 input: &pb2.IndirectRequired{
1127 Union: &pb2.IndirectRequired_OneofNested{
1128 OneofNested: &pb2.NestedWithRequired{},
1129 },
1130 },
1131 want: "oneof_nested: {}\n",
1132 }, {
1133 desc: "unknown fields not printed",
1134 input: func() proto.Message {
1135 m := &pb2.Scalars{
1136 OptString: proto.String("this message contains unknown fields"),
1137 }
1138 m.ProtoReflect().SetUnknown(protopack.Message{
1139 protopack.Tag{101, protopack.VarintType}, protopack.Bool(true),
1140 protopack.Tag{102, protopack.VarintType}, protopack.Varint(0xff),
1141 protopack.Tag{103, protopack.Fixed32Type}, protopack.Uint32(47),
1142 protopack.Tag{104, protopack.Fixed64Type}, protopack.Int64(0xdeadbeef),
1143 }.Marshal())
1144 return m
1145 }(),
1146 want: `opt_string: "this message contains unknown fields"
1147 `,
1148 }, {
1149 desc: "unknown varint and fixed types",
1150 mo: prototext.MarshalOptions{EmitUnknown: true},
1151 input: func() proto.Message {
1152 m := &pb2.Scalars{
1153 OptString: proto.String("this message contains unknown fields"),
1154 }
1155 m.ProtoReflect().SetUnknown(protopack.Message{
1156 protopack.Tag{101, protopack.VarintType}, protopack.Bool(true),
1157 protopack.Tag{102, protopack.VarintType}, protopack.Varint(0xff),
1158 protopack.Tag{103, protopack.Fixed32Type}, protopack.Uint32(0x47),
1159 protopack.Tag{104, protopack.Fixed64Type}, protopack.Int64(0xdeadbeef),
1160 }.Marshal())
1161 return m
1162 }(),
1163 want: `opt_string: "this message contains unknown fields"
1164 101: 1
1165 102: 255
1166 103: 0x47
1167 104: 0xdeadbeef
1168 `,
1169 }, {
1170 desc: "unknown length-delimited",
1171 mo: prototext.MarshalOptions{EmitUnknown: true},
1172 input: func() proto.Message {
1173 m := new(pb2.Scalars)
1174 m.ProtoReflect().SetUnknown(protopack.Message{
1175 protopack.Tag{101, protopack.BytesType}, protopack.LengthPrefix{protopack.Bool(true), protopack.Bool(false)},
1176 protopack.Tag{102, protopack.BytesType}, protopack.String("hello world"),
1177 protopack.Tag{103, protopack.BytesType}, protopack.Bytes("\xe4\xb8\x96\xe7\x95\x8c"),
1178 }.Marshal())
1179 return m
1180 }(),
1181 want: `101: "\x01\x00"
1182 102: "hello world"
1183 103: "世界"
1184 `,
1185 }, {
1186 desc: "unknown group type",
1187 mo: prototext.MarshalOptions{EmitUnknown: true},
1188 input: func() proto.Message {
1189 m := new(pb2.Scalars)
1190 m.ProtoReflect().SetUnknown(protopack.Message{
1191 protopack.Tag{101, protopack.StartGroupType}, protopack.Tag{101, protopack.EndGroupType},
1192 protopack.Tag{102, protopack.StartGroupType},
1193 protopack.Tag{101, protopack.VarintType}, protopack.Bool(false),
1194 protopack.Tag{102, protopack.BytesType}, protopack.String("inside a group"),
1195 protopack.Tag{102, protopack.EndGroupType},
1196 }.Marshal())
1197 return m
1198 }(),
1199 want: `101: {}
1200 102: {
1201 101: 0
1202 102: "inside a group"
1203 }
1204 `,
1205 }, {
1206 desc: "unknown unpack repeated field",
1207 mo: prototext.MarshalOptions{EmitUnknown: true},
1208 input: func() proto.Message {
1209 m := new(pb2.Scalars)
1210 m.ProtoReflect().SetUnknown(protopack.Message{
1211 protopack.Tag{101, protopack.BytesType}, protopack.LengthPrefix{protopack.Bool(true), protopack.Bool(false), protopack.Bool(true)},
1212 protopack.Tag{102, protopack.BytesType}, protopack.String("hello"),
1213 protopack.Tag{101, protopack.VarintType}, protopack.Bool(true),
1214 protopack.Tag{102, protopack.BytesType}, protopack.String("世界"),
1215 }.Marshal())
1216 return m
1217 }(),
1218 want: `101: "\x01\x00\x01"
1219 102: "hello"
1220 101: 1
1221 102: "世界"
1222 `,
1223 }, {
1224 desc: "extensions of non-repeated fields",
1225 input: func() proto.Message {
1226 m := &pb2.Extensions{
1227 OptString: proto.String("non-extension field"),
1228 OptBool: proto.Bool(true),
1229 OptInt32: proto.Int32(42),
1230 }
1231 proto.SetExtension(m, pb2.E_OptExtBool, true)
1232 proto.SetExtension(m, pb2.E_OptExtString, "extension field")
1233 proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
1234 proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
1235 OptString: proto.String("nested in an extension"),
1236 OptNested: &pb2.Nested{
1237 OptString: proto.String("another nested in an extension"),
1238 },
1239 })
1240 return m
1241 }(),
1242 want: `opt_string: "non-extension field"
1243 opt_bool: true
1244 opt_int32: 42
1245 [pb2.opt_ext_bool]: true
1246 [pb2.opt_ext_enum]: TEN
1247 [pb2.opt_ext_nested]: {
1248 opt_string: "nested in an extension"
1249 opt_nested: {
1250 opt_string: "another nested in an extension"
1251 }
1252 }
1253 [pb2.opt_ext_string]: "extension field"
1254 `,
1255 }, {
1256 desc: "proto2 extension field contains invalid UTF-8",
1257 input: func() proto.Message {
1258 m := &pb2.Extensions{}
1259 proto.SetExtension(m, pb2.E_OptExtString, "abc\xff")
1260 return m
1261 }(),
1262 want: `[pb2.opt_ext_string]: "abc\xff"
1263 `,
1264 }, {
1265 desc: "extension partial returns error",
1266 input: func() proto.Message {
1267 m := &pb2.Extensions{}
1268 proto.SetExtension(m, pb2.E_OptExtPartial, &pb2.PartialRequired{
1269 OptString: proto.String("partial1"),
1270 })
1271 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtPartial, &pb2.PartialRequired{
1272 OptString: proto.String("partial2"),
1273 })
1274 return m
1275 }(),
1276 want: `[pb2.ExtensionsContainer.opt_ext_partial]: {
1277 opt_string: "partial2"
1278 }
1279 [pb2.opt_ext_partial]: {
1280 opt_string: "partial1"
1281 }
1282 `,
1283 wantErr: true,
1284 }, {
1285 desc: "extension partial with AllowPartial",
1286 mo: prototext.MarshalOptions{AllowPartial: true},
1287 input: func() proto.Message {
1288 m := &pb2.Extensions{}
1289 proto.SetExtension(m, pb2.E_OptExtPartial, &pb2.PartialRequired{
1290 OptString: proto.String("partial1"),
1291 })
1292 return m
1293 }(),
1294 want: `[pb2.opt_ext_partial]: {
1295 opt_string: "partial1"
1296 }
1297 `,
1298 }, {
1299 desc: "extensions of repeated fields",
1300 input: func() proto.Message {
1301 m := &pb2.Extensions{}
1302 proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1303 proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
1304 proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
1305 &pb2.Nested{OptString: proto.String("one")},
1306 &pb2.Nested{OptString: proto.String("two")},
1307 &pb2.Nested{OptString: proto.String("three")},
1308 })
1309 return m
1310 }(),
1311 want: `[pb2.rpt_ext_enum]: TEN
1312 [pb2.rpt_ext_enum]: 101
1313 [pb2.rpt_ext_enum]: ONE
1314 [pb2.rpt_ext_fixed32]: 42
1315 [pb2.rpt_ext_fixed32]: 47
1316 [pb2.rpt_ext_nested]: {
1317 opt_string: "one"
1318 }
1319 [pb2.rpt_ext_nested]: {
1320 opt_string: "two"
1321 }
1322 [pb2.rpt_ext_nested]: {
1323 opt_string: "three"
1324 }
1325 `,
1326 }, {
1327 desc: "extensions of non-repeated fields in another message",
1328 input: func() proto.Message {
1329 m := &pb2.Extensions{}
1330 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
1331 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
1332 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
1333 proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
1334 OptString: proto.String("nested in an extension"),
1335 OptNested: &pb2.Nested{
1336 OptString: proto.String("another nested in an extension"),
1337 },
1338 })
1339 return m
1340 }(),
1341 want: `[pb2.ExtensionsContainer.opt_ext_bool]: true
1342 [pb2.ExtensionsContainer.opt_ext_enum]: TEN
1343 [pb2.ExtensionsContainer.opt_ext_nested]: {
1344 opt_string: "nested in an extension"
1345 opt_nested: {
1346 opt_string: "another nested in an extension"
1347 }
1348 }
1349 [pb2.ExtensionsContainer.opt_ext_string]: "extension field"
1350 `,
1351 }, {
1352 desc: "extensions of repeated fields in another message",
1353 input: func() proto.Message {
1354 m := &pb2.Extensions{
1355 OptString: proto.String("non-extension field"),
1356 OptBool: proto.Bool(true),
1357 OptInt32: proto.Int32(42),
1358 }
1359 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1360 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
1361 proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
1362 &pb2.Nested{OptString: proto.String("one")},
1363 &pb2.Nested{OptString: proto.String("two")},
1364 &pb2.Nested{OptString: proto.String("three")},
1365 })
1366 return m
1367 }(),
1368 want: `opt_string: "non-extension field"
1369 opt_bool: true
1370 opt_int32: 42
1371 [pb2.ExtensionsContainer.rpt_ext_enum]: TEN
1372 [pb2.ExtensionsContainer.rpt_ext_enum]: 101
1373 [pb2.ExtensionsContainer.rpt_ext_enum]: ONE
1374 [pb2.ExtensionsContainer.rpt_ext_nested]: {
1375 opt_string: "one"
1376 }
1377 [pb2.ExtensionsContainer.rpt_ext_nested]: {
1378 opt_string: "two"
1379 }
1380 [pb2.ExtensionsContainer.rpt_ext_nested]: {
1381 opt_string: "three"
1382 }
1383 [pb2.ExtensionsContainer.rpt_ext_string]: "hello"
1384 [pb2.ExtensionsContainer.rpt_ext_string]: "world"
1385 `,
1386 }, {
1387 desc: "MessageSet",
1388 input: func() proto.Message {
1389 m := &pb2.MessageSet{}
1390 proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1391 OptString: proto.String("a messageset extension"),
1392 })
1393 proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1394 OptString: proto.String("not a messageset extension"),
1395 })
1396 proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1397 OptString: proto.String("just a regular extension"),
1398 })
1399 return m
1400 }(),
1401 want: `[pb2.MessageSetExtension.ext_nested]: {
1402 opt_string: "just a regular extension"
1403 }
1404 [pb2.MessageSetExtension]: {
1405 opt_string: "a messageset extension"
1406 }
1407 [pb2.MessageSetExtension.not_message_set_extension]: {
1408 opt_string: "not a messageset extension"
1409 }
1410 `,
1411 skip: !flags.ProtoLegacy,
1412 }, {
1413 desc: "not real MessageSet 1",
1414 input: func() proto.Message {
1415 m := &pb2.FakeMessageSet{}
1416 proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1417 OptString: proto.String("not a messageset extension"),
1418 })
1419 return m
1420 }(),
1421 want: `[pb2.FakeMessageSetExtension.message_set_extension]: {
1422 opt_string: "not a messageset extension"
1423 }
1424 `,
1425 skip: !flags.ProtoLegacy,
1426 }, {
1427 desc: "not real MessageSet 2",
1428 input: func() proto.Message {
1429 m := &pb2.MessageSet{}
1430 proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1431 OptString: proto.String("another not a messageset extension"),
1432 })
1433 return m
1434 }(),
1435 want: `[pb2.message_set_extension]: {
1436 opt_string: "another not a messageset extension"
1437 }
1438 `,
1439 skip: !flags.ProtoLegacy,
1440 }, {
1441 desc: "Any not expanded",
1442 mo: prototext.MarshalOptions{
1443 Resolver: new(protoregistry.Types),
1444 },
1445 input: func() proto.Message {
1446 m := &pb2.Nested{
1447 OptString: proto.String("embedded inside Any"),
1448 OptNested: &pb2.Nested{
1449 OptString: proto.String("inception"),
1450 },
1451 }
1452 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1453 if err != nil {
1454 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1455 }
1456 return &anypb.Any{
1457 TypeUrl: "pb2.Nested",
1458 Value: b,
1459 }
1460 }(),
1461 want: `type_url: "pb2.Nested"
1462 value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
1463 `,
1464 }, {
1465 desc: "Any expanded",
1466 input: func() proto.Message {
1467 m := &pb2.Nested{
1468 OptString: proto.String("embedded inside Any"),
1469 OptNested: &pb2.Nested{
1470 OptString: proto.String("inception"),
1471 },
1472 }
1473 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1474 if err != nil {
1475 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1476 }
1477 return &anypb.Any{
1478 TypeUrl: "foo/pb2.Nested",
1479 Value: b,
1480 }
1481 }(),
1482 want: `[foo/pb2.Nested]: {
1483 opt_string: "embedded inside Any"
1484 opt_nested: {
1485 opt_string: "inception"
1486 }
1487 }
1488 `,
1489 }, {
1490 desc: "Any expanded with missing required",
1491 input: func() proto.Message {
1492 m := &pb2.PartialRequired{
1493 OptString: proto.String("embedded inside Any"),
1494 }
1495 b, err := proto.MarshalOptions{
1496 AllowPartial: true,
1497 Deterministic: true,
1498 }.Marshal(m)
1499 if err != nil {
1500 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1501 }
1502 return &anypb.Any{
1503 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
1504 Value: b,
1505 }
1506 }(),
1507 want: `[pb2.PartialRequired]: {
1508 opt_string: "embedded inside Any"
1509 }
1510 `,
1511 }, {
1512 desc: "Any with invalid value",
1513 input: &anypb.Any{
1514 TypeUrl: "foo/pb2.Nested",
1515 Value: []byte("\x80"),
1516 },
1517 want: `type_url: "foo/pb2.Nested"
1518 value: "\x80"
1519 `,
1520 }, {
1521 desc: "Any expanded in another message",
1522 input: func() *pb2.KnownTypes {
1523 m1 := &pb2.Nested{
1524 OptString: proto.String("message inside Any of another Any field"),
1525 }
1526 b1, err := proto.MarshalOptions{Deterministic: true}.Marshal(m1)
1527 if err != nil {
1528 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1529 }
1530 m2 := &anypb.Any{
1531 TypeUrl: "pb2.Nested",
1532 Value: b1,
1533 }
1534 b2, err := proto.MarshalOptions{Deterministic: true}.Marshal(m2)
1535 if err != nil {
1536 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1537 }
1538 return &pb2.KnownTypes{
1539 OptAny: &anypb.Any{
1540 TypeUrl: "google.protobuf.Any",
1541 Value: b2,
1542 },
1543 }
1544 }(),
1545 want: `opt_any: {
1546 [google.protobuf.Any]: {
1547 [pb2.Nested]: {
1548 opt_string: "message inside Any of another Any field"
1549 }
1550 }
1551 }
1552 `,
1553 }, {
1554 desc: "Any expanded with invalid UTF-8 in proto2",
1555 input: func() *pb2.KnownTypes {
1556 m := &pb2.Nested{
1557 OptString: proto.String("invalid UTF-8 abc\xff"),
1558 }
1559 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1560 if err != nil {
1561 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1562 }
1563 return &pb2.KnownTypes{
1564 OptAny: &anypb.Any{
1565 TypeUrl: "pb2.Nested",
1566 Value: b,
1567 },
1568 }
1569 }(),
1570 want: `opt_any: {
1571 [pb2.Nested]: {
1572 opt_string: "invalid UTF-8 abc\xff"
1573 }
1574 }
1575 `,
1576 }, {
1577 desc: "Any not expanded due to invalid data",
1578 mo: prototext.MarshalOptions{EmitASCII: true},
1579 input: func() *pb2.KnownTypes {
1580 return &pb2.KnownTypes{
1581 OptAny: &anypb.Any{
1582 TypeUrl: "pb3.Scalar",
1583 Value: []byte("\xde\xad\xbe\xef"),
1584 },
1585 }
1586 }(),
1587 want: `opt_any: {
1588 type_url: "pb3.Scalar"
1589 value: "\u07ad\xbe\xef"
1590 }
1591 `,
1592 }, {
1593 desc: "Any inside Any expanded",
1594 input: func() *pb2.KnownTypes {
1595 m1 := &pb2.Nested{
1596 OptString: proto.String("invalid UTF-8 abc\xff"),
1597 }
1598 b1, err := proto.MarshalOptions{Deterministic: true}.Marshal(m1)
1599 if err != nil {
1600 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1601 }
1602 m2 := &anypb.Any{
1603 TypeUrl: "pb2.Nested",
1604 Value: b1,
1605 }
1606 b2, err := proto.MarshalOptions{Deterministic: true}.Marshal(m2)
1607 if err != nil {
1608 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1609 }
1610 return &pb2.KnownTypes{
1611 OptAny: &anypb.Any{
1612 TypeUrl: "google.protobuf.Any",
1613 Value: b2,
1614 },
1615 }
1616 }(),
1617 want: `opt_any: {
1618 [google.protobuf.Any]: {
1619 [pb2.Nested]: {
1620 opt_string: "invalid UTF-8 abc\xff"
1621 }
1622 }
1623 }
1624 `,
1625 }, {
1626 desc: "Any inside Any not expanded due to invalid data",
1627 mo: prototext.MarshalOptions{EmitASCII: true},
1628 input: func() *pb2.KnownTypes {
1629 m := &anypb.Any{
1630 TypeUrl: "pb2.Nested",
1631 Value: []byte("\xde\xad\xbe\xef"),
1632 }
1633 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1634 if err != nil {
1635 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1636 }
1637 return &pb2.KnownTypes{
1638 OptAny: &anypb.Any{
1639 TypeUrl: "google.protobuf.Any",
1640 Value: b,
1641 },
1642 }
1643 }(),
1644 want: `opt_any: {
1645 [google.protobuf.Any]: {
1646 type_url: "pb2.Nested"
1647 value: "\u07ad\xbe\xef"
1648 }
1649 }
1650 `,
1651 }}
1652
1653 for _, tt := range tests {
1654 tt := tt
1655 if tt.skip {
1656 continue
1657 }
1658 t.Run(tt.desc, func(t *testing.T) {
1659
1660 tt.mo.Indent = " "
1661 b, err := tt.mo.Marshal(tt.input)
1662 if err != nil && !tt.wantErr {
1663 t.Errorf("Marshal() returned error: %v\n", err)
1664 }
1665 if err == nil && tt.wantErr {
1666 t.Error("Marshal() got nil error, want error\n")
1667 }
1668 got := string(b)
1669 if got != tt.want {
1670 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1671 if diff := cmp.Diff(tt.want, got); diff != "" {
1672 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1673 }
1674 }
1675 })
1676 }
1677 }
1678
1679 func TestEncodeAppend(t *testing.T) {
1680 want := []byte("prefix")
1681 got := append([]byte(nil), want...)
1682 got, err := prototext.MarshalOptions{}.MarshalAppend(got, &pb3.Scalars{
1683 SString: "value",
1684 })
1685 if err != nil {
1686 t.Fatal(err)
1687 }
1688 if !bytes.HasPrefix(got, want) {
1689 t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
1690 }
1691 }
1692
1693 func TestMarshalAppendAllocations(t *testing.T) {
1694 m := &pb3.Scalars{SInt32: 1}
1695 const count = 1000
1696 size := 9
1697 b := make([]byte, size)
1698
1699 marshalAllocs := testing.AllocsPerRun(count, func() {
1700 _, err := prototext.MarshalOptions{}.MarshalAppend(b[:0], m)
1701 if err != nil {
1702 t.Fatal(err)
1703 }
1704 })
1705 b = nil
1706 marshalAppendAllocs := testing.AllocsPerRun(count, func() {
1707 var err error
1708 b, err = prototext.MarshalOptions{}.MarshalAppend(b, m)
1709 if err != nil {
1710 t.Fatal(err)
1711 }
1712 })
1713 if marshalAllocs != marshalAppendAllocs {
1714 t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
1715 t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
1716 t.Errorf("expect amortized allocs/op to be identical")
1717 }
1718 }
1719
View as plain text