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