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