blob: b0e397336b754e5f4cff55ea839379b3fa574259 [file] [log] [blame]
Herbie Ong7b828bc2019-02-08 19:56:24 -08001// Copyright 2019 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 jsonpb_test
6
7import (
Damien Neilbc310b52019-04-11 11:46:55 -07008 "bytes"
Herbie Ong0b0f4032019-03-18 19:06:15 -07009 "encoding/hex"
Herbie Ong7b828bc2019-02-08 19:56:24 -080010 "math"
11 "strings"
12 "testing"
13
14 "github.com/golang/protobuf/v2/encoding/jsonpb"
15 "github.com/golang/protobuf/v2/internal/encoding/pack"
Herbie Ongf83d5bb2019-03-14 00:01:27 -070016 "github.com/golang/protobuf/v2/internal/encoding/wire"
Herbie Ong7b828bc2019-02-08 19:56:24 -080017 "github.com/golang/protobuf/v2/internal/scalar"
18 "github.com/golang/protobuf/v2/proto"
Herbie Ong0b0f4032019-03-18 19:06:15 -070019 preg "github.com/golang/protobuf/v2/reflect/protoregistry"
Joe Tsai4fddeba2019-03-20 18:29:32 -070020 "github.com/golang/protobuf/v2/runtime/protoiface"
Herbie Ong7b828bc2019-02-08 19:56:24 -080021 "github.com/google/go-cmp/cmp"
22 "github.com/google/go-cmp/cmp/cmpopts"
23
Herbie Ong7b828bc2019-02-08 19:56:24 -080024 "github.com/golang/protobuf/v2/encoding/testprotos/pb2"
25 "github.com/golang/protobuf/v2/encoding/testprotos/pb3"
Herbie Ong0b0f4032019-03-18 19:06:15 -070026 knownpb "github.com/golang/protobuf/v2/types/known"
Herbie Ong7b828bc2019-02-08 19:56:24 -080027)
28
29// splitLines is a cmpopts.Option for comparing strings with line breaks.
30var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
31 return strings.Split(s, "\n")
32})
33
34func pb2Enum(i int32) *pb2.Enum {
35 p := new(pb2.Enum)
36 *p = pb2.Enum(i)
37 return p
38}
39
40func pb2Enums_NestedEnum(i int32) *pb2.Enums_NestedEnum {
41 p := new(pb2.Enums_NestedEnum)
42 *p = pb2.Enums_NestedEnum(i)
43 return p
44}
45
Joe Tsai4fddeba2019-03-20 18:29:32 -070046func setExtension(m proto.Message, xd *protoiface.ExtensionDescV1, val interface{}) {
Herbie Ongf83d5bb2019-03-14 00:01:27 -070047 knownFields := m.ProtoReflect().KnownFields()
48 extTypes := knownFields.ExtensionTypes()
49 extTypes.Register(xd.Type)
50 if val == nil {
51 return
52 }
53 pval := xd.Type.ValueOf(val)
54 knownFields.Set(wire.Number(xd.Field), pval)
55}
56
Herbie Ong0b0f4032019-03-18 19:06:15 -070057// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
58func dhex(s string) []byte {
59 b, err := hex.DecodeString(s)
60 if err != nil {
61 panic(err)
62 }
63 return b
64}
65
Herbie Ong7b828bc2019-02-08 19:56:24 -080066func TestMarshal(t *testing.T) {
67 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070068 desc string
69 mo jsonpb.MarshalOptions
70 input proto.Message
71 want string
72 wantErr bool // TODO: Verify error message substring.
Herbie Ong7b828bc2019-02-08 19:56:24 -080073 }{{
74 desc: "proto2 optional scalars not set",
75 input: &pb2.Scalars{},
76 want: "{}",
77 }, {
78 desc: "proto3 scalars not set",
79 input: &pb3.Scalars{},
80 want: "{}",
81 }, {
82 desc: "proto2 optional scalars set to zero values",
83 input: &pb2.Scalars{
84 OptBool: scalar.Bool(false),
85 OptInt32: scalar.Int32(0),
86 OptInt64: scalar.Int64(0),
87 OptUint32: scalar.Uint32(0),
88 OptUint64: scalar.Uint64(0),
89 OptSint32: scalar.Int32(0),
90 OptSint64: scalar.Int64(0),
91 OptFixed32: scalar.Uint32(0),
92 OptFixed64: scalar.Uint64(0),
93 OptSfixed32: scalar.Int32(0),
94 OptSfixed64: scalar.Int64(0),
95 OptFloat: scalar.Float32(0),
96 OptDouble: scalar.Float64(0),
97 OptBytes: []byte{},
98 OptString: scalar.String(""),
99 },
100 want: `{
101 "optBool": false,
102 "optInt32": 0,
103 "optInt64": "0",
104 "optUint32": 0,
105 "optUint64": "0",
106 "optSint32": 0,
107 "optSint64": "0",
108 "optFixed32": 0,
109 "optFixed64": "0",
110 "optSfixed32": 0,
111 "optSfixed64": "0",
112 "optFloat": 0,
113 "optDouble": 0,
114 "optBytes": "",
115 "optString": ""
116}`,
117 }, {
118 desc: "proto2 optional scalars set to some values",
119 input: &pb2.Scalars{
120 OptBool: scalar.Bool(true),
121 OptInt32: scalar.Int32(0xff),
122 OptInt64: scalar.Int64(0xdeadbeef),
123 OptUint32: scalar.Uint32(47),
124 OptUint64: scalar.Uint64(0xdeadbeef),
125 OptSint32: scalar.Int32(-1001),
126 OptSint64: scalar.Int64(-0xffff),
127 OptFixed64: scalar.Uint64(64),
128 OptSfixed32: scalar.Int32(-32),
129 OptFloat: scalar.Float32(1.02),
130 OptDouble: scalar.Float64(1.234),
Herbie Ong87608a72019-03-06 14:32:24 -0800131 OptBytes: []byte("谷歌"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800132 OptString: scalar.String("谷歌"),
133 },
134 want: `{
135 "optBool": true,
136 "optInt32": 255,
137 "optInt64": "3735928559",
138 "optUint32": 47,
139 "optUint64": "3735928559",
140 "optSint32": -1001,
141 "optSint64": "-65535",
142 "optFixed64": "64",
143 "optSfixed32": -32,
144 "optFloat": 1.02,
145 "optDouble": 1.234,
146 "optBytes": "6LC35q2M",
147 "optString": "谷歌"
148}`,
149 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700150 desc: "string",
151 input: &pb3.Scalars{
152 SString: "谷歌",
153 },
154 want: `{
155 "sString": "谷歌"
156}`,
157 }, {
158 desc: "string with invalid UTF8",
159 input: &pb3.Scalars{
160 SString: "abc\xff",
161 },
162 want: "{\n \"sString\": \"abc\xff\"\n}",
163 wantErr: true,
164 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800165 desc: "float nan",
166 input: &pb3.Scalars{
167 SFloat: float32(math.NaN()),
168 },
169 want: `{
170 "sFloat": "NaN"
171}`,
172 }, {
173 desc: "float positive infinity",
174 input: &pb3.Scalars{
175 SFloat: float32(math.Inf(1)),
176 },
177 want: `{
178 "sFloat": "Infinity"
179}`,
180 }, {
181 desc: "float negative infinity",
182 input: &pb3.Scalars{
183 SFloat: float32(math.Inf(-1)),
184 },
185 want: `{
186 "sFloat": "-Infinity"
187}`,
188 }, {
189 desc: "double nan",
190 input: &pb3.Scalars{
191 SDouble: math.NaN(),
192 },
193 want: `{
194 "sDouble": "NaN"
195}`,
196 }, {
197 desc: "double positive infinity",
198 input: &pb3.Scalars{
199 SDouble: math.Inf(1),
200 },
201 want: `{
202 "sDouble": "Infinity"
203}`,
204 }, {
205 desc: "double negative infinity",
206 input: &pb3.Scalars{
207 SDouble: math.Inf(-1),
208 },
209 want: `{
210 "sDouble": "-Infinity"
211}`,
212 }, {
213 desc: "proto2 enum not set",
214 input: &pb2.Enums{},
215 want: "{}",
216 }, {
217 desc: "proto2 enum set to zero value",
218 input: &pb2.Enums{
219 OptEnum: pb2Enum(0),
220 OptNestedEnum: pb2Enums_NestedEnum(0),
221 },
222 want: `{
223 "optEnum": 0,
224 "optNestedEnum": 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: `{
233 "optEnum": "ONE",
234 "optNestedEnum": "UNO"
235}`,
236 }, {
237 desc: "proto2 enum set to numeric values",
238 input: &pb2.Enums{
239 OptEnum: pb2Enum(2),
240 OptNestedEnum: pb2Enums_NestedEnum(2),
241 },
242 want: `{
243 "optEnum": "TWO",
244 "optNestedEnum": "DOS"
245}`,
246 }, {
247 desc: "proto2 enum set to unnamed numeric values",
248 input: &pb2.Enums{
249 OptEnum: pb2Enum(101),
250 OptNestedEnum: pb2Enums_NestedEnum(-101),
251 },
252 want: `{
253 "optEnum": 101,
254 "optNestedEnum": -101
255}`,
256 }, {
257 desc: "proto3 enum not set",
258 input: &pb3.Enums{},
259 want: "{}",
260 }, {
261 desc: "proto3 enum set to zero value",
262 input: &pb3.Enums{
263 SEnum: pb3.Enum_ZERO,
264 SNestedEnum: pb3.Enums_CERO,
265 },
266 want: "{}",
267 }, {
268 desc: "proto3 enum",
269 input: &pb3.Enums{
270 SEnum: pb3.Enum_ONE,
271 SNestedEnum: pb3.Enums_UNO,
272 },
273 want: `{
274 "sEnum": "ONE",
275 "sNestedEnum": "UNO"
276}`,
277 }, {
278 desc: "proto3 enum set to numeric values",
279 input: &pb3.Enums{
280 SEnum: 2,
281 SNestedEnum: 2,
282 },
283 want: `{
284 "sEnum": "TWO",
285 "sNestedEnum": "DOS"
286}`,
287 }, {
288 desc: "proto3 enum set to unnamed numeric values",
289 input: &pb3.Enums{
290 SEnum: -47,
291 SNestedEnum: 47,
292 },
293 want: `{
294 "sEnum": -47,
295 "sNestedEnum": 47
296}`,
297 }, {
298 desc: "proto2 nested message not set",
299 input: &pb2.Nests{},
300 want: "{}",
301 }, {
302 desc: "proto2 nested message set to empty",
303 input: &pb2.Nests{
304 OptNested: &pb2.Nested{},
305 Optgroup: &pb2.Nests_OptGroup{},
306 },
307 want: `{
308 "optNested": {},
309 "optgroup": {}
310}`,
311 }, {
312 desc: "proto2 nested messages",
313 input: &pb2.Nests{
314 OptNested: &pb2.Nested{
315 OptString: scalar.String("nested message"),
316 OptNested: &pb2.Nested{
317 OptString: scalar.String("another nested message"),
318 },
319 },
320 },
321 want: `{
322 "optNested": {
323 "optString": "nested message",
324 "optNested": {
325 "optString": "another nested message"
326 }
327 }
328}`,
329 }, {
330 desc: "proto2 groups",
331 input: &pb2.Nests{
332 Optgroup: &pb2.Nests_OptGroup{
333 OptString: scalar.String("inside a group"),
334 OptNested: &pb2.Nested{
335 OptString: scalar.String("nested message inside a group"),
336 },
337 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
338 OptFixed32: scalar.Uint32(47),
339 },
340 },
341 },
342 want: `{
343 "optgroup": {
344 "optString": "inside a group",
345 "optNested": {
346 "optString": "nested message inside a group"
347 },
348 "optnestedgroup": {
349 "optFixed32": 47
350 }
351 }
352}`,
353 }, {
354 desc: "proto3 nested message not set",
355 input: &pb3.Nests{},
356 want: "{}",
357 }, {
358 desc: "proto3 nested message set to empty",
359 input: &pb3.Nests{
360 SNested: &pb3.Nested{},
361 },
362 want: `{
363 "sNested": {}
364}`,
365 }, {
366 desc: "proto3 nested message",
367 input: &pb3.Nests{
368 SNested: &pb3.Nested{
369 SString: "nested message",
370 SNested: &pb3.Nested{
371 SString: "another nested message",
372 },
373 },
374 },
375 want: `{
376 "sNested": {
377 "sString": "nested message",
378 "sNested": {
379 "sString": "another nested message"
380 }
381 }
382}`,
383 }, {
384 desc: "oneof not set",
385 input: &pb3.Oneofs{},
386 want: "{}",
387 }, {
388 desc: "oneof set to empty string",
389 input: &pb3.Oneofs{
390 Union: &pb3.Oneofs_OneofString{},
391 },
392 want: `{
393 "oneofString": ""
394}`,
395 }, {
396 desc: "oneof set to string",
397 input: &pb3.Oneofs{
398 Union: &pb3.Oneofs_OneofString{
399 OneofString: "hello",
400 },
401 },
402 want: `{
403 "oneofString": "hello"
404}`,
405 }, {
406 desc: "oneof set to enum",
407 input: &pb3.Oneofs{
408 Union: &pb3.Oneofs_OneofEnum{
409 OneofEnum: pb3.Enum_ZERO,
410 },
411 },
412 want: `{
413 "oneofEnum": "ZERO"
414}`,
415 }, {
416 desc: "oneof set to empty message",
417 input: &pb3.Oneofs{
418 Union: &pb3.Oneofs_OneofNested{
419 OneofNested: &pb3.Nested{},
420 },
421 },
422 want: `{
423 "oneofNested": {}
424}`,
425 }, {
426 desc: "oneof set to message",
427 input: &pb3.Oneofs{
428 Union: &pb3.Oneofs_OneofNested{
429 OneofNested: &pb3.Nested{
430 SString: "nested message",
431 },
432 },
433 },
434 want: `{
435 "oneofNested": {
436 "sString": "nested message"
437 }
438}`,
439 }, {
440 desc: "repeated fields not set",
441 input: &pb2.Repeats{},
442 want: "{}",
443 }, {
444 desc: "repeated fields set to empty slices",
445 input: &pb2.Repeats{
446 RptBool: []bool{},
447 RptInt32: []int32{},
448 RptInt64: []int64{},
449 RptUint32: []uint32{},
450 RptUint64: []uint64{},
451 RptFloat: []float32{},
452 RptDouble: []float64{},
453 RptBytes: [][]byte{},
454 },
455 want: "{}",
456 }, {
457 desc: "repeated fields set to some values",
458 input: &pb2.Repeats{
459 RptBool: []bool{true, false, true, true},
460 RptInt32: []int32{1, 6, 0, 0},
461 RptInt64: []int64{-64, 47},
462 RptUint32: []uint32{0xff, 0xffff},
463 RptUint64: []uint64{0xdeadbeef},
464 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
465 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
466 RptString: []string{"hello", "世界"},
467 RptBytes: [][]byte{
468 []byte("hello"),
469 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
470 },
471 },
472 want: `{
473 "rptBool": [
474 true,
475 false,
476 true,
477 true
478 ],
479 "rptInt32": [
480 1,
481 6,
482 0,
483 0
484 ],
485 "rptInt64": [
486 "-64",
487 "47"
488 ],
489 "rptUint32": [
490 255,
491 65535
492 ],
493 "rptUint64": [
494 "3735928559"
495 ],
496 "rptFloat": [
497 "NaN",
498 "Infinity",
499 "-Infinity",
500 1.034
501 ],
502 "rptDouble": [
503 "NaN",
504 "Infinity",
505 "-Infinity",
506 1.23e-308
507 ],
508 "rptString": [
509 "hello",
510 "世界"
511 ],
512 "rptBytes": [
513 "aGVsbG8=",
514 "5LiW55WM"
515 ]
516}`,
517 }, {
518 desc: "repeated enums",
519 input: &pb2.Enums{
520 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
521 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
522 },
523 want: `{
524 "rptEnum": [
525 "ONE",
526 "TWO",
527 "TEN",
528 42
529 ],
530 "rptNestedEnum": [
531 "DOS",
532 47,
533 "DIEZ"
534 ]
535}`,
536 }, {
537 desc: "repeated messages set to empty",
538 input: &pb2.Nests{
539 RptNested: []*pb2.Nested{},
540 Rptgroup: []*pb2.Nests_RptGroup{},
541 },
542 want: "{}",
543 }, {
544 desc: "repeated messages",
545 input: &pb2.Nests{
546 RptNested: []*pb2.Nested{
547 {
548 OptString: scalar.String("repeat nested one"),
549 },
550 {
551 OptString: scalar.String("repeat nested two"),
552 OptNested: &pb2.Nested{
553 OptString: scalar.String("inside repeat nested two"),
554 },
555 },
556 {},
557 },
558 },
559 want: `{
560 "rptNested": [
561 {
562 "optString": "repeat nested one"
563 },
564 {
565 "optString": "repeat nested two",
566 "optNested": {
567 "optString": "inside repeat nested two"
568 }
569 },
570 {}
571 ]
572}`,
573 }, {
574 desc: "repeated messages contains nil value",
575 input: &pb2.Nests{
576 RptNested: []*pb2.Nested{nil, {}},
577 },
578 want: `{
579 "rptNested": [
580 {},
581 {}
582 ]
583}`,
584 }, {
585 desc: "repeated groups",
586 input: &pb2.Nests{
587 Rptgroup: []*pb2.Nests_RptGroup{
588 {
589 RptString: []string{"hello", "world"},
590 },
591 {},
592 nil,
593 },
594 },
595 want: `{
596 "rptgroup": [
597 {
598 "rptString": [
599 "hello",
600 "world"
601 ]
602 },
603 {},
604 {}
605 ]
606}`,
607 }, {
608 desc: "map fields not set",
609 input: &pb3.Maps{},
610 want: "{}",
611 }, {
612 desc: "map fields set to empty",
613 input: &pb3.Maps{
614 Int32ToStr: map[int32]string{},
615 BoolToUint32: map[bool]uint32{},
616 Uint64ToEnum: map[uint64]pb3.Enum{},
617 StrToNested: map[string]*pb3.Nested{},
618 StrToOneofs: map[string]*pb3.Oneofs{},
619 },
620 want: "{}",
621 }, {
622 desc: "map fields 1",
623 input: &pb3.Maps{
624 BoolToUint32: map[bool]uint32{
625 true: 42,
626 false: 101,
627 },
628 },
629 want: `{
630 "boolToUint32": {
631 "false": 101,
632 "true": 42
633 }
634}`,
635 }, {
636 desc: "map fields 2",
637 input: &pb3.Maps{
638 Int32ToStr: map[int32]string{
639 -101: "-101",
640 0xff: "0xff",
641 0: "zero",
642 },
643 },
644 want: `{
645 "int32ToStr": {
646 "-101": "-101",
647 "0": "zero",
648 "255": "0xff"
649 }
650}`,
651 }, {
652 desc: "map fields 3",
653 input: &pb3.Maps{
654 Uint64ToEnum: map[uint64]pb3.Enum{
655 1: pb3.Enum_ONE,
656 2: pb3.Enum_TWO,
657 10: pb3.Enum_TEN,
658 47: 47,
659 },
660 },
661 want: `{
662 "uint64ToEnum": {
663 "1": "ONE",
664 "2": "TWO",
665 "10": "TEN",
666 "47": 47
667 }
668}`,
669 }, {
670 desc: "map fields 4",
671 input: &pb3.Maps{
672 StrToNested: map[string]*pb3.Nested{
673 "nested": &pb3.Nested{
674 SString: "nested in a map",
675 },
676 },
677 },
678 want: `{
679 "strToNested": {
680 "nested": {
681 "sString": "nested in a map"
682 }
683 }
684}`,
685 }, {
686 desc: "map fields 5",
687 input: &pb3.Maps{
688 StrToOneofs: map[string]*pb3.Oneofs{
689 "string": &pb3.Oneofs{
690 Union: &pb3.Oneofs_OneofString{
691 OneofString: "hello",
692 },
693 },
694 "nested": &pb3.Oneofs{
695 Union: &pb3.Oneofs_OneofNested{
696 OneofNested: &pb3.Nested{
697 SString: "nested oneof in map field value",
698 },
699 },
700 },
701 },
702 },
703 want: `{
704 "strToOneofs": {
705 "nested": {
706 "oneofNested": {
707 "sString": "nested oneof in map field value"
708 }
709 },
710 "string": {
711 "oneofString": "hello"
712 }
713 }
714}`,
715 }, {
716 desc: "map field contains nil value",
717 input: &pb3.Maps{
718 StrToNested: map[string]*pb3.Nested{
719 "nil": nil,
720 },
721 },
722 want: `{
723 "strToNested": {
724 "nil": {}
725 }
726}`,
727 }, {
Herbie Ong329be5b2019-03-27 14:47:59 -0700728 desc: "required fields not set",
729 input: &pb2.Requireds{},
730 want: `{}`,
731 wantErr: true,
732 }, {
733 desc: "required fields partially set",
734 input: &pb2.Requireds{
735 ReqBool: scalar.Bool(false),
736 ReqSfixed64: scalar.Int64(0),
737 ReqDouble: scalar.Float64(1.23),
738 ReqString: scalar.String("hello"),
739 ReqEnum: pb2.Enum_ONE.Enum(),
740 },
741 want: `{
742 "reqBool": false,
743 "reqSfixed64": "0",
744 "reqDouble": 1.23,
745 "reqString": "hello",
746 "reqEnum": "ONE"
747}`,
748 wantErr: true,
749 }, {
750 desc: "required fields not set with AllowPartial",
751 mo: jsonpb.MarshalOptions{AllowPartial: true},
752 input: &pb2.Requireds{
753 ReqBool: scalar.Bool(false),
754 ReqSfixed64: scalar.Int64(0),
755 ReqDouble: scalar.Float64(1.23),
756 ReqString: scalar.String("hello"),
757 ReqEnum: pb2.Enum_ONE.Enum(),
758 },
759 want: `{
760 "reqBool": false,
761 "reqSfixed64": "0",
762 "reqDouble": 1.23,
763 "reqString": "hello",
764 "reqEnum": "ONE"
765}`,
766 }, {
767 desc: "required fields all set",
768 input: &pb2.Requireds{
769 ReqBool: scalar.Bool(false),
770 ReqSfixed64: scalar.Int64(0),
771 ReqDouble: scalar.Float64(1.23),
772 ReqString: scalar.String("hello"),
773 ReqEnum: pb2.Enum_ONE.Enum(),
774 ReqNested: &pb2.Nested{},
775 },
776 want: `{
777 "reqBool": false,
778 "reqSfixed64": "0",
779 "reqDouble": 1.23,
780 "reqString": "hello",
781 "reqEnum": "ONE",
782 "reqNested": {}
783}`,
784 }, {
785 desc: "indirect required field",
786 input: &pb2.IndirectRequired{
787 OptNested: &pb2.NestedWithRequired{},
788 },
789 want: `{
790 "optNested": {}
791}`,
792 wantErr: true,
793 }, {
794 desc: "indirect required field with AllowPartial",
795 mo: jsonpb.MarshalOptions{AllowPartial: true},
796 input: &pb2.IndirectRequired{
797 OptNested: &pb2.NestedWithRequired{},
798 },
799 want: `{
800 "optNested": {}
801}`,
802 }, {
803 desc: "indirect required field in empty repeated",
804 input: &pb2.IndirectRequired{
805 RptNested: []*pb2.NestedWithRequired{},
806 },
807 want: `{}`,
808 }, {
809 desc: "indirect required field in repeated",
810 input: &pb2.IndirectRequired{
811 RptNested: []*pb2.NestedWithRequired{
812 &pb2.NestedWithRequired{},
813 },
814 },
815 want: `{
816 "rptNested": [
817 {}
818 ]
819}`,
820 wantErr: true,
821 }, {
822 desc: "indirect required field in repeated with AllowPartial",
823 mo: jsonpb.MarshalOptions{AllowPartial: true},
824 input: &pb2.IndirectRequired{
825 RptNested: []*pb2.NestedWithRequired{
826 &pb2.NestedWithRequired{},
827 },
828 },
829 want: `{
830 "rptNested": [
831 {}
832 ]
833}`,
834 }, {
835 desc: "indirect required field in empty map",
836 input: &pb2.IndirectRequired{
837 StrToNested: map[string]*pb2.NestedWithRequired{},
838 },
839 want: "{}",
840 }, {
841 desc: "indirect required field in map",
842 input: &pb2.IndirectRequired{
843 StrToNested: map[string]*pb2.NestedWithRequired{
844 "fail": &pb2.NestedWithRequired{},
845 },
846 },
847 want: `{
848 "strToNested": {
849 "fail": {}
850 }
851}`,
852 wantErr: true,
853 }, {
854 desc: "indirect required field in map with AllowPartial",
855 mo: jsonpb.MarshalOptions{AllowPartial: true},
856 input: &pb2.IndirectRequired{
857 StrToNested: map[string]*pb2.NestedWithRequired{
858 "fail": &pb2.NestedWithRequired{},
859 },
860 },
861 want: `{
862 "strToNested": {
863 "fail": {}
864 }
865}`,
866 }, {
867 desc: "indirect required field in oneof",
868 input: &pb2.IndirectRequired{
869 Union: &pb2.IndirectRequired_OneofNested{
870 OneofNested: &pb2.NestedWithRequired{},
871 },
872 },
873 want: `{
874 "oneofNested": {}
875}`,
876 wantErr: true,
877 }, {
878 desc: "indirect required field in oneof with AllowPartial",
879 mo: jsonpb.MarshalOptions{AllowPartial: true},
880 input: &pb2.IndirectRequired{
881 Union: &pb2.IndirectRequired_OneofNested{
882 OneofNested: &pb2.NestedWithRequired{},
883 },
884 },
885 want: `{
886 "oneofNested": {}
887}`,
888 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800889 desc: "unknown fields are ignored",
890 input: &pb2.Scalars{
891 OptString: scalar.String("no unknowns"),
892 XXX_unrecognized: pack.Message{
893 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
894 }.Marshal(),
895 },
896 want: `{
897 "optString": "no unknowns"
898}`,
899 }, {
900 desc: "json_name",
901 input: &pb3.JSONNames{
902 SString: "json_name",
903 },
904 want: `{
905 "foo_bar": "json_name"
906}`,
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700907 }, {
908 desc: "extensions of non-repeated fields",
909 input: func() proto.Message {
910 m := &pb2.Extensions{
911 OptString: scalar.String("non-extension field"),
912 OptBool: scalar.Bool(true),
913 OptInt32: scalar.Int32(42),
914 }
915 setExtension(m, pb2.E_OptExtBool, true)
916 setExtension(m, pb2.E_OptExtString, "extension field")
917 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
918 setExtension(m, pb2.E_OptExtNested, &pb2.Nested{
919 OptString: scalar.String("nested in an extension"),
920 OptNested: &pb2.Nested{
921 OptString: scalar.String("another nested in an extension"),
922 },
923 })
924 return m
925 }(),
926 want: `{
927 "optString": "non-extension field",
928 "optBool": true,
929 "optInt32": 42,
930 "[pb2.opt_ext_bool]": true,
931 "[pb2.opt_ext_enum]": "TEN",
932 "[pb2.opt_ext_nested]": {
933 "optString": "nested in an extension",
934 "optNested": {
935 "optString": "another nested in an extension"
936 }
937 },
938 "[pb2.opt_ext_string]": "extension field"
939}`,
940 }, {
941 desc: "extension message field set to nil",
942 input: func() proto.Message {
943 m := &pb2.Extensions{}
944 setExtension(m, pb2.E_OptExtNested, nil)
945 return m
946 }(),
947 want: "{}",
948 }, {
949 desc: "extensions of repeated fields",
950 input: func() proto.Message {
951 m := &pb2.Extensions{}
952 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
953 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
954 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
955 &pb2.Nested{OptString: scalar.String("one")},
956 &pb2.Nested{OptString: scalar.String("two")},
957 &pb2.Nested{OptString: scalar.String("three")},
958 })
959 return m
960 }(),
961 want: `{
962 "[pb2.rpt_ext_enum]": [
963 "TEN",
964 101,
965 "ONE"
966 ],
967 "[pb2.rpt_ext_fixed32]": [
968 42,
969 47
970 ],
971 "[pb2.rpt_ext_nested]": [
972 {
973 "optString": "one"
974 },
975 {
976 "optString": "two"
977 },
978 {
979 "optString": "three"
980 }
981 ]
982}`,
983 }, {
984 desc: "extensions of non-repeated fields in another message",
985 input: func() proto.Message {
986 m := &pb2.Extensions{}
987 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
988 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
989 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
990 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
991 OptString: scalar.String("nested in an extension"),
992 OptNested: &pb2.Nested{
993 OptString: scalar.String("another nested in an extension"),
994 },
995 })
996 return m
997 }(),
998 want: `{
999 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
1000 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
1001 "[pb2.ExtensionsContainer.opt_ext_nested]": {
1002 "optString": "nested in an extension",
1003 "optNested": {
1004 "optString": "another nested in an extension"
1005 }
1006 },
1007 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
1008}`,
1009 }, {
1010 desc: "extensions of repeated fields in another message",
1011 input: func() proto.Message {
1012 m := &pb2.Extensions{
1013 OptString: scalar.String("non-extension field"),
1014 OptBool: scalar.Bool(true),
1015 OptInt32: scalar.Int32(42),
1016 }
1017 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1018 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
1019 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
1020 &pb2.Nested{OptString: scalar.String("one")},
1021 &pb2.Nested{OptString: scalar.String("two")},
1022 &pb2.Nested{OptString: scalar.String("three")},
1023 })
1024 return m
1025 }(),
1026 want: `{
1027 "optString": "non-extension field",
1028 "optBool": true,
1029 "optInt32": 42,
1030 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
1031 "TEN",
1032 101,
1033 "ONE"
1034 ],
1035 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1036 {
1037 "optString": "one"
1038 },
1039 {
1040 "optString": "two"
1041 },
1042 {
1043 "optString": "three"
1044 }
1045 ],
1046 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1047 "hello",
1048 "world"
1049 ]
1050}`,
1051 }, {
1052 desc: "MessageSet",
1053 input: func() proto.Message {
1054 m := &pb2.MessageSet{}
1055 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1056 OptString: scalar.String("a messageset extension"),
1057 })
1058 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1059 OptString: scalar.String("not a messageset extension"),
1060 })
1061 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1062 OptString: scalar.String("just a regular extension"),
1063 })
1064 return m
1065 }(),
1066 want: `{
1067 "[pb2.MessageSetExtension]": {
1068 "optString": "a messageset extension"
1069 },
1070 "[pb2.MessageSetExtension.ext_nested]": {
1071 "optString": "just a regular extension"
1072 },
1073 "[pb2.MessageSetExtension.not_message_set_extension]": {
1074 "optString": "not a messageset extension"
1075 }
1076}`,
1077 }, {
1078 desc: "not real MessageSet 1",
1079 input: func() proto.Message {
1080 m := &pb2.FakeMessageSet{}
1081 setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1082 OptString: scalar.String("not a messageset extension"),
1083 })
1084 return m
1085 }(),
1086 want: `{
1087 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1088 "optString": "not a messageset extension"
1089 }
1090}`,
1091 }, {
1092 desc: "not real MessageSet 2",
1093 input: func() proto.Message {
1094 m := &pb2.MessageSet{}
1095 setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1096 OptString: scalar.String("another not a messageset extension"),
1097 })
1098 return m
1099 }(),
1100 want: `{
1101 "[pb2.message_set_extension]": {
1102 "optString": "another not a messageset extension"
1103 }
1104}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001105 }, {
1106 desc: "BoolValue empty",
1107 input: &knownpb.BoolValue{},
1108 want: `false`,
1109 }, {
1110 desc: "BoolValue",
1111 input: &knownpb.BoolValue{Value: true},
1112 want: `true`,
1113 }, {
1114 desc: "Int32Value empty",
1115 input: &knownpb.Int32Value{},
1116 want: `0`,
1117 }, {
1118 desc: "Int32Value",
1119 input: &knownpb.Int32Value{Value: 42},
1120 want: `42`,
1121 }, {
1122 desc: "Int64Value",
1123 input: &knownpb.Int64Value{Value: 42},
1124 want: `"42"`,
1125 }, {
1126 desc: "UInt32Value",
1127 input: &knownpb.UInt32Value{Value: 42},
1128 want: `42`,
1129 }, {
1130 desc: "UInt64Value",
1131 input: &knownpb.UInt64Value{Value: 42},
1132 want: `"42"`,
1133 }, {
1134 desc: "FloatValue",
1135 input: &knownpb.FloatValue{Value: 1.02},
1136 want: `1.02`,
1137 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001138 desc: "FloatValue Infinity",
1139 input: &knownpb.FloatValue{Value: float32(math.Inf(-1))},
1140 want: `"-Infinity"`,
1141 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001142 desc: "DoubleValue",
1143 input: &knownpb.DoubleValue{Value: 1.02},
1144 want: `1.02`,
1145 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001146 desc: "DoubleValue NaN",
1147 input: &knownpb.DoubleValue{Value: math.NaN()},
1148 want: `"NaN"`,
1149 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001150 desc: "StringValue empty",
1151 input: &knownpb.StringValue{},
1152 want: `""`,
1153 }, {
1154 desc: "StringValue",
1155 input: &knownpb.StringValue{Value: "谷歌"},
1156 want: `"谷歌"`,
1157 }, {
1158 desc: "StringValue with invalid UTF8 error",
1159 input: &knownpb.StringValue{Value: "abc\xff"},
1160 want: "\"abc\xff\"",
1161 wantErr: true,
1162 }, {
1163 desc: "StringValue field with invalid UTF8 error",
1164 input: &pb2.KnownTypes{
1165 OptString: &knownpb.StringValue{Value: "abc\xff"},
1166 },
1167 want: "{\n \"optString\": \"abc\xff\"\n}",
1168 wantErr: true,
1169 }, {
1170 desc: "BytesValue",
1171 input: &knownpb.BytesValue{Value: []byte("hello")},
1172 want: `"aGVsbG8="`,
1173 }, {
1174 desc: "Empty",
1175 input: &knownpb.Empty{},
1176 want: `{}`,
1177 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001178 desc: "NullValue field",
1179 input: &pb2.KnownTypes{OptNull: new(knownpb.NullValue)},
1180 want: `{
1181 "optNull": null
1182}`,
1183 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001184 desc: "Value empty",
1185 input: &knownpb.Value{},
1186 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001187 }, {
1188 desc: "Value empty field",
1189 input: &pb2.KnownTypes{
1190 OptValue: &knownpb.Value{},
1191 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001192 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001193 }, {
1194 desc: "Value contains NullValue",
1195 input: &knownpb.Value{Kind: &knownpb.Value_NullValue{}},
1196 want: `null`,
1197 }, {
1198 desc: "Value contains BoolValue",
1199 input: &knownpb.Value{Kind: &knownpb.Value_BoolValue{}},
1200 want: `false`,
1201 }, {
1202 desc: "Value contains NumberValue",
1203 input: &knownpb.Value{Kind: &knownpb.Value_NumberValue{1.02}},
1204 want: `1.02`,
1205 }, {
1206 desc: "Value contains StringValue",
1207 input: &knownpb.Value{Kind: &knownpb.Value_StringValue{"hello"}},
1208 want: `"hello"`,
1209 }, {
1210 desc: "Value contains StringValue with invalid UTF8",
1211 input: &knownpb.Value{Kind: &knownpb.Value_StringValue{"\xff"}},
1212 want: "\"\xff\"",
1213 wantErr: true,
1214 }, {
1215 desc: "Value contains Struct",
1216 input: &knownpb.Value{
1217 Kind: &knownpb.Value_StructValue{
1218 &knownpb.Struct{
1219 Fields: map[string]*knownpb.Value{
1220 "null": {Kind: &knownpb.Value_NullValue{}},
1221 "number": {Kind: &knownpb.Value_NumberValue{}},
1222 "string": {Kind: &knownpb.Value_StringValue{}},
1223 "struct": {Kind: &knownpb.Value_StructValue{}},
1224 "list": {Kind: &knownpb.Value_ListValue{}},
1225 "bool": {Kind: &knownpb.Value_BoolValue{}},
1226 },
1227 },
1228 },
1229 },
1230 want: `{
1231 "bool": false,
1232 "list": [],
1233 "null": null,
1234 "number": 0,
1235 "string": "",
1236 "struct": {}
1237}`,
1238 }, {
1239 desc: "Value contains ListValue",
1240 input: &knownpb.Value{
1241 Kind: &knownpb.Value_ListValue{
1242 &knownpb.ListValue{
1243 Values: []*knownpb.Value{
1244 {Kind: &knownpb.Value_BoolValue{}},
1245 {Kind: &knownpb.Value_NullValue{}},
1246 {Kind: &knownpb.Value_NumberValue{}},
1247 {Kind: &knownpb.Value_StringValue{}},
1248 {Kind: &knownpb.Value_StructValue{}},
1249 {Kind: &knownpb.Value_ListValue{}},
1250 },
1251 },
1252 },
1253 },
1254 want: `[
1255 false,
1256 null,
1257 0,
1258 "",
1259 {},
1260 []
1261]`,
1262 }, {
1263 desc: "Struct with nil map",
1264 input: &knownpb.Struct{},
1265 want: `{}`,
1266 }, {
1267 desc: "Struct with empty map",
1268 input: &knownpb.Struct{
1269 Fields: map[string]*knownpb.Value{},
1270 },
1271 want: `{}`,
1272 }, {
1273 desc: "Struct",
1274 input: &knownpb.Struct{
1275 Fields: map[string]*knownpb.Value{
1276 "bool": {Kind: &knownpb.Value_BoolValue{true}},
1277 "null": {Kind: &knownpb.Value_NullValue{}},
1278 "number": {Kind: &knownpb.Value_NumberValue{3.1415}},
1279 "string": {Kind: &knownpb.Value_StringValue{"hello"}},
1280 "struct": {
1281 Kind: &knownpb.Value_StructValue{
1282 &knownpb.Struct{
1283 Fields: map[string]*knownpb.Value{
1284 "string": {Kind: &knownpb.Value_StringValue{"world"}},
1285 },
1286 },
1287 },
1288 },
1289 "list": {
1290 Kind: &knownpb.Value_ListValue{
1291 &knownpb.ListValue{
1292 Values: []*knownpb.Value{
1293 {Kind: &knownpb.Value_BoolValue{}},
1294 {Kind: &knownpb.Value_NullValue{}},
1295 {Kind: &knownpb.Value_NumberValue{}},
1296 },
1297 },
1298 },
1299 },
1300 },
1301 },
1302 want: `{
1303 "bool": true,
1304 "list": [
1305 false,
1306 null,
1307 0
1308 ],
1309 "null": null,
1310 "number": 3.1415,
1311 "string": "hello",
1312 "struct": {
1313 "string": "world"
1314 }
1315}`,
1316 }, {
1317 desc: "Struct message with invalid UTF8 string",
1318 input: &knownpb.Struct{
1319 Fields: map[string]*knownpb.Value{
1320 "string": {Kind: &knownpb.Value_StringValue{"\xff"}},
1321 },
1322 },
1323 want: "{\n \"string\": \"\xff\"\n}",
1324 wantErr: true,
1325 }, {
1326 desc: "ListValue with nil values",
1327 input: &knownpb.ListValue{},
1328 want: `[]`,
1329 }, {
1330 desc: "ListValue with empty values",
1331 input: &knownpb.ListValue{
1332 Values: []*knownpb.Value{},
1333 },
1334 want: `[]`,
1335 }, {
1336 desc: "ListValue",
1337 input: &knownpb.ListValue{
1338 Values: []*knownpb.Value{
1339 {Kind: &knownpb.Value_BoolValue{true}},
1340 {Kind: &knownpb.Value_NullValue{}},
1341 {Kind: &knownpb.Value_NumberValue{3.1415}},
1342 {Kind: &knownpb.Value_StringValue{"hello"}},
1343 {
1344 Kind: &knownpb.Value_ListValue{
1345 &knownpb.ListValue{
1346 Values: []*knownpb.Value{
1347 {Kind: &knownpb.Value_BoolValue{}},
1348 {Kind: &knownpb.Value_NullValue{}},
1349 {Kind: &knownpb.Value_NumberValue{}},
1350 },
1351 },
1352 },
1353 },
1354 {
1355 Kind: &knownpb.Value_StructValue{
1356 &knownpb.Struct{
1357 Fields: map[string]*knownpb.Value{
1358 "string": {Kind: &knownpb.Value_StringValue{"world"}},
1359 },
1360 },
1361 },
1362 },
1363 },
1364 },
1365 want: `[
1366 true,
1367 null,
1368 3.1415,
1369 "hello",
1370 [
1371 false,
1372 null,
1373 0
1374 ],
1375 {
1376 "string": "world"
1377 }
1378]`,
1379 }, {
1380 desc: "ListValue with invalid UTF8 string",
1381 input: &knownpb.ListValue{
1382 Values: []*knownpb.Value{
1383 {Kind: &knownpb.Value_StringValue{"\xff"}},
1384 },
1385 },
1386 want: "[\n \"\xff\"\n]",
1387 wantErr: true,
1388 }, {
1389 desc: "Duration empty",
1390 input: &knownpb.Duration{},
1391 want: `"0s"`,
1392 }, {
1393 desc: "Duration with secs",
1394 input: &knownpb.Duration{Seconds: 3},
1395 want: `"3s"`,
1396 }, {
1397 desc: "Duration with -secs",
1398 input: &knownpb.Duration{Seconds: -3},
1399 want: `"-3s"`,
1400 }, {
1401 desc: "Duration with nanos",
1402 input: &knownpb.Duration{Nanos: 1e6},
1403 want: `"0.001s"`,
1404 }, {
1405 desc: "Duration with -nanos",
1406 input: &knownpb.Duration{Nanos: -1e6},
1407 want: `"-0.001s"`,
1408 }, {
1409 desc: "Duration with large secs",
1410 input: &knownpb.Duration{Seconds: 1e10, Nanos: 1},
1411 want: `"10000000000.000000001s"`,
1412 }, {
1413 desc: "Duration with 6-digit nanos",
1414 input: &knownpb.Duration{Nanos: 1e4},
1415 want: `"0.000010s"`,
1416 }, {
1417 desc: "Duration with 3-digit nanos",
1418 input: &knownpb.Duration{Nanos: 1e6},
1419 want: `"0.001s"`,
1420 }, {
1421 desc: "Duration with -secs -nanos",
1422 input: &knownpb.Duration{Seconds: -123, Nanos: -450},
1423 want: `"-123.000000450s"`,
1424 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001425 desc: "Duration max value",
1426 input: &knownpb.Duration{Seconds: 315576000000, Nanos: 999999999},
1427 want: `"315576000000.999999999s"`,
1428 }, {
1429 desc: "Duration min value",
1430 input: &knownpb.Duration{Seconds: -315576000000, Nanos: -999999999},
1431 want: `"-315576000000.999999999s"`,
1432 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001433 desc: "Duration with +secs -nanos",
1434 input: &knownpb.Duration{Seconds: 1, Nanos: -1},
1435 wantErr: true,
1436 }, {
1437 desc: "Duration with -secs +nanos",
1438 input: &knownpb.Duration{Seconds: -1, Nanos: 1},
1439 wantErr: true,
1440 }, {
1441 desc: "Duration with +secs out of range",
1442 input: &knownpb.Duration{Seconds: 315576000001},
1443 wantErr: true,
1444 }, {
1445 desc: "Duration with -secs out of range",
1446 input: &knownpb.Duration{Seconds: -315576000001},
1447 wantErr: true,
1448 }, {
1449 desc: "Duration with +nanos out of range",
1450 input: &knownpb.Duration{Seconds: 0, Nanos: 1e9},
1451 wantErr: true,
1452 }, {
1453 desc: "Duration with -nanos out of range",
1454 input: &knownpb.Duration{Seconds: 0, Nanos: -1e9},
1455 wantErr: true,
1456 }, {
1457 desc: "Timestamp zero",
1458 input: &knownpb.Timestamp{},
1459 want: `"1970-01-01T00:00:00Z"`,
1460 }, {
1461 desc: "Timestamp",
1462 input: &knownpb.Timestamp{Seconds: 1553036601},
1463 want: `"2019-03-19T23:03:21Z"`,
1464 }, {
1465 desc: "Timestamp with nanos",
1466 input: &knownpb.Timestamp{Seconds: 1553036601, Nanos: 1},
1467 want: `"2019-03-19T23:03:21.000000001Z"`,
1468 }, {
1469 desc: "Timestamp with 6-digit nanos",
1470 input: &knownpb.Timestamp{Nanos: 1e3},
1471 want: `"1970-01-01T00:00:00.000001Z"`,
1472 }, {
1473 desc: "Timestamp with 3-digit nanos",
1474 input: &knownpb.Timestamp{Nanos: 1e7},
1475 want: `"1970-01-01T00:00:00.010Z"`,
1476 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001477 desc: "Timestamp max value",
1478 input: &knownpb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
1479 want: `"9999-12-31T23:59:59.999999999Z"`,
1480 }, {
1481 desc: "Timestamp min value",
1482 input: &knownpb.Timestamp{Seconds: -62135596800},
1483 want: `"0001-01-01T00:00:00Z"`,
1484 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001485 desc: "Timestamp with +secs out of range",
1486 input: &knownpb.Timestamp{Seconds: 253402300800},
1487 wantErr: true,
1488 }, {
1489 desc: "Timestamp with -secs out of range",
1490 input: &knownpb.Timestamp{Seconds: -62135596801},
1491 wantErr: true,
1492 }, {
1493 desc: "Timestamp with -nanos",
1494 input: &knownpb.Timestamp{Nanos: -1},
1495 wantErr: true,
1496 }, {
1497 desc: "Timestamp with +nanos out of range",
1498 input: &knownpb.Timestamp{Nanos: 1e9},
1499 wantErr: true,
1500 }, {
1501 desc: "FieldMask empty",
1502 input: &knownpb.FieldMask{},
1503 want: `""`,
1504 }, {
1505 desc: "FieldMask",
1506 input: &knownpb.FieldMask{
1507 Paths: []string{
1508 "foo",
1509 "foo_bar",
1510 "foo.bar_qux",
1511 "_foo",
1512 },
1513 },
1514 want: `"foo,fooBar,foo.barQux,Foo"`,
1515 }, {
1516 desc: "FieldMask error 1",
1517 input: &knownpb.FieldMask{
1518 Paths: []string{"foo_"},
1519 },
1520 wantErr: true,
1521 }, {
1522 desc: "FieldMask error 2",
1523 input: &knownpb.FieldMask{
1524 Paths: []string{"foo__bar"},
1525 },
1526 wantErr: true,
1527 }, {
1528 desc: "Any empty",
1529 input: &knownpb.Any{},
1530 want: `{}`,
1531 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001532 desc: "Any with non-custom message",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001533 mo: jsonpb.MarshalOptions{
1534 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1535 },
1536 input: func() proto.Message {
1537 m := &pb2.Nested{
1538 OptString: scalar.String("embedded inside Any"),
1539 OptNested: &pb2.Nested{
1540 OptString: scalar.String("inception"),
1541 },
1542 }
1543 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1544 if err != nil {
1545 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1546 }
1547 return &knownpb.Any{
1548 TypeUrl: "foo/pb2.Nested",
1549 Value: b,
1550 }
1551 }(),
1552 want: `{
1553 "@type": "foo/pb2.Nested",
1554 "optString": "embedded inside Any",
1555 "optNested": {
1556 "optString": "inception"
1557 }
1558}`,
1559 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001560 desc: "Any with empty embedded message",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001561 mo: jsonpb.MarshalOptions{
1562 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1563 },
1564 input: &knownpb.Any{TypeUrl: "foo/pb2.Nested"},
1565 want: `{
1566 "@type": "foo/pb2.Nested"
1567}`,
1568 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001569 desc: "Any without registered type",
1570 mo: jsonpb.MarshalOptions{Resolver: preg.NewTypes()},
1571 input: &knownpb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001572 wantErr: true,
1573 }, {
1574 desc: "Any with missing required error",
1575 mo: jsonpb.MarshalOptions{
1576 Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
1577 },
1578 input: func() proto.Message {
1579 m := &pb2.PartialRequired{
1580 OptString: scalar.String("embedded inside Any"),
1581 }
Damien Neil96c229a2019-04-03 12:17:24 -07001582 b, err := proto.MarshalOptions{
1583 AllowPartial: true,
1584 Deterministic: true,
1585 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001586 if err != nil {
1587 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1588 }
1589 return &knownpb.Any{
1590 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1591 Value: b,
1592 }
1593 }(),
1594 want: `{
1595 "@type": "pb2.PartialRequired",
1596 "optString": "embedded inside Any"
1597}`,
1598 wantErr: true,
1599 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001600 desc: "Any with partial required and AllowPartial",
1601 mo: jsonpb.MarshalOptions{
1602 AllowPartial: true,
1603 Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
1604 },
1605 input: func() proto.Message {
1606 m := &pb2.PartialRequired{
1607 OptString: scalar.String("embedded inside Any"),
1608 }
Damien Neil96c229a2019-04-03 12:17:24 -07001609 b, err := proto.MarshalOptions{
1610 AllowPartial: true,
1611 Deterministic: true,
1612 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001613 if err != nil {
1614 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1615 }
1616 return &knownpb.Any{
1617 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1618 Value: b,
1619 }
1620 }(),
1621 want: `{
1622 "@type": "pb2.PartialRequired",
1623 "optString": "embedded inside Any"
1624}`,
1625 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001626 desc: "Any with invalid UTF8",
1627 mo: jsonpb.MarshalOptions{
1628 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1629 },
1630 input: func() proto.Message {
1631 m := &pb2.Nested{
1632 OptString: scalar.String("abc\xff"),
1633 }
1634 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1635 if err != nil {
1636 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1637 }
1638 return &knownpb.Any{
1639 TypeUrl: "foo/pb2.Nested",
1640 Value: b,
1641 }
1642 }(),
1643 want: `{
1644 "@type": "foo/pb2.Nested",
1645 "optString": "` + "abc\xff" + `"
1646}`,
1647 wantErr: true,
1648 }, {
1649 desc: "Any with invalid value",
1650 mo: jsonpb.MarshalOptions{
1651 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1652 },
1653 input: &knownpb.Any{
1654 TypeUrl: "foo/pb2.Nested",
1655 Value: dhex("80"),
1656 },
1657 wantErr: true,
1658 }, {
1659 desc: "Any with BoolValue",
1660 mo: jsonpb.MarshalOptions{
1661 Resolver: preg.NewTypes((&knownpb.BoolValue{}).ProtoReflect().Type()),
1662 },
1663 input: func() proto.Message {
1664 m := &knownpb.BoolValue{Value: true}
1665 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1666 if err != nil {
1667 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1668 }
1669 return &knownpb.Any{
1670 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1671 Value: b,
1672 }
1673 }(),
1674 want: `{
1675 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1676 "value": true
1677}`,
1678 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001679 desc: "Any with Empty",
1680 mo: jsonpb.MarshalOptions{
1681 Resolver: preg.NewTypes((&knownpb.Empty{}).ProtoReflect().Type()),
1682 },
1683 input: func() proto.Message {
1684 m := &knownpb.Empty{}
1685 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1686 if err != nil {
1687 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1688 }
1689 return &knownpb.Any{
1690 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1691 Value: b,
1692 }
1693 }(),
1694 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001695 "@type": "type.googleapis.com/google.protobuf.Empty",
1696 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001697}`,
1698 }, {
1699 desc: "Any with StringValue containing invalid UTF8",
1700 mo: jsonpb.MarshalOptions{
1701 Resolver: preg.NewTypes((&knownpb.StringValue{}).ProtoReflect().Type()),
1702 },
1703 input: func() proto.Message {
Damien Neilbc310b52019-04-11 11:46:55 -07001704 m := &knownpb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001705 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1706 if err != nil {
1707 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1708 }
1709 return &knownpb.Any{
1710 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001711 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001712 }
1713 }(),
1714 want: `{
1715 "@type": "google.protobuf.StringValue",
1716 "value": "` + "abc\xff" + `"
1717}`,
1718 wantErr: true,
1719 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001720 desc: "Any with Int64Value",
1721 mo: jsonpb.MarshalOptions{
1722 Resolver: preg.NewTypes((&knownpb.Int64Value{}).ProtoReflect().Type()),
1723 },
1724 input: func() proto.Message {
1725 m := &knownpb.Int64Value{Value: 42}
1726 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1727 if err != nil {
1728 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1729 }
1730 return &knownpb.Any{
1731 TypeUrl: "google.protobuf.Int64Value",
1732 Value: b,
1733 }
1734 }(),
1735 want: `{
1736 "@type": "google.protobuf.Int64Value",
1737 "value": "42"
1738}`,
1739 }, {
1740 desc: "Any with Duration",
1741 mo: jsonpb.MarshalOptions{
1742 Resolver: preg.NewTypes((&knownpb.Duration{}).ProtoReflect().Type()),
1743 },
1744 input: func() proto.Message {
1745 m := &knownpb.Duration{}
1746 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1747 if err != nil {
1748 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1749 }
1750 return &knownpb.Any{
1751 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1752 Value: b,
1753 }
1754 }(),
1755 want: `{
1756 "@type": "type.googleapis.com/google.protobuf.Duration",
1757 "value": "0s"
1758}`,
1759 }, {
1760 desc: "Any with empty Value",
1761 mo: jsonpb.MarshalOptions{
1762 Resolver: preg.NewTypes((&knownpb.Value{}).ProtoReflect().Type()),
1763 },
1764 input: func() proto.Message {
1765 m := &knownpb.Value{}
1766 b, err := proto.Marshal(m)
1767 if err != nil {
1768 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1769 }
1770 return &knownpb.Any{
1771 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1772 Value: b,
1773 }
1774 }(),
1775 wantErr: true,
1776 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001777 desc: "Any with Value of StringValue",
1778 mo: jsonpb.MarshalOptions{
1779 Resolver: preg.NewTypes((&knownpb.Value{}).ProtoReflect().Type()),
1780 },
1781 input: func() proto.Message {
Damien Neilbc310b52019-04-11 11:46:55 -07001782 m := &knownpb.Value{Kind: &knownpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001783 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1784 if err != nil {
1785 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1786 }
1787 return &knownpb.Any{
1788 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001789 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001790 }
1791 }(),
1792 want: `{
1793 "@type": "type.googleapis.com/google.protobuf.Value",
1794 "value": "` + "abc\xff" + `"
1795}`,
1796 wantErr: true,
1797 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001798 desc: "Any with Value of NullValue",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001799 mo: jsonpb.MarshalOptions{
1800 Resolver: preg.NewTypes((&knownpb.Value{}).ProtoReflect().Type()),
1801 },
1802 input: func() proto.Message {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001803 m := &knownpb.Value{Kind: &knownpb.Value_NullValue{}}
1804 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001805 if err != nil {
1806 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1807 }
1808 return &knownpb.Any{
1809 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1810 Value: b,
1811 }
1812 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001813 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001814 "@type": "type.googleapis.com/google.protobuf.Value",
1815 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001816}`,
1817 }, {
1818 desc: "Any with Struct",
1819 mo: jsonpb.MarshalOptions{
1820 Resolver: preg.NewTypes(
1821 (&knownpb.Struct{}).ProtoReflect().Type(),
1822 (&knownpb.Value{}).ProtoReflect().Type(),
1823 (&knownpb.BoolValue{}).ProtoReflect().Type(),
1824 knownpb.NullValue_NULL_VALUE.Type(),
1825 (&knownpb.StringValue{}).ProtoReflect().Type(),
1826 ),
1827 },
1828 input: func() proto.Message {
1829 m := &knownpb.Struct{
1830 Fields: map[string]*knownpb.Value{
1831 "bool": {Kind: &knownpb.Value_BoolValue{true}},
1832 "null": {Kind: &knownpb.Value_NullValue{}},
1833 "string": {Kind: &knownpb.Value_StringValue{"hello"}},
1834 "struct": {
1835 Kind: &knownpb.Value_StructValue{
1836 &knownpb.Struct{
1837 Fields: map[string]*knownpb.Value{
1838 "string": {Kind: &knownpb.Value_StringValue{"world"}},
1839 },
1840 },
1841 },
1842 },
1843 },
1844 }
1845 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1846 if err != nil {
1847 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1848 }
1849 return &knownpb.Any{
1850 TypeUrl: "google.protobuf.Struct",
1851 Value: b,
1852 }
1853 }(),
1854 want: `{
1855 "@type": "google.protobuf.Struct",
1856 "value": {
1857 "bool": true,
1858 "null": null,
1859 "string": "hello",
1860 "struct": {
1861 "string": "world"
1862 }
1863 }
1864}`,
1865 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001866 desc: "Any with missing type_url",
1867 mo: jsonpb.MarshalOptions{
1868 Resolver: preg.NewTypes((&knownpb.BoolValue{}).ProtoReflect().Type()),
1869 },
1870 input: func() proto.Message {
1871 m := &knownpb.BoolValue{Value: true}
1872 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1873 if err != nil {
1874 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1875 }
1876 return &knownpb.Any{
1877 Value: b,
1878 }
1879 }(),
1880 wantErr: true,
1881 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001882 desc: "well known types as field values",
1883 mo: jsonpb.MarshalOptions{
1884 Resolver: preg.NewTypes((&knownpb.Empty{}).ProtoReflect().Type()),
1885 },
1886 input: &pb2.KnownTypes{
1887 OptBool: &knownpb.BoolValue{Value: false},
1888 OptInt32: &knownpb.Int32Value{Value: 42},
1889 OptInt64: &knownpb.Int64Value{Value: 42},
1890 OptUint32: &knownpb.UInt32Value{Value: 42},
1891 OptUint64: &knownpb.UInt64Value{Value: 42},
1892 OptFloat: &knownpb.FloatValue{Value: 1.23},
1893 OptDouble: &knownpb.DoubleValue{Value: 3.1415},
1894 OptString: &knownpb.StringValue{Value: "hello"},
1895 OptBytes: &knownpb.BytesValue{Value: []byte("hello")},
1896 OptDuration: &knownpb.Duration{Seconds: 123},
1897 OptTimestamp: &knownpb.Timestamp{Seconds: 1553036601},
1898 OptStruct: &knownpb.Struct{
1899 Fields: map[string]*knownpb.Value{
1900 "string": {Kind: &knownpb.Value_StringValue{"hello"}},
1901 },
1902 },
1903 OptList: &knownpb.ListValue{
1904 Values: []*knownpb.Value{
1905 {Kind: &knownpb.Value_NullValue{}},
1906 {Kind: &knownpb.Value_StringValue{}},
1907 {Kind: &knownpb.Value_StructValue{}},
1908 {Kind: &knownpb.Value_ListValue{}},
1909 },
1910 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001911 OptValue: &knownpb.Value{
1912 Kind: &knownpb.Value_StringValue{"world"},
1913 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001914 OptEmpty: &knownpb.Empty{},
1915 OptAny: &knownpb.Any{
1916 TypeUrl: "google.protobuf.Empty",
1917 },
1918 OptFieldmask: &knownpb.FieldMask{
1919 Paths: []string{"foo_bar", "bar_foo"},
1920 },
1921 },
1922 want: `{
1923 "optBool": false,
1924 "optInt32": 42,
1925 "optInt64": "42",
1926 "optUint32": 42,
1927 "optUint64": "42",
1928 "optFloat": 1.23,
1929 "optDouble": 3.1415,
1930 "optString": "hello",
1931 "optBytes": "aGVsbG8=",
1932 "optDuration": "123s",
1933 "optTimestamp": "2019-03-19T23:03:21Z",
1934 "optStruct": {
1935 "string": "hello"
1936 },
1937 "optList": [
1938 null,
1939 "",
1940 {},
1941 []
1942 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001943 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001944 "optEmpty": {},
1945 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001946 "@type": "google.protobuf.Empty",
1947 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001948 },
1949 "optFieldmask": "fooBar,barFoo"
1950}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001951 }}
1952
1953 for _, tt := range tests {
1954 tt := tt
1955 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001956 // Use 2-space indentation on all MarshalOptions.
1957 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001958 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001959 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001960 t.Errorf("Marshal() returned error: %v\n", err)
1961 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001962 if err == nil && tt.wantErr {
1963 t.Errorf("Marshal() got nil error, want error\n")
1964 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001965 got := string(b)
1966 if got != tt.want {
1967 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1968 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
1969 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1970 }
1971 }
1972 })
1973 }
1974}