blob: 28beaf4050c9c879010f71ba4e8aedd47a170c67 [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 (
8 "math"
9 "strings"
10 "testing"
11
12 "github.com/golang/protobuf/v2/encoding/textpb"
13 "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
14 "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
15 "github.com/golang/protobuf/v2/internal/detrand"
16 "github.com/golang/protobuf/v2/internal/impl"
17 "github.com/golang/protobuf/v2/internal/scalar"
18 "github.com/golang/protobuf/v2/proto"
19 "github.com/google/go-cmp/cmp"
20 "github.com/google/go-cmp/cmp/cmpopts"
21
22 anypb "github.com/golang/protobuf/ptypes/any"
23 durpb "github.com/golang/protobuf/ptypes/duration"
24 emptypb "github.com/golang/protobuf/ptypes/empty"
25 stpb "github.com/golang/protobuf/ptypes/struct"
26 tspb "github.com/golang/protobuf/ptypes/timestamp"
27 wpb "github.com/golang/protobuf/ptypes/wrappers"
28)
29
30func init() {
31 // Disable detrand to enable direct comparisons on outputs.
32 detrand.Disable()
33}
34
35func M(m interface{}) proto.Message {
36 return impl.MessageOf(m).Interface()
37}
38
39// splitLines is a cmpopts.Option for comparing strings with line breaks.
40var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
41 return strings.Split(s, "\n")
42})
43
44func TestMarshal(t *testing.T) {
45 tests := []struct {
46 desc string
47 input proto.Message
48 want string
49 wantErr bool
50 }{{
51 desc: "nil message",
52 want: "\n",
53 }, {
54 desc: "proto2 optional scalar fields not set",
55 input: M(&pb2.Scalars{}),
56 want: "\n",
57 }, {
58 desc: "proto3 scalar fields not set",
59 input: M(&pb3.Scalars{}),
60 want: "\n",
61 }, {
62 desc: "proto2 optional scalar fields set to zero values",
63 input: M(&pb2.Scalars{
64 OptBool: scalar.Bool(false),
65 OptInt32: scalar.Int32(0),
66 OptInt64: scalar.Int64(0),
67 OptUint32: scalar.Uint32(0),
68 OptUint64: scalar.Uint64(0),
69 OptSint32: scalar.Int32(0),
70 OptSint64: scalar.Int64(0),
71 OptFixed32: scalar.Uint32(0),
72 OptFixed64: scalar.Uint64(0),
73 OptSfixed32: scalar.Int32(0),
74 OptSfixed64: scalar.Int64(0),
75 OptFloat: scalar.Float32(0),
76 OptDouble: scalar.Float64(0),
77 OptBytes: []byte{},
78 OptString: scalar.String(""),
79 }),
80 want: `opt_bool: false
81opt_int32: 0
82opt_int64: 0
83opt_uint32: 0
84opt_uint64: 0
85opt_sint32: 0
86opt_sint64: 0
87opt_fixed32: 0
88opt_fixed64: 0
89opt_sfixed32: 0
90opt_sfixed64: 0
91opt_float: 0
92opt_double: 0
93opt_bytes: ""
94opt_string: ""
95`,
96 }, {
97 desc: "proto3 scalar fields set to zero values",
98 input: M(&pb3.Scalars{
99 SBool: false,
100 SInt32: 0,
101 SInt64: 0,
102 SUint32: 0,
103 SUint64: 0,
104 SSint32: 0,
105 SSint64: 0,
106 SFixed32: 0,
107 SFixed64: 0,
108 SSfixed32: 0,
109 SSfixed64: 0,
110 SFloat: 0,
111 SDouble: 0,
112 SBytes: []byte{},
113 SString: "",
114 }),
115 want: "\n",
116 }, {
117 desc: "proto2 optional scalar fields set to some values",
118 input: M(&pb2.Scalars{
119 OptBool: scalar.Bool(true),
120 OptInt32: scalar.Int32(0xff),
121 OptInt64: scalar.Int64(0xdeadbeef),
122 OptUint32: scalar.Uint32(47),
123 OptUint64: scalar.Uint64(0xdeadbeef),
124 OptSint32: scalar.Int32(-1001),
125 OptSint64: scalar.Int64(-0xffff),
126 OptFixed64: scalar.Uint64(64),
127 OptSfixed32: scalar.Int32(-32),
128 // TODO: Update encoder to output same decimals.
129 OptFloat: scalar.Float32(1.02),
130 OptDouble: scalar.Float64(1.23e100),
131 // TODO: Update encoder to not output UTF8 for bytes.
132 OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
133 OptString: scalar.String("谷歌"),
134 }),
135 want: `opt_bool: true
136opt_int32: 255
137opt_int64: 3735928559
138opt_uint32: 47
139opt_uint64: 3735928559
140opt_sint32: -1001
141opt_sint64: -65535
142opt_fixed64: 64
143opt_sfixed32: -32
144opt_float: 1.0199999809265137
145opt_double: 1.23e+100
146opt_bytes: "谷歌"
147opt_string: "谷歌"
148`,
149 }, {
150 desc: "proto3 enum empty message",
151 input: M(&pb3.Enums{}),
152 want: "\n",
153 }, {
154 desc: "proto3 enum",
155 input: M(&pb3.Enums{
156 SEnum: pb3.Enum_ONE,
157 RptEnum: []pb3.Enum{pb3.Enum_ONE, 10, 0, 21, -1},
158 SNestedEnum: pb3.Enums_DIEZ,
159 RptNestedEnum: []pb3.Enums_NestedEnum{21, pb3.Enums_CERO, -7, 10},
160 }),
161 want: `s_enum: ONE
162rpt_enum: ONE
163rpt_enum: TEN
164rpt_enum: ZERO
165rpt_enum: 21
166rpt_enum: -1
167s_nested_enum: DIEZ
168rpt_nested_enum: 21
169rpt_nested_enum: CERO
170rpt_nested_enum: -7
171rpt_nested_enum: DIEZ
172`,
173 }, {
174 desc: "float32 nan",
175 input: M(&pb3.Scalars{
176 SFloat: float32(math.NaN()),
177 }),
178 want: "s_float: nan\n",
179 }, {
180 desc: "float32 positive infinity",
181 input: M(&pb3.Scalars{
182 SFloat: float32(math.Inf(1)),
183 }),
184 want: "s_float: inf\n",
185 }, {
186 desc: "float32 negative infinity",
187 input: M(&pb3.Scalars{
188 SFloat: float32(math.Inf(-1)),
189 }),
190 want: "s_float: -inf\n",
191 }, {
192 desc: "float64 nan",
193 input: M(&pb3.Scalars{
194 SDouble: math.NaN(),
195 }),
196 want: "s_double: nan\n",
197 }, {
198 desc: "float64 positive infinity",
199 input: M(&pb3.Scalars{
200 SDouble: math.Inf(1),
201 }),
202 want: "s_double: inf\n",
203 }, {
204 desc: "float64 negative infinity",
205 input: M(&pb3.Scalars{
206 SDouble: math.Inf(-1),
207 }),
208 want: "s_double: -inf\n",
209 }, {
210 desc: "proto2 bytes set to empty string",
211 input: M(&pb2.Scalars{
212 OptBytes: []byte(""),
213 }),
214 want: "opt_bytes: \"\"\n",
215 }, {
216 desc: "proto3 bytes set to empty string",
217 input: M(&pb3.Scalars{
218 SBytes: []byte(""),
219 }),
220 want: "\n",
221 }, {
222 desc: "proto2 repeated not set",
223 input: M(&pb2.Repeats{}),
224 want: "\n",
225 }, {
226 desc: "proto2 repeated set to empty slices",
227 input: M(&pb2.Repeats{
228 RptBool: []bool{},
229 RptInt32: []int32{},
230 RptInt64: []int64{},
231 RptUint32: []uint32{},
232 RptUint64: []uint64{},
233 RptFloat: []float32{},
234 RptDouble: []float64{},
235 RptBytes: [][]byte{},
236 }),
237 want: "\n",
238 }, {
239 desc: "proto2 repeated set to some values",
240 input: M(&pb2.Repeats{
241 RptBool: []bool{true, false, true, true},
242 RptInt32: []int32{1, 6, 0, 0},
243 RptInt64: []int64{-64, 47},
244 RptUint32: []uint32{0xff, 0xffff},
245 RptUint64: []uint64{0xdeadbeef},
246 // TODO: add float32 examples.
247 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
248 RptString: []string{"hello", "世界"},
249 RptBytes: [][]byte{
250 []byte("hello"),
251 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
252 },
253 }),
254 want: `rpt_bool: true
255rpt_bool: false
256rpt_bool: true
257rpt_bool: true
258rpt_int32: 1
259rpt_int32: 6
260rpt_int32: 0
261rpt_int32: 0
262rpt_int64: -64
263rpt_int64: 47
264rpt_uint32: 255
265rpt_uint32: 65535
266rpt_uint64: 3735928559
267rpt_double: nan
268rpt_double: inf
269rpt_double: -inf
270rpt_double: 1.23e-308
271rpt_string: "hello"
272rpt_string: "世界"
273rpt_bytes: "hello"
274rpt_bytes: "世界"
275`,
276 }, {
277 desc: "proto2 enum fields not set",
278 input: M(&pb2.Enums{}),
279 want: "\n",
280 }, {
281 desc: "proto2 enum fields",
282 input: M(&pb2.Enums{
283 OptEnum: pb2.Enum_FIRST.Enum(),
284 RptEnum: []pb2.Enum{pb2.Enum_FIRST, 2, pb2.Enum_TENTH, 42},
285 OptNestedEnum: pb2.Enums_UNO.Enum(),
286 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
287 }),
288 want: `opt_enum: FIRST
289rpt_enum: FIRST
290rpt_enum: SECOND
291rpt_enum: TENTH
292rpt_enum: 42
293opt_nested_enum: UNO
294rpt_nested_enum: DOS
295rpt_nested_enum: 47
296rpt_nested_enum: DIEZ
297`,
298 }, {
299 desc: "proto3 enum fields set to zero value",
300 input: M(&pb3.Enums{
301 SEnum: pb3.Enum_ZERO,
302 RptEnum: []pb3.Enum{},
303 SNestedEnum: pb3.Enums_CERO,
304 RptNestedEnum: []pb3.Enums_NestedEnum{},
305 }),
306 want: "\n",
307 }, {
308 desc: "proto3 enum fields",
309 input: M(&pb3.Enums{
310 SEnum: pb3.Enum_TWO,
311 RptEnum: []pb3.Enum{1, 0, 0},
312 SNestedEnum: pb3.Enums_DOS,
313 RptNestedEnum: []pb3.Enums_NestedEnum{101, pb3.Enums_DIEZ, 10},
314 }),
315 want: `s_enum: TWO
316rpt_enum: ONE
317rpt_enum: ZERO
318rpt_enum: ZERO
319s_nested_enum: DOS
320rpt_nested_enum: 101
321rpt_nested_enum: DIEZ
322rpt_nested_enum: DIEZ
323`,
324 }, {
325 desc: "proto2 nested message not set",
326 input: M(&pb2.Nests{}),
327 want: "\n",
328 }, {
329 desc: "proto2 nested message set to empty",
330 input: M(&pb2.Nests{
331 OptNested: &pb2.Nested{},
332 Optgroup: &pb2.Nests_OptGroup{},
333 RptNested: []*pb2.Nested{},
334 Rptgroup: []*pb2.Nests_RptGroup{},
335 }),
336 want: `opt_nested: {}
337optgroup: {}
338`,
339 }, {
340 desc: "proto2 nested messages",
341 input: M(&pb2.Nests{
342 OptNested: &pb2.Nested{
343 OptString: scalar.String("nested message"),
344 OptNested: &pb2.Nested{
345 OptString: scalar.String("another nested message"),
346 },
347 },
348 RptNested: []*pb2.Nested{
349 {
350 OptString: scalar.String("repeat nested one"),
351 },
352 {
353 OptString: scalar.String("repeat nested two"),
354 OptNested: &pb2.Nested{
355 OptString: scalar.String("inside repeat nested two"),
356 },
357 },
358 {},
359 },
360 }),
361 want: `opt_nested: {
362 opt_string: "nested message"
363 opt_nested: {
364 opt_string: "another nested message"
365 }
366}
367rpt_nested: {
368 opt_string: "repeat nested one"
369}
370rpt_nested: {
371 opt_string: "repeat nested two"
372 opt_nested: {
373 opt_string: "inside repeat nested two"
374 }
375}
376rpt_nested: {}
377`,
378 }, {
379 desc: "proto2 group fields",
380 input: M(&pb2.Nests{
381 Optgroup: &pb2.Nests_OptGroup{
382 OptBool: scalar.Bool(true),
383 OptString: scalar.String("inside a group"),
384 OptNested: &pb2.Nested{
385 OptString: scalar.String("nested message inside a group"),
386 },
387 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
388 OptEnum: pb2.Enum_TENTH.Enum(),
389 },
390 },
391 Rptgroup: []*pb2.Nests_RptGroup{
392 {
393 RptBool: []bool{true, false},
394 },
395 {},
396 },
397 }),
398 want: `optgroup: {
399 opt_bool: true
400 opt_string: "inside a group"
401 opt_nested: {
402 opt_string: "nested message inside a group"
403 }
404 optnestedgroup: {
405 opt_enum: TENTH
406 }
407}
408rptgroup: {
409 rpt_bool: true
410 rpt_bool: false
411}
412rptgroup: {}
413`,
414 }, {
415 desc: "proto3 nested message not set",
416 input: M(&pb3.Nests{}),
417 want: "\n",
418 }, {
419 desc: "proto3 nested message",
420 input: M(&pb3.Nests{
421 SNested: &pb3.Nested{
422 SString: "nested message",
423 SNested: &pb3.Nested{
424 SString: "another nested message",
425 },
426 },
427 RptNested: []*pb3.Nested{
428 {
429 SString: "repeated nested one",
430 SNested: &pb3.Nested{
431 SString: "inside repeated nested one",
432 },
433 },
434 {
435 SString: "repeated nested two",
436 },
437 {},
438 },
439 }),
440 want: `s_nested: {
441 s_string: "nested message"
442 s_nested: {
443 s_string: "another nested message"
444 }
445}
446rpt_nested: {
447 s_string: "repeated nested one"
448 s_nested: {
449 s_string: "inside repeated nested one"
450 }
451}
452rpt_nested: {
453 s_string: "repeated nested two"
454}
455rpt_nested: {}
456`,
457 }, {
458 desc: "proto2 required fields not set",
459 input: M(&pb2.Requireds{}),
460 want: "\n",
461 wantErr: true,
462 }, {
463 desc: "proto2 required fields partially set",
464 input: M(&pb2.Requireds{
465 ReqBool: scalar.Bool(false),
466 ReqFixed32: scalar.Uint32(47),
467 ReqSfixed64: scalar.Int64(0xbeefcafe),
468 ReqDouble: scalar.Float64(math.NaN()),
469 ReqString: scalar.String("hello"),
470 ReqEnum: pb2.Enum_FIRST.Enum(),
471 }),
472 want: `req_bool: false
473req_fixed32: 47
474req_sfixed64: 3203386110
475req_double: nan
476req_string: "hello"
477req_enum: FIRST
478`,
479 wantErr: true,
480 }, {
481 desc: "proto2 required fields all set",
482 input: M(&pb2.Requireds{
483 ReqBool: scalar.Bool(false),
484 ReqFixed32: scalar.Uint32(0),
485 ReqFixed64: scalar.Uint64(0),
486 ReqSfixed32: scalar.Int32(0),
487 ReqSfixed64: scalar.Int64(0),
488 ReqFloat: scalar.Float32(0),
489 ReqDouble: scalar.Float64(0),
490 ReqString: scalar.String(""),
491 ReqEnum: pb2.Enum_UNKNOWN.Enum(),
492 ReqBytes: []byte{},
493 ReqNested: &pb2.Nested{},
494 }),
495 want: `req_bool: false
496req_fixed32: 0
497req_fixed64: 0
498req_sfixed32: 0
499req_sfixed64: 0
500req_float: 0
501req_double: 0
502req_string: ""
503req_bytes: ""
504req_enum: UNKNOWN
505req_nested: {}
506`,
507 }, {
508 desc: "oneof fields",
509 input: M(&pb2.Oneofs{}),
510 want: "\n",
511 }, {
512 desc: "oneof field set to empty string",
513 input: M(&pb2.Oneofs{
514 Union: &pb2.Oneofs_Str{},
515 }),
516 want: "str: \"\"\n",
517 }, {
518 desc: "oneof field set to string",
519 input: M(&pb2.Oneofs{
520 Union: &pb2.Oneofs_Str{
521 Str: "hello",
522 },
523 }),
524 want: "str: \"hello\"\n",
525 }, {
526 desc: "oneof field set to empty message",
527 input: M(&pb2.Oneofs{
528 Union: &pb2.Oneofs_Msg{
529 Msg: &pb2.Nested{},
530 },
531 }),
532 want: "msg: {}\n",
533 }, {
534 desc: "oneof field set to message",
535 input: M(&pb2.Oneofs{
536 Union: &pb2.Oneofs_Msg{
537 Msg: &pb2.Nested{
538 OptString: scalar.String("nested message"),
539 },
540 },
541 }),
542 want: `msg: {
543 opt_string: "nested message"
544}
545`,
546 }, {
547 desc: "map fields empty",
548 input: M(&pb2.Maps{}),
549 want: "\n",
550 }, {
551 desc: "map fields set to empty maps",
552 input: M(&pb2.Maps{
553 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{},
559 }),
560 want: "\n",
561 }, {
562 desc: "map fields 1",
563 input: M(&pb2.Maps{
564 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 },
577 }),
578 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",
609 input: M(&pb2.Maps{
610 Uint64ToEnum: map[uint64]pb2.Enum{
611 1: pb2.Enum_FIRST,
612 2: pb2.Enum_SECOND,
613 10: pb2.Enum_TENTH,
614 },
615 }),
616 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",
631 input: M(&pb2.Maps{
632 StrToNested: map[string]*pb2.Nested{
633 "nested_one": &pb2.Nested{
634 OptString: scalar.String("nested in a map"),
635 },
636 },
637 }),
638 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",
647 input: M(&pb2.Maps{
648 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 },
662 }),
663 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 }, {
679 desc: "well-known type fields not set",
680 input: M(&pb2.KnownTypes{}),
681 want: "\n",
682 }, {
683 desc: "well-known type fields set to empty messages",
684 input: M(&pb2.KnownTypes{
685 OptBool: &wpb.BoolValue{},
686 OptInt32: &wpb.Int32Value{},
687 OptInt64: &wpb.Int64Value{},
688 OptUint32: &wpb.UInt32Value{},
689 OptUint64: &wpb.UInt64Value{},
690 OptFloat: &wpb.FloatValue{},
691 OptDouble: &wpb.DoubleValue{},
692 OptString: &wpb.StringValue{},
693 OptBytes: &wpb.BytesValue{},
694 OptDuration: &durpb.Duration{},
695 OptTimestamp: &tspb.Timestamp{},
696 OptStruct: &stpb.Struct{},
697 OptList: &stpb.ListValue{},
698 OptValue: &stpb.Value{},
699 OptEmpty: &emptypb.Empty{},
700 OptAny: &anypb.Any{},
701 }),
702 want: `opt_bool: {}
703opt_int32: {}
704opt_int64: {}
705opt_uint32: {}
706opt_uint64: {}
707opt_float: {}
708opt_double: {}
709opt_string: {}
710opt_bytes: {}
711opt_duration: {}
712opt_timestamp: {}
713opt_struct: {}
714opt_list: {}
715opt_value: {}
716opt_empty: {}
717opt_any: {}
718`,
719 }, {
720 desc: "well-known type scalar fields",
721 input: M(&pb2.KnownTypes{
722 OptBool: &wpb.BoolValue{
723 Value: true,
724 },
725 OptInt32: &wpb.Int32Value{
726 Value: -42,
727 },
728 OptInt64: &wpb.Int64Value{
729 Value: -42,
730 },
731 OptUint32: &wpb.UInt32Value{
732 Value: 0xff,
733 },
734 OptUint64: &wpb.UInt64Value{
735 Value: 0xffff,
736 },
737 OptFloat: &wpb.FloatValue{
738 Value: 1.234,
739 },
740 OptDouble: &wpb.DoubleValue{
741 Value: 1.23e308,
742 },
743 OptString: &wpb.StringValue{
744 Value: "谷歌",
745 },
746 OptBytes: &wpb.BytesValue{
747 Value: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
748 },
749 }),
750 want: `opt_bool: {
751 value: true
752}
753opt_int32: {
754 value: -42
755}
756opt_int64: {
757 value: -42
758}
759opt_uint32: {
760 value: 255
761}
762opt_uint64: {
763 value: 65535
764}
765opt_float: {
766 value: 1.2339999675750732
767}
768opt_double: {
769 value: 1.23e+308
770}
771opt_string: {
772 value: "谷歌"
773}
774opt_bytes: {
775 value: "谷歌"
776}
777`,
778 }, {
779 desc: "well-known type time-related fields",
780 input: M(&pb2.KnownTypes{
781 OptDuration: &durpb.Duration{
782 Seconds: -3600,
783 Nanos: -123,
784 },
785 OptTimestamp: &tspb.Timestamp{
786 Seconds: 1257894000,
787 Nanos: 123,
788 },
789 }),
790 want: `opt_duration: {
791 seconds: -3600
792 nanos: -123
793}
794opt_timestamp: {
795 seconds: 1257894000
796 nanos: 123
797}
798`,
799 }, {
800 desc: "well-known type struct field and different Value types",
801 input: M(&pb2.KnownTypes{
802 OptStruct: &stpb.Struct{
803 Fields: map[string]*stpb.Value{
804 "bool": &stpb.Value{
805 Kind: &stpb.Value_BoolValue{
806 BoolValue: true,
807 },
808 },
809 "double": &stpb.Value{
810 Kind: &stpb.Value_NumberValue{
811 NumberValue: 3.1415,
812 },
813 },
814 "null": &stpb.Value{
815 Kind: &stpb.Value_NullValue{
816 NullValue: stpb.NullValue_NULL_VALUE,
817 },
818 },
819 "string": &stpb.Value{
820 Kind: &stpb.Value_StringValue{
821 StringValue: "string",
822 },
823 },
824 "struct": &stpb.Value{
825 Kind: &stpb.Value_StructValue{
826 StructValue: &stpb.Struct{
827 Fields: map[string]*stpb.Value{
828 "bool": &stpb.Value{
829 Kind: &stpb.Value_BoolValue{
830 BoolValue: false,
831 },
832 },
833 },
834 },
835 },
836 },
837 "list": &stpb.Value{
838 Kind: &stpb.Value_ListValue{
839 ListValue: &stpb.ListValue{
840 Values: []*stpb.Value{
841 {
842 Kind: &stpb.Value_BoolValue{
843 BoolValue: false,
844 },
845 },
846 {
847 Kind: &stpb.Value_StringValue{
848 StringValue: "hello",
849 },
850 },
851 },
852 },
853 },
854 },
855 },
856 },
857 }),
858 want: `opt_struct: {
859 fields: {
860 key: "bool"
861 value: {
862 bool_value: true
863 }
864 }
865 fields: {
866 key: "double"
867 value: {
868 number_value: 3.1415
869 }
870 }
871 fields: {
872 key: "list"
873 value: {
874 list_value: {
875 values: {
876 bool_value: false
877 }
878 values: {
879 string_value: "hello"
880 }
881 }
882 }
883 }
884 fields: {
885 key: "null"
886 value: {
887 null_value: NULL_VALUE
888 }
889 }
890 fields: {
891 key: "string"
892 value: {
893 string_value: "string"
894 }
895 }
896 fields: {
897 key: "struct"
898 value: {
899 struct_value: {
900 fields: {
901 key: "bool"
902 value: {
903 bool_value: false
904 }
905 }
906 }
907 }
908 }
909}
910`,
911 }}
912
913 for _, tt := range tests {
914 tt := tt
915 t.Run(tt.desc, func(t *testing.T) {
916 t.Parallel()
917 want := tt.want
918 b, err := textpb.Marshal(tt.input)
919 if err != nil && !tt.wantErr {
920 t.Errorf("Marshal() returned error: %v\n\n", err)
921 }
922 if tt.wantErr && err == nil {
923 t.Errorf("Marshal() got nil error, want error\n\n")
924 }
925 if got := string(b); got != want {
926 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, want)
927 if diff := cmp.Diff(want, got, splitLines); diff != "" {
928 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
929 }
930 }
931 })
932 }
933}