blob: 0c1286170dc01c49d0a6a16792aef2c92a0dba20 [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 }, {
1425 desc: "Duration with +secs -nanos",
1426 input: &knownpb.Duration{Seconds: 1, Nanos: -1},
1427 wantErr: true,
1428 }, {
1429 desc: "Duration with -secs +nanos",
1430 input: &knownpb.Duration{Seconds: -1, Nanos: 1},
1431 wantErr: true,
1432 }, {
1433 desc: "Duration with +secs out of range",
1434 input: &knownpb.Duration{Seconds: 315576000001},
1435 wantErr: true,
1436 }, {
1437 desc: "Duration with -secs out of range",
1438 input: &knownpb.Duration{Seconds: -315576000001},
1439 wantErr: true,
1440 }, {
1441 desc: "Duration with +nanos out of range",
1442 input: &knownpb.Duration{Seconds: 0, Nanos: 1e9},
1443 wantErr: true,
1444 }, {
1445 desc: "Duration with -nanos out of range",
1446 input: &knownpb.Duration{Seconds: 0, Nanos: -1e9},
1447 wantErr: true,
1448 }, {
1449 desc: "Timestamp zero",
1450 input: &knownpb.Timestamp{},
1451 want: `"1970-01-01T00:00:00Z"`,
1452 }, {
1453 desc: "Timestamp",
1454 input: &knownpb.Timestamp{Seconds: 1553036601},
1455 want: `"2019-03-19T23:03:21Z"`,
1456 }, {
1457 desc: "Timestamp with nanos",
1458 input: &knownpb.Timestamp{Seconds: 1553036601, Nanos: 1},
1459 want: `"2019-03-19T23:03:21.000000001Z"`,
1460 }, {
1461 desc: "Timestamp with 6-digit nanos",
1462 input: &knownpb.Timestamp{Nanos: 1e3},
1463 want: `"1970-01-01T00:00:00.000001Z"`,
1464 }, {
1465 desc: "Timestamp with 3-digit nanos",
1466 input: &knownpb.Timestamp{Nanos: 1e7},
1467 want: `"1970-01-01T00:00:00.010Z"`,
1468 }, {
1469 desc: "Timestamp with +secs out of range",
1470 input: &knownpb.Timestamp{Seconds: 253402300800},
1471 wantErr: true,
1472 }, {
1473 desc: "Timestamp with -secs out of range",
1474 input: &knownpb.Timestamp{Seconds: -62135596801},
1475 wantErr: true,
1476 }, {
1477 desc: "Timestamp with -nanos",
1478 input: &knownpb.Timestamp{Nanos: -1},
1479 wantErr: true,
1480 }, {
1481 desc: "Timestamp with +nanos out of range",
1482 input: &knownpb.Timestamp{Nanos: 1e9},
1483 wantErr: true,
1484 }, {
1485 desc: "FieldMask empty",
1486 input: &knownpb.FieldMask{},
1487 want: `""`,
1488 }, {
1489 desc: "FieldMask",
1490 input: &knownpb.FieldMask{
1491 Paths: []string{
1492 "foo",
1493 "foo_bar",
1494 "foo.bar_qux",
1495 "_foo",
1496 },
1497 },
1498 want: `"foo,fooBar,foo.barQux,Foo"`,
1499 }, {
1500 desc: "FieldMask error 1",
1501 input: &knownpb.FieldMask{
1502 Paths: []string{"foo_"},
1503 },
1504 wantErr: true,
1505 }, {
1506 desc: "FieldMask error 2",
1507 input: &knownpb.FieldMask{
1508 Paths: []string{"foo__bar"},
1509 },
1510 wantErr: true,
1511 }, {
1512 desc: "Any empty",
1513 input: &knownpb.Any{},
1514 want: `{}`,
1515 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001516 desc: "Any with non-custom message",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001517 mo: jsonpb.MarshalOptions{
1518 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1519 },
1520 input: func() proto.Message {
1521 m := &pb2.Nested{
1522 OptString: scalar.String("embedded inside Any"),
1523 OptNested: &pb2.Nested{
1524 OptString: scalar.String("inception"),
1525 },
1526 }
1527 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1528 if err != nil {
1529 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1530 }
1531 return &knownpb.Any{
1532 TypeUrl: "foo/pb2.Nested",
1533 Value: b,
1534 }
1535 }(),
1536 want: `{
1537 "@type": "foo/pb2.Nested",
1538 "optString": "embedded inside Any",
1539 "optNested": {
1540 "optString": "inception"
1541 }
1542}`,
1543 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001544 desc: "Any with empty embedded message",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001545 mo: jsonpb.MarshalOptions{
1546 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1547 },
1548 input: &knownpb.Any{TypeUrl: "foo/pb2.Nested"},
1549 want: `{
1550 "@type": "foo/pb2.Nested"
1551}`,
1552 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001553 desc: "Any without registered type",
1554 mo: jsonpb.MarshalOptions{Resolver: preg.NewTypes()},
1555 input: &knownpb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001556 wantErr: true,
1557 }, {
1558 desc: "Any with missing required error",
1559 mo: jsonpb.MarshalOptions{
1560 Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
1561 },
1562 input: func() proto.Message {
1563 m := &pb2.PartialRequired{
1564 OptString: scalar.String("embedded inside Any"),
1565 }
Damien Neil96c229a2019-04-03 12:17:24 -07001566 b, err := proto.MarshalOptions{
1567 AllowPartial: true,
1568 Deterministic: true,
1569 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001570 if err != nil {
1571 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1572 }
1573 return &knownpb.Any{
1574 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1575 Value: b,
1576 }
1577 }(),
1578 want: `{
1579 "@type": "pb2.PartialRequired",
1580 "optString": "embedded inside Any"
1581}`,
1582 wantErr: true,
1583 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001584 desc: "Any with partial required and AllowPartial",
1585 mo: jsonpb.MarshalOptions{
1586 AllowPartial: true,
1587 Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
1588 },
1589 input: func() proto.Message {
1590 m := &pb2.PartialRequired{
1591 OptString: scalar.String("embedded inside Any"),
1592 }
Damien Neil96c229a2019-04-03 12:17:24 -07001593 b, err := proto.MarshalOptions{
1594 AllowPartial: true,
1595 Deterministic: true,
1596 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001597 if err != nil {
1598 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1599 }
1600 return &knownpb.Any{
1601 TypeUrl: string(m.ProtoReflect().Type().FullName()),
1602 Value: b,
1603 }
1604 }(),
1605 want: `{
1606 "@type": "pb2.PartialRequired",
1607 "optString": "embedded inside Any"
1608}`,
1609 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001610 desc: "Any with invalid UTF8",
1611 mo: jsonpb.MarshalOptions{
1612 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1613 },
1614 input: func() proto.Message {
1615 m := &pb2.Nested{
1616 OptString: scalar.String("abc\xff"),
1617 }
1618 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1619 if err != nil {
1620 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1621 }
1622 return &knownpb.Any{
1623 TypeUrl: "foo/pb2.Nested",
1624 Value: b,
1625 }
1626 }(),
1627 want: `{
1628 "@type": "foo/pb2.Nested",
1629 "optString": "` + "abc\xff" + `"
1630}`,
1631 wantErr: true,
1632 }, {
1633 desc: "Any with invalid value",
1634 mo: jsonpb.MarshalOptions{
1635 Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
1636 },
1637 input: &knownpb.Any{
1638 TypeUrl: "foo/pb2.Nested",
1639 Value: dhex("80"),
1640 },
1641 wantErr: true,
1642 }, {
1643 desc: "Any with BoolValue",
1644 mo: jsonpb.MarshalOptions{
1645 Resolver: preg.NewTypes((&knownpb.BoolValue{}).ProtoReflect().Type()),
1646 },
1647 input: func() proto.Message {
1648 m := &knownpb.BoolValue{Value: true}
1649 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1650 if err != nil {
1651 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1652 }
1653 return &knownpb.Any{
1654 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1655 Value: b,
1656 }
1657 }(),
1658 want: `{
1659 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1660 "value": true
1661}`,
1662 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001663 desc: "Any with Empty",
1664 mo: jsonpb.MarshalOptions{
1665 Resolver: preg.NewTypes((&knownpb.Empty{}).ProtoReflect().Type()),
1666 },
1667 input: func() proto.Message {
1668 m := &knownpb.Empty{}
1669 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1670 if err != nil {
1671 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1672 }
1673 return &knownpb.Any{
1674 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1675 Value: b,
1676 }
1677 }(),
1678 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001679 "@type": "type.googleapis.com/google.protobuf.Empty",
1680 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001681}`,
1682 }, {
1683 desc: "Any with StringValue containing invalid UTF8",
1684 mo: jsonpb.MarshalOptions{
1685 Resolver: preg.NewTypes((&knownpb.StringValue{}).ProtoReflect().Type()),
1686 },
1687 input: func() proto.Message {
Damien Neilbc310b52019-04-11 11:46:55 -07001688 m := &knownpb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001689 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1690 if err != nil {
1691 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1692 }
1693 return &knownpb.Any{
1694 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001695 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001696 }
1697 }(),
1698 want: `{
1699 "@type": "google.protobuf.StringValue",
1700 "value": "` + "abc\xff" + `"
1701}`,
1702 wantErr: true,
1703 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001704 desc: "Any with Int64Value",
1705 mo: jsonpb.MarshalOptions{
1706 Resolver: preg.NewTypes((&knownpb.Int64Value{}).ProtoReflect().Type()),
1707 },
1708 input: func() proto.Message {
1709 m := &knownpb.Int64Value{Value: 42}
1710 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1711 if err != nil {
1712 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1713 }
1714 return &knownpb.Any{
1715 TypeUrl: "google.protobuf.Int64Value",
1716 Value: b,
1717 }
1718 }(),
1719 want: `{
1720 "@type": "google.protobuf.Int64Value",
1721 "value": "42"
1722}`,
1723 }, {
1724 desc: "Any with Duration",
1725 mo: jsonpb.MarshalOptions{
1726 Resolver: preg.NewTypes((&knownpb.Duration{}).ProtoReflect().Type()),
1727 },
1728 input: func() proto.Message {
1729 m := &knownpb.Duration{}
1730 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1731 if err != nil {
1732 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1733 }
1734 return &knownpb.Any{
1735 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1736 Value: b,
1737 }
1738 }(),
1739 want: `{
1740 "@type": "type.googleapis.com/google.protobuf.Duration",
1741 "value": "0s"
1742}`,
1743 }, {
1744 desc: "Any with empty Value",
1745 mo: jsonpb.MarshalOptions{
1746 Resolver: preg.NewTypes((&knownpb.Value{}).ProtoReflect().Type()),
1747 },
1748 input: func() proto.Message {
1749 m := &knownpb.Value{}
1750 b, err := proto.Marshal(m)
1751 if err != nil {
1752 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1753 }
1754 return &knownpb.Any{
1755 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1756 Value: b,
1757 }
1758 }(),
1759 wantErr: true,
1760 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001761 desc: "Any with Value of StringValue",
1762 mo: jsonpb.MarshalOptions{
1763 Resolver: preg.NewTypes((&knownpb.Value{}).ProtoReflect().Type()),
1764 },
1765 input: func() proto.Message {
Damien Neilbc310b52019-04-11 11:46:55 -07001766 m := &knownpb.Value{Kind: &knownpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001767 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1768 if err != nil {
1769 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1770 }
1771 return &knownpb.Any{
1772 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001773 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001774 }
1775 }(),
1776 want: `{
1777 "@type": "type.googleapis.com/google.protobuf.Value",
1778 "value": "` + "abc\xff" + `"
1779}`,
1780 wantErr: true,
1781 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001782 desc: "Any with Value of NullValue",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001783 mo: jsonpb.MarshalOptions{
1784 Resolver: preg.NewTypes((&knownpb.Value{}).ProtoReflect().Type()),
1785 },
1786 input: func() proto.Message {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001787 m := &knownpb.Value{Kind: &knownpb.Value_NullValue{}}
1788 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001789 if err != nil {
1790 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1791 }
1792 return &knownpb.Any{
1793 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1794 Value: b,
1795 }
1796 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001797 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001798 "@type": "type.googleapis.com/google.protobuf.Value",
1799 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001800}`,
1801 }, {
1802 desc: "Any with Struct",
1803 mo: jsonpb.MarshalOptions{
1804 Resolver: preg.NewTypes(
1805 (&knownpb.Struct{}).ProtoReflect().Type(),
1806 (&knownpb.Value{}).ProtoReflect().Type(),
1807 (&knownpb.BoolValue{}).ProtoReflect().Type(),
1808 knownpb.NullValue_NULL_VALUE.Type(),
1809 (&knownpb.StringValue{}).ProtoReflect().Type(),
1810 ),
1811 },
1812 input: func() proto.Message {
1813 m := &knownpb.Struct{
1814 Fields: map[string]*knownpb.Value{
1815 "bool": {Kind: &knownpb.Value_BoolValue{true}},
1816 "null": {Kind: &knownpb.Value_NullValue{}},
1817 "string": {Kind: &knownpb.Value_StringValue{"hello"}},
1818 "struct": {
1819 Kind: &knownpb.Value_StructValue{
1820 &knownpb.Struct{
1821 Fields: map[string]*knownpb.Value{
1822 "string": {Kind: &knownpb.Value_StringValue{"world"}},
1823 },
1824 },
1825 },
1826 },
1827 },
1828 }
1829 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1830 if err != nil {
1831 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1832 }
1833 return &knownpb.Any{
1834 TypeUrl: "google.protobuf.Struct",
1835 Value: b,
1836 }
1837 }(),
1838 want: `{
1839 "@type": "google.protobuf.Struct",
1840 "value": {
1841 "bool": true,
1842 "null": null,
1843 "string": "hello",
1844 "struct": {
1845 "string": "world"
1846 }
1847 }
1848}`,
1849 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001850 desc: "Any with missing type_url",
1851 mo: jsonpb.MarshalOptions{
1852 Resolver: preg.NewTypes((&knownpb.BoolValue{}).ProtoReflect().Type()),
1853 },
1854 input: func() proto.Message {
1855 m := &knownpb.BoolValue{Value: true}
1856 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1857 if err != nil {
1858 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1859 }
1860 return &knownpb.Any{
1861 Value: b,
1862 }
1863 }(),
1864 wantErr: true,
1865 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001866 desc: "well known types as field values",
1867 mo: jsonpb.MarshalOptions{
1868 Resolver: preg.NewTypes((&knownpb.Empty{}).ProtoReflect().Type()),
1869 },
1870 input: &pb2.KnownTypes{
1871 OptBool: &knownpb.BoolValue{Value: false},
1872 OptInt32: &knownpb.Int32Value{Value: 42},
1873 OptInt64: &knownpb.Int64Value{Value: 42},
1874 OptUint32: &knownpb.UInt32Value{Value: 42},
1875 OptUint64: &knownpb.UInt64Value{Value: 42},
1876 OptFloat: &knownpb.FloatValue{Value: 1.23},
1877 OptDouble: &knownpb.DoubleValue{Value: 3.1415},
1878 OptString: &knownpb.StringValue{Value: "hello"},
1879 OptBytes: &knownpb.BytesValue{Value: []byte("hello")},
1880 OptDuration: &knownpb.Duration{Seconds: 123},
1881 OptTimestamp: &knownpb.Timestamp{Seconds: 1553036601},
1882 OptStruct: &knownpb.Struct{
1883 Fields: map[string]*knownpb.Value{
1884 "string": {Kind: &knownpb.Value_StringValue{"hello"}},
1885 },
1886 },
1887 OptList: &knownpb.ListValue{
1888 Values: []*knownpb.Value{
1889 {Kind: &knownpb.Value_NullValue{}},
1890 {Kind: &knownpb.Value_StringValue{}},
1891 {Kind: &knownpb.Value_StructValue{}},
1892 {Kind: &knownpb.Value_ListValue{}},
1893 },
1894 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001895 OptValue: &knownpb.Value{
1896 Kind: &knownpb.Value_StringValue{"world"},
1897 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001898 OptEmpty: &knownpb.Empty{},
1899 OptAny: &knownpb.Any{
1900 TypeUrl: "google.protobuf.Empty",
1901 },
1902 OptFieldmask: &knownpb.FieldMask{
1903 Paths: []string{"foo_bar", "bar_foo"},
1904 },
1905 },
1906 want: `{
1907 "optBool": false,
1908 "optInt32": 42,
1909 "optInt64": "42",
1910 "optUint32": 42,
1911 "optUint64": "42",
1912 "optFloat": 1.23,
1913 "optDouble": 3.1415,
1914 "optString": "hello",
1915 "optBytes": "aGVsbG8=",
1916 "optDuration": "123s",
1917 "optTimestamp": "2019-03-19T23:03:21Z",
1918 "optStruct": {
1919 "string": "hello"
1920 },
1921 "optList": [
1922 null,
1923 "",
1924 {},
1925 []
1926 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001927 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001928 "optEmpty": {},
1929 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001930 "@type": "google.protobuf.Empty",
1931 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001932 },
1933 "optFieldmask": "fooBar,barFoo"
1934}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001935 }}
1936
1937 for _, tt := range tests {
1938 tt := tt
1939 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001940 // Use 2-space indentation on all MarshalOptions.
1941 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001942 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001943 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001944 t.Errorf("Marshal() returned error: %v\n", err)
1945 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001946 if err == nil && tt.wantErr {
1947 t.Errorf("Marshal() got nil error, want error\n")
1948 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001949 got := string(b)
1950 if got != tt.want {
1951 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1952 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
1953 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1954 }
1955 }
1956 })
1957 }
1958}