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