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