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