blob: 3bffa6865c14e83446a79d9287307a34f871f2ba [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
Damien Neil5c5b5312019-05-14 12:44:37 -07005package protojson_test
Herbie Ong7b828bc2019-02-08 19:56:24 -08006
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
Herbie Ong7b828bc2019-02-08 19:56:24 -080014 "github.com/google/go-cmp/cmp"
15 "github.com/google/go-cmp/cmp/cmpopts"
Damien Neil5c5b5312019-05-14 12:44:37 -070016 "google.golang.org/protobuf/encoding/protojson"
Damien Neile89e6242019-05-13 23:55:40 -070017 "google.golang.org/protobuf/internal/encoding/pack"
Damien Neile89e6242019-05-13 23:55:40 -070018 pimpl "google.golang.org/protobuf/internal/impl"
19 "google.golang.org/protobuf/internal/scalar"
20 "google.golang.org/protobuf/proto"
21 preg "google.golang.org/protobuf/reflect/protoregistry"
22 "google.golang.org/protobuf/runtime/protoiface"
Herbie Ong7b828bc2019-02-08 19:56:24 -080023
Damien Neile89e6242019-05-13 23:55:40 -070024 "google.golang.org/protobuf/encoding/testprotos/pb2"
25 "google.golang.org/protobuf/encoding/testprotos/pb3"
Joe Tsaia95b29f2019-05-16 12:47:20 -070026 "google.golang.org/protobuf/types/known/anypb"
27 "google.golang.org/protobuf/types/known/durationpb"
28 "google.golang.org/protobuf/types/known/emptypb"
29 "google.golang.org/protobuf/types/known/fieldmaskpb"
30 "google.golang.org/protobuf/types/known/structpb"
31 "google.golang.org/protobuf/types/known/timestamppb"
32 "google.golang.org/protobuf/types/known/wrapperspb"
Herbie Ong7b828bc2019-02-08 19:56:24 -080033)
34
35// splitLines is a cmpopts.Option for comparing strings with line breaks.
36var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
37 return strings.Split(s, "\n")
38})
39
40func pb2Enum(i int32) *pb2.Enum {
41 p := new(pb2.Enum)
42 *p = pb2.Enum(i)
43 return p
44}
45
46func pb2Enums_NestedEnum(i int32) *pb2.Enums_NestedEnum {
47 p := new(pb2.Enums_NestedEnum)
48 *p = pb2.Enums_NestedEnum(i)
49 return p
50}
51
Joe Tsai378c1322019-04-25 23:48:08 -070052// TODO: Replace this with proto.SetExtension.
Joe Tsai4fddeba2019-03-20 18:29:32 -070053func setExtension(m proto.Message, xd *protoiface.ExtensionDescV1, val interface{}) {
Joe Tsai378c1322019-04-25 23:48:08 -070054 m.ProtoReflect().Set(xd.Type, xd.Type.ValueOf(val))
Herbie Ongf83d5bb2019-03-14 00:01:27 -070055}
56
Herbie Ong0b0f4032019-03-18 19:06:15 -070057// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
58func dhex(s string) []byte {
59 b, err := hex.DecodeString(s)
60 if err != nil {
61 panic(err)
62 }
63 return b
64}
65
Herbie Ong7b828bc2019-02-08 19:56:24 -080066func TestMarshal(t *testing.T) {
67 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070068 desc string
Damien Neil5c5b5312019-05-14 12:44:37 -070069 mo protojson.MarshalOptions
Herbie Ong0b0f4032019-03-18 19:06:15 -070070 input proto.Message
71 want string
72 wantErr bool // TODO: Verify error message substring.
Herbie Ong7b828bc2019-02-08 19:56:24 -080073 }{{
74 desc: "proto2 optional scalars not set",
75 input: &pb2.Scalars{},
76 want: "{}",
77 }, {
78 desc: "proto3 scalars not set",
79 input: &pb3.Scalars{},
80 want: "{}",
81 }, {
82 desc: "proto2 optional scalars set to zero values",
83 input: &pb2.Scalars{
84 OptBool: scalar.Bool(false),
85 OptInt32: scalar.Int32(0),
86 OptInt64: scalar.Int64(0),
87 OptUint32: scalar.Uint32(0),
88 OptUint64: scalar.Uint64(0),
89 OptSint32: scalar.Int32(0),
90 OptSint64: scalar.Int64(0),
91 OptFixed32: scalar.Uint32(0),
92 OptFixed64: scalar.Uint64(0),
93 OptSfixed32: scalar.Int32(0),
94 OptSfixed64: scalar.Int64(0),
95 OptFloat: scalar.Float32(0),
96 OptDouble: scalar.Float64(0),
97 OptBytes: []byte{},
98 OptString: scalar.String(""),
99 },
100 want: `{
101 "optBool": false,
102 "optInt32": 0,
103 "optInt64": "0",
104 "optUint32": 0,
105 "optUint64": "0",
106 "optSint32": 0,
107 "optSint64": "0",
108 "optFixed32": 0,
109 "optFixed64": "0",
110 "optSfixed32": 0,
111 "optSfixed64": "0",
112 "optFloat": 0,
113 "optDouble": 0,
114 "optBytes": "",
115 "optString": ""
116}`,
117 }, {
118 desc: "proto2 optional scalars set to some values",
119 input: &pb2.Scalars{
120 OptBool: scalar.Bool(true),
121 OptInt32: scalar.Int32(0xff),
122 OptInt64: scalar.Int64(0xdeadbeef),
123 OptUint32: scalar.Uint32(47),
124 OptUint64: scalar.Uint64(0xdeadbeef),
125 OptSint32: scalar.Int32(-1001),
126 OptSint64: scalar.Int64(-0xffff),
127 OptFixed64: scalar.Uint64(64),
128 OptSfixed32: scalar.Int32(-32),
129 OptFloat: scalar.Float32(1.02),
130 OptDouble: scalar.Float64(1.234),
Herbie Ong87608a72019-03-06 14:32:24 -0800131 OptBytes: []byte("谷歌"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800132 OptString: scalar.String("谷歌"),
133 },
134 want: `{
135 "optBool": true,
136 "optInt32": 255,
137 "optInt64": "3735928559",
138 "optUint32": 47,
139 "optUint64": "3735928559",
140 "optSint32": -1001,
141 "optSint64": "-65535",
142 "optFixed64": "64",
143 "optSfixed32": -32,
144 "optFloat": 1.02,
145 "optDouble": 1.234,
146 "optBytes": "6LC35q2M",
147 "optString": "谷歌"
148}`,
149 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700150 desc: "string",
151 input: &pb3.Scalars{
152 SString: "谷歌",
153 },
154 want: `{
155 "sString": "谷歌"
156}`,
157 }, {
158 desc: "string with invalid UTF8",
159 input: &pb3.Scalars{
160 SString: "abc\xff",
161 },
Herbie Ong0b0f4032019-03-18 19:06:15 -0700162 wantErr: true,
163 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800164 desc: "float nan",
165 input: &pb3.Scalars{
166 SFloat: float32(math.NaN()),
167 },
168 want: `{
169 "sFloat": "NaN"
170}`,
171 }, {
172 desc: "float positive infinity",
173 input: &pb3.Scalars{
174 SFloat: float32(math.Inf(1)),
175 },
176 want: `{
177 "sFloat": "Infinity"
178}`,
179 }, {
180 desc: "float negative infinity",
181 input: &pb3.Scalars{
182 SFloat: float32(math.Inf(-1)),
183 },
184 want: `{
185 "sFloat": "-Infinity"
186}`,
187 }, {
188 desc: "double nan",
189 input: &pb3.Scalars{
190 SDouble: math.NaN(),
191 },
192 want: `{
193 "sDouble": "NaN"
194}`,
195 }, {
196 desc: "double positive infinity",
197 input: &pb3.Scalars{
198 SDouble: math.Inf(1),
199 },
200 want: `{
201 "sDouble": "Infinity"
202}`,
203 }, {
204 desc: "double negative infinity",
205 input: &pb3.Scalars{
206 SDouble: math.Inf(-1),
207 },
208 want: `{
209 "sDouble": "-Infinity"
210}`,
211 }, {
212 desc: "proto2 enum not set",
213 input: &pb2.Enums{},
214 want: "{}",
215 }, {
216 desc: "proto2 enum set to zero value",
217 input: &pb2.Enums{
218 OptEnum: pb2Enum(0),
219 OptNestedEnum: pb2Enums_NestedEnum(0),
220 },
221 want: `{
222 "optEnum": 0,
223 "optNestedEnum": 0
224}`,
225 }, {
226 desc: "proto2 enum",
227 input: &pb2.Enums{
228 OptEnum: pb2.Enum_ONE.Enum(),
229 OptNestedEnum: pb2.Enums_UNO.Enum(),
230 },
231 want: `{
232 "optEnum": "ONE",
233 "optNestedEnum": "UNO"
234}`,
235 }, {
236 desc: "proto2 enum set to numeric values",
237 input: &pb2.Enums{
238 OptEnum: pb2Enum(2),
239 OptNestedEnum: pb2Enums_NestedEnum(2),
240 },
241 want: `{
242 "optEnum": "TWO",
243 "optNestedEnum": "DOS"
244}`,
245 }, {
246 desc: "proto2 enum set to unnamed numeric values",
247 input: &pb2.Enums{
248 OptEnum: pb2Enum(101),
249 OptNestedEnum: pb2Enums_NestedEnum(-101),
250 },
251 want: `{
252 "optEnum": 101,
253 "optNestedEnum": -101
254}`,
255 }, {
256 desc: "proto3 enum not set",
257 input: &pb3.Enums{},
258 want: "{}",
259 }, {
260 desc: "proto3 enum set to zero value",
261 input: &pb3.Enums{
262 SEnum: pb3.Enum_ZERO,
263 SNestedEnum: pb3.Enums_CERO,
264 },
265 want: "{}",
266 }, {
267 desc: "proto3 enum",
268 input: &pb3.Enums{
269 SEnum: pb3.Enum_ONE,
270 SNestedEnum: pb3.Enums_UNO,
271 },
272 want: `{
273 "sEnum": "ONE",
274 "sNestedEnum": "UNO"
275}`,
276 }, {
277 desc: "proto3 enum set to numeric values",
278 input: &pb3.Enums{
279 SEnum: 2,
280 SNestedEnum: 2,
281 },
282 want: `{
283 "sEnum": "TWO",
284 "sNestedEnum": "DOS"
285}`,
286 }, {
287 desc: "proto3 enum set to unnamed numeric values",
288 input: &pb3.Enums{
289 SEnum: -47,
290 SNestedEnum: 47,
291 },
292 want: `{
293 "sEnum": -47,
294 "sNestedEnum": 47
295}`,
296 }, {
297 desc: "proto2 nested message not set",
298 input: &pb2.Nests{},
299 want: "{}",
300 }, {
301 desc: "proto2 nested message set to empty",
302 input: &pb2.Nests{
303 OptNested: &pb2.Nested{},
304 Optgroup: &pb2.Nests_OptGroup{},
305 },
306 want: `{
307 "optNested": {},
308 "optgroup": {}
309}`,
310 }, {
311 desc: "proto2 nested messages",
312 input: &pb2.Nests{
313 OptNested: &pb2.Nested{
314 OptString: scalar.String("nested message"),
315 OptNested: &pb2.Nested{
316 OptString: scalar.String("another nested message"),
317 },
318 },
319 },
320 want: `{
321 "optNested": {
322 "optString": "nested message",
323 "optNested": {
324 "optString": "another nested message"
325 }
326 }
327}`,
328 }, {
329 desc: "proto2 groups",
330 input: &pb2.Nests{
331 Optgroup: &pb2.Nests_OptGroup{
332 OptString: scalar.String("inside a group"),
333 OptNested: &pb2.Nested{
334 OptString: scalar.String("nested message inside a group"),
335 },
336 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
337 OptFixed32: scalar.Uint32(47),
338 },
339 },
340 },
341 want: `{
342 "optgroup": {
343 "optString": "inside a group",
344 "optNested": {
345 "optString": "nested message inside a group"
346 },
347 "optnestedgroup": {
348 "optFixed32": 47
349 }
350 }
351}`,
352 }, {
353 desc: "proto3 nested message not set",
354 input: &pb3.Nests{},
355 want: "{}",
356 }, {
357 desc: "proto3 nested message set to empty",
358 input: &pb3.Nests{
359 SNested: &pb3.Nested{},
360 },
361 want: `{
362 "sNested": {}
363}`,
364 }, {
365 desc: "proto3 nested message",
366 input: &pb3.Nests{
367 SNested: &pb3.Nested{
368 SString: "nested message",
369 SNested: &pb3.Nested{
370 SString: "another nested message",
371 },
372 },
373 },
374 want: `{
375 "sNested": {
376 "sString": "nested message",
377 "sNested": {
378 "sString": "another nested message"
379 }
380 }
381}`,
382 }, {
383 desc: "oneof not set",
384 input: &pb3.Oneofs{},
385 want: "{}",
386 }, {
387 desc: "oneof set to empty string",
388 input: &pb3.Oneofs{
389 Union: &pb3.Oneofs_OneofString{},
390 },
391 want: `{
392 "oneofString": ""
393}`,
394 }, {
395 desc: "oneof set to string",
396 input: &pb3.Oneofs{
397 Union: &pb3.Oneofs_OneofString{
398 OneofString: "hello",
399 },
400 },
401 want: `{
402 "oneofString": "hello"
403}`,
404 }, {
405 desc: "oneof set to enum",
406 input: &pb3.Oneofs{
407 Union: &pb3.Oneofs_OneofEnum{
408 OneofEnum: pb3.Enum_ZERO,
409 },
410 },
411 want: `{
412 "oneofEnum": "ZERO"
413}`,
414 }, {
415 desc: "oneof set to empty message",
416 input: &pb3.Oneofs{
417 Union: &pb3.Oneofs_OneofNested{
418 OneofNested: &pb3.Nested{},
419 },
420 },
421 want: `{
422 "oneofNested": {}
423}`,
424 }, {
425 desc: "oneof set to message",
426 input: &pb3.Oneofs{
427 Union: &pb3.Oneofs_OneofNested{
428 OneofNested: &pb3.Nested{
429 SString: "nested message",
430 },
431 },
432 },
433 want: `{
434 "oneofNested": {
435 "sString": "nested message"
436 }
437}`,
438 }, {
439 desc: "repeated fields not set",
440 input: &pb2.Repeats{},
441 want: "{}",
442 }, {
443 desc: "repeated fields set to empty slices",
444 input: &pb2.Repeats{
445 RptBool: []bool{},
446 RptInt32: []int32{},
447 RptInt64: []int64{},
448 RptUint32: []uint32{},
449 RptUint64: []uint64{},
450 RptFloat: []float32{},
451 RptDouble: []float64{},
452 RptBytes: [][]byte{},
453 },
454 want: "{}",
455 }, {
456 desc: "repeated fields set to some values",
457 input: &pb2.Repeats{
458 RptBool: []bool{true, false, true, true},
459 RptInt32: []int32{1, 6, 0, 0},
460 RptInt64: []int64{-64, 47},
461 RptUint32: []uint32{0xff, 0xffff},
462 RptUint64: []uint64{0xdeadbeef},
463 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
464 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
465 RptString: []string{"hello", "世界"},
466 RptBytes: [][]byte{
467 []byte("hello"),
468 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
469 },
470 },
471 want: `{
472 "rptBool": [
473 true,
474 false,
475 true,
476 true
477 ],
478 "rptInt32": [
479 1,
480 6,
481 0,
482 0
483 ],
484 "rptInt64": [
485 "-64",
486 "47"
487 ],
488 "rptUint32": [
489 255,
490 65535
491 ],
492 "rptUint64": [
493 "3735928559"
494 ],
495 "rptFloat": [
496 "NaN",
497 "Infinity",
498 "-Infinity",
499 1.034
500 ],
501 "rptDouble": [
502 "NaN",
503 "Infinity",
504 "-Infinity",
505 1.23e-308
506 ],
507 "rptString": [
508 "hello",
509 "世界"
510 ],
511 "rptBytes": [
512 "aGVsbG8=",
513 "5LiW55WM"
514 ]
515}`,
516 }, {
517 desc: "repeated enums",
518 input: &pb2.Enums{
519 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
520 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
521 },
522 want: `{
523 "rptEnum": [
524 "ONE",
525 "TWO",
526 "TEN",
527 42
528 ],
529 "rptNestedEnum": [
530 "DOS",
531 47,
532 "DIEZ"
533 ]
534}`,
535 }, {
536 desc: "repeated messages set to empty",
537 input: &pb2.Nests{
538 RptNested: []*pb2.Nested{},
539 Rptgroup: []*pb2.Nests_RptGroup{},
540 },
541 want: "{}",
542 }, {
543 desc: "repeated messages",
544 input: &pb2.Nests{
545 RptNested: []*pb2.Nested{
546 {
547 OptString: scalar.String("repeat nested one"),
548 },
549 {
550 OptString: scalar.String("repeat nested two"),
551 OptNested: &pb2.Nested{
552 OptString: scalar.String("inside repeat nested two"),
553 },
554 },
555 {},
556 },
557 },
558 want: `{
559 "rptNested": [
560 {
561 "optString": "repeat nested one"
562 },
563 {
564 "optString": "repeat nested two",
565 "optNested": {
566 "optString": "inside repeat nested two"
567 }
568 },
569 {}
570 ]
571}`,
572 }, {
573 desc: "repeated messages contains nil value",
574 input: &pb2.Nests{
575 RptNested: []*pb2.Nested{nil, {}},
576 },
577 want: `{
578 "rptNested": [
579 {},
580 {}
581 ]
582}`,
583 }, {
584 desc: "repeated groups",
585 input: &pb2.Nests{
586 Rptgroup: []*pb2.Nests_RptGroup{
587 {
588 RptString: []string{"hello", "world"},
589 },
590 {},
591 nil,
592 },
593 },
594 want: `{
595 "rptgroup": [
596 {
597 "rptString": [
598 "hello",
599 "world"
600 ]
601 },
602 {},
603 {}
604 ]
605}`,
606 }, {
607 desc: "map fields not set",
608 input: &pb3.Maps{},
609 want: "{}",
610 }, {
611 desc: "map fields set to empty",
612 input: &pb3.Maps{
613 Int32ToStr: map[int32]string{},
614 BoolToUint32: map[bool]uint32{},
615 Uint64ToEnum: map[uint64]pb3.Enum{},
616 StrToNested: map[string]*pb3.Nested{},
617 StrToOneofs: map[string]*pb3.Oneofs{},
618 },
619 want: "{}",
620 }, {
621 desc: "map fields 1",
622 input: &pb3.Maps{
623 BoolToUint32: map[bool]uint32{
624 true: 42,
625 false: 101,
626 },
627 },
628 want: `{
629 "boolToUint32": {
630 "false": 101,
631 "true": 42
632 }
633}`,
634 }, {
635 desc: "map fields 2",
636 input: &pb3.Maps{
637 Int32ToStr: map[int32]string{
638 -101: "-101",
639 0xff: "0xff",
640 0: "zero",
641 },
642 },
643 want: `{
644 "int32ToStr": {
645 "-101": "-101",
646 "0": "zero",
647 "255": "0xff"
648 }
649}`,
650 }, {
651 desc: "map fields 3",
652 input: &pb3.Maps{
653 Uint64ToEnum: map[uint64]pb3.Enum{
654 1: pb3.Enum_ONE,
655 2: pb3.Enum_TWO,
656 10: pb3.Enum_TEN,
657 47: 47,
658 },
659 },
660 want: `{
661 "uint64ToEnum": {
662 "1": "ONE",
663 "2": "TWO",
664 "10": "TEN",
665 "47": 47
666 }
667}`,
668 }, {
669 desc: "map fields 4",
670 input: &pb3.Maps{
671 StrToNested: map[string]*pb3.Nested{
672 "nested": &pb3.Nested{
673 SString: "nested in a map",
674 },
675 },
676 },
677 want: `{
678 "strToNested": {
679 "nested": {
680 "sString": "nested in a map"
681 }
682 }
683}`,
684 }, {
685 desc: "map fields 5",
686 input: &pb3.Maps{
687 StrToOneofs: map[string]*pb3.Oneofs{
688 "string": &pb3.Oneofs{
689 Union: &pb3.Oneofs_OneofString{
690 OneofString: "hello",
691 },
692 },
693 "nested": &pb3.Oneofs{
694 Union: &pb3.Oneofs_OneofNested{
695 OneofNested: &pb3.Nested{
696 SString: "nested oneof in map field value",
697 },
698 },
699 },
700 },
701 },
702 want: `{
703 "strToOneofs": {
704 "nested": {
705 "oneofNested": {
706 "sString": "nested oneof in map field value"
707 }
708 },
709 "string": {
710 "oneofString": "hello"
711 }
712 }
713}`,
714 }, {
715 desc: "map field contains nil value",
716 input: &pb3.Maps{
717 StrToNested: map[string]*pb3.Nested{
718 "nil": nil,
719 },
720 },
721 want: `{
722 "strToNested": {
723 "nil": {}
724 }
725}`,
726 }, {
Herbie Ong329be5b2019-03-27 14:47:59 -0700727 desc: "required fields not set",
728 input: &pb2.Requireds{},
729 want: `{}`,
730 wantErr: true,
731 }, {
732 desc: "required fields partially set",
733 input: &pb2.Requireds{
734 ReqBool: scalar.Bool(false),
735 ReqSfixed64: scalar.Int64(0),
736 ReqDouble: scalar.Float64(1.23),
737 ReqString: scalar.String("hello"),
738 ReqEnum: pb2.Enum_ONE.Enum(),
739 },
740 want: `{
741 "reqBool": false,
742 "reqSfixed64": "0",
743 "reqDouble": 1.23,
744 "reqString": "hello",
745 "reqEnum": "ONE"
746}`,
747 wantErr: true,
748 }, {
749 desc: "required fields not set with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700750 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700751 input: &pb2.Requireds{
752 ReqBool: scalar.Bool(false),
753 ReqSfixed64: scalar.Int64(0),
754 ReqDouble: scalar.Float64(1.23),
755 ReqString: scalar.String("hello"),
756 ReqEnum: pb2.Enum_ONE.Enum(),
757 },
758 want: `{
759 "reqBool": false,
760 "reqSfixed64": "0",
761 "reqDouble": 1.23,
762 "reqString": "hello",
763 "reqEnum": "ONE"
764}`,
765 }, {
766 desc: "required fields all set",
767 input: &pb2.Requireds{
768 ReqBool: scalar.Bool(false),
769 ReqSfixed64: scalar.Int64(0),
770 ReqDouble: scalar.Float64(1.23),
771 ReqString: scalar.String("hello"),
772 ReqEnum: pb2.Enum_ONE.Enum(),
773 ReqNested: &pb2.Nested{},
774 },
775 want: `{
776 "reqBool": false,
777 "reqSfixed64": "0",
778 "reqDouble": 1.23,
779 "reqString": "hello",
780 "reqEnum": "ONE",
781 "reqNested": {}
782}`,
783 }, {
784 desc: "indirect required field",
785 input: &pb2.IndirectRequired{
786 OptNested: &pb2.NestedWithRequired{},
787 },
788 want: `{
789 "optNested": {}
790}`,
791 wantErr: true,
792 }, {
793 desc: "indirect required field with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700794 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700795 input: &pb2.IndirectRequired{
796 OptNested: &pb2.NestedWithRequired{},
797 },
798 want: `{
799 "optNested": {}
800}`,
801 }, {
802 desc: "indirect required field in empty repeated",
803 input: &pb2.IndirectRequired{
804 RptNested: []*pb2.NestedWithRequired{},
805 },
806 want: `{}`,
807 }, {
808 desc: "indirect required field in repeated",
809 input: &pb2.IndirectRequired{
810 RptNested: []*pb2.NestedWithRequired{
811 &pb2.NestedWithRequired{},
812 },
813 },
814 want: `{
815 "rptNested": [
816 {}
817 ]
818}`,
819 wantErr: true,
820 }, {
821 desc: "indirect required field in repeated with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700822 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700823 input: &pb2.IndirectRequired{
824 RptNested: []*pb2.NestedWithRequired{
825 &pb2.NestedWithRequired{},
826 },
827 },
828 want: `{
829 "rptNested": [
830 {}
831 ]
832}`,
833 }, {
834 desc: "indirect required field in empty map",
835 input: &pb2.IndirectRequired{
836 StrToNested: map[string]*pb2.NestedWithRequired{},
837 },
838 want: "{}",
839 }, {
840 desc: "indirect required field in map",
841 input: &pb2.IndirectRequired{
842 StrToNested: map[string]*pb2.NestedWithRequired{
843 "fail": &pb2.NestedWithRequired{},
844 },
845 },
846 want: `{
847 "strToNested": {
848 "fail": {}
849 }
850}`,
851 wantErr: true,
852 }, {
853 desc: "indirect required field in map with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700854 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700855 input: &pb2.IndirectRequired{
856 StrToNested: map[string]*pb2.NestedWithRequired{
857 "fail": &pb2.NestedWithRequired{},
858 },
859 },
860 want: `{
861 "strToNested": {
862 "fail": {}
863 }
864}`,
865 }, {
866 desc: "indirect required field in oneof",
867 input: &pb2.IndirectRequired{
868 Union: &pb2.IndirectRequired_OneofNested{
869 OneofNested: &pb2.NestedWithRequired{},
870 },
871 },
872 want: `{
873 "oneofNested": {}
874}`,
875 wantErr: true,
876 }, {
877 desc: "indirect required field in oneof with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700878 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700879 input: &pb2.IndirectRequired{
880 Union: &pb2.IndirectRequired_OneofNested{
881 OneofNested: &pb2.NestedWithRequired{},
882 },
883 },
884 want: `{
885 "oneofNested": {}
886}`,
887 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800888 desc: "unknown fields are ignored",
Joe Tsai28216c72019-06-22 13:20:09 -0700889 input: func() proto.Message {
890 m := &pb2.Scalars{
891 OptString: scalar.String("no unknowns"),
892 }
893 m.ProtoReflect().SetUnknown(pack.Message{
Herbie Ong7b828bc2019-02-08 19:56:24 -0800894 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
Joe Tsai28216c72019-06-22 13:20:09 -0700895 }.Marshal())
896 return m
897 }(),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800898 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 }, {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700943 desc: "extensions of repeated fields",
944 input: func() proto.Message {
945 m := &pb2.Extensions{}
946 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
947 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
948 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
949 &pb2.Nested{OptString: scalar.String("one")},
950 &pb2.Nested{OptString: scalar.String("two")},
951 &pb2.Nested{OptString: scalar.String("three")},
952 })
953 return m
954 }(),
955 want: `{
956 "[pb2.rpt_ext_enum]": [
957 "TEN",
958 101,
959 "ONE"
960 ],
961 "[pb2.rpt_ext_fixed32]": [
962 42,
963 47
964 ],
965 "[pb2.rpt_ext_nested]": [
966 {
967 "optString": "one"
968 },
969 {
970 "optString": "two"
971 },
972 {
973 "optString": "three"
974 }
975 ]
976}`,
977 }, {
978 desc: "extensions of non-repeated fields in another message",
979 input: func() proto.Message {
980 m := &pb2.Extensions{}
981 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
982 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
983 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
984 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
985 OptString: scalar.String("nested in an extension"),
986 OptNested: &pb2.Nested{
987 OptString: scalar.String("another nested in an extension"),
988 },
989 })
990 return m
991 }(),
992 want: `{
993 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
994 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
995 "[pb2.ExtensionsContainer.opt_ext_nested]": {
996 "optString": "nested in an extension",
997 "optNested": {
998 "optString": "another nested in an extension"
999 }
1000 },
1001 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
1002}`,
1003 }, {
1004 desc: "extensions of repeated fields in another message",
1005 input: func() proto.Message {
1006 m := &pb2.Extensions{
1007 OptString: scalar.String("non-extension field"),
1008 OptBool: scalar.Bool(true),
1009 OptInt32: scalar.Int32(42),
1010 }
1011 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1012 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
1013 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
1014 &pb2.Nested{OptString: scalar.String("one")},
1015 &pb2.Nested{OptString: scalar.String("two")},
1016 &pb2.Nested{OptString: scalar.String("three")},
1017 })
1018 return m
1019 }(),
1020 want: `{
1021 "optString": "non-extension field",
1022 "optBool": true,
1023 "optInt32": 42,
1024 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
1025 "TEN",
1026 101,
1027 "ONE"
1028 ],
1029 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1030 {
1031 "optString": "one"
1032 },
1033 {
1034 "optString": "two"
1035 },
1036 {
1037 "optString": "three"
1038 }
1039 ],
1040 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1041 "hello",
1042 "world"
1043 ]
1044}`,
1045 }, {
1046 desc: "MessageSet",
1047 input: func() proto.Message {
1048 m := &pb2.MessageSet{}
1049 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1050 OptString: scalar.String("a messageset extension"),
1051 })
1052 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1053 OptString: scalar.String("not a messageset extension"),
1054 })
1055 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1056 OptString: scalar.String("just a regular extension"),
1057 })
1058 return m
1059 }(),
1060 want: `{
1061 "[pb2.MessageSetExtension]": {
1062 "optString": "a messageset extension"
1063 },
1064 "[pb2.MessageSetExtension.ext_nested]": {
1065 "optString": "just a regular extension"
1066 },
1067 "[pb2.MessageSetExtension.not_message_set_extension]": {
1068 "optString": "not a messageset extension"
1069 }
1070}`,
1071 }, {
1072 desc: "not real MessageSet 1",
1073 input: func() proto.Message {
1074 m := &pb2.FakeMessageSet{}
1075 setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1076 OptString: scalar.String("not a messageset extension"),
1077 })
1078 return m
1079 }(),
1080 want: `{
1081 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1082 "optString": "not a messageset extension"
1083 }
1084}`,
1085 }, {
1086 desc: "not real MessageSet 2",
1087 input: func() proto.Message {
1088 m := &pb2.MessageSet{}
1089 setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1090 OptString: scalar.String("another not a messageset extension"),
1091 })
1092 return m
1093 }(),
1094 want: `{
1095 "[pb2.message_set_extension]": {
1096 "optString": "another not a messageset extension"
1097 }
1098}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001099 }, {
1100 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001101 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001102 want: `false`,
1103 }, {
1104 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001105 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001106 want: `true`,
1107 }, {
1108 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001109 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001110 want: `0`,
1111 }, {
1112 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001113 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001114 want: `42`,
1115 }, {
1116 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001117 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001118 want: `"42"`,
1119 }, {
1120 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001121 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001122 want: `42`,
1123 }, {
1124 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001125 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001126 want: `"42"`,
1127 }, {
1128 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001129 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001130 want: `1.02`,
1131 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001132 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001133 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001134 want: `"-Infinity"`,
1135 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001136 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001137 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001138 want: `1.02`,
1139 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001140 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001141 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001142 want: `"NaN"`,
1143 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001144 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001145 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001146 want: `""`,
1147 }, {
1148 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001149 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001150 want: `"谷歌"`,
1151 }, {
1152 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001153 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001154 wantErr: true,
1155 }, {
1156 desc: "StringValue field with invalid UTF8 error",
1157 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001158 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001159 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001160 wantErr: true,
1161 }, {
1162 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001163 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001164 want: `"aGVsbG8="`,
1165 }, {
1166 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001167 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001168 want: `{}`,
1169 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001170 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001171 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001172 want: `{
1173 "optNull": null
1174}`,
1175 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001176 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001177 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001178 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001179 }, {
1180 desc: "Value empty field",
1181 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001182 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001183 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001184 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001185 }, {
1186 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001187 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001188 want: `null`,
1189 }, {
1190 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001191 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001192 want: `false`,
1193 }, {
1194 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001195 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001196 want: `1.02`,
1197 }, {
1198 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001199 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001200 want: `"hello"`,
1201 }, {
1202 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001203 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001204 wantErr: true,
1205 }, {
1206 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001207 input: &structpb.Value{
1208 Kind: &structpb.Value_StructValue{
1209 &structpb.Struct{
1210 Fields: map[string]*structpb.Value{
1211 "null": {Kind: &structpb.Value_NullValue{}},
1212 "number": {Kind: &structpb.Value_NumberValue{}},
1213 "string": {Kind: &structpb.Value_StringValue{}},
1214 "struct": {Kind: &structpb.Value_StructValue{}},
1215 "list": {Kind: &structpb.Value_ListValue{}},
1216 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001217 },
1218 },
1219 },
1220 },
1221 want: `{
1222 "bool": false,
1223 "list": [],
1224 "null": null,
1225 "number": 0,
1226 "string": "",
1227 "struct": {}
1228}`,
1229 }, {
1230 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001231 input: &structpb.Value{
1232 Kind: &structpb.Value_ListValue{
1233 &structpb.ListValue{
1234 Values: []*structpb.Value{
1235 {Kind: &structpb.Value_BoolValue{}},
1236 {Kind: &structpb.Value_NullValue{}},
1237 {Kind: &structpb.Value_NumberValue{}},
1238 {Kind: &structpb.Value_StringValue{}},
1239 {Kind: &structpb.Value_StructValue{}},
1240 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001241 },
1242 },
1243 },
1244 },
1245 want: `[
1246 false,
1247 null,
1248 0,
1249 "",
1250 {},
1251 []
1252]`,
1253 }, {
1254 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001255 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001256 want: `{}`,
1257 }, {
1258 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001259 input: &structpb.Struct{
1260 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001261 },
1262 want: `{}`,
1263 }, {
1264 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001265 input: &structpb.Struct{
1266 Fields: map[string]*structpb.Value{
1267 "bool": {Kind: &structpb.Value_BoolValue{true}},
1268 "null": {Kind: &structpb.Value_NullValue{}},
1269 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1270 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001271 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001272 Kind: &structpb.Value_StructValue{
1273 &structpb.Struct{
1274 Fields: map[string]*structpb.Value{
1275 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001276 },
1277 },
1278 },
1279 },
1280 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001281 Kind: &structpb.Value_ListValue{
1282 &structpb.ListValue{
1283 Values: []*structpb.Value{
1284 {Kind: &structpb.Value_BoolValue{}},
1285 {Kind: &structpb.Value_NullValue{}},
1286 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001287 },
1288 },
1289 },
1290 },
1291 },
1292 },
1293 want: `{
1294 "bool": true,
1295 "list": [
1296 false,
1297 null,
1298 0
1299 ],
1300 "null": null,
1301 "number": 3.1415,
1302 "string": "hello",
1303 "struct": {
1304 "string": "world"
1305 }
1306}`,
1307 }, {
1308 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001309 input: &structpb.Struct{
1310 Fields: map[string]*structpb.Value{
1311 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001312 },
1313 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001314 wantErr: true,
1315 }, {
1316 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001317 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001318 want: `[]`,
1319 }, {
1320 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001321 input: &structpb.ListValue{
1322 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001323 },
1324 want: `[]`,
1325 }, {
1326 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001327 input: &structpb.ListValue{
1328 Values: []*structpb.Value{
1329 {Kind: &structpb.Value_BoolValue{true}},
1330 {Kind: &structpb.Value_NullValue{}},
1331 {Kind: &structpb.Value_NumberValue{3.1415}},
1332 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001333 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001334 Kind: &structpb.Value_ListValue{
1335 &structpb.ListValue{
1336 Values: []*structpb.Value{
1337 {Kind: &structpb.Value_BoolValue{}},
1338 {Kind: &structpb.Value_NullValue{}},
1339 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001340 },
1341 },
1342 },
1343 },
1344 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001345 Kind: &structpb.Value_StructValue{
1346 &structpb.Struct{
1347 Fields: map[string]*structpb.Value{
1348 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001349 },
1350 },
1351 },
1352 },
1353 },
1354 },
1355 want: `[
1356 true,
1357 null,
1358 3.1415,
1359 "hello",
1360 [
1361 false,
1362 null,
1363 0
1364 ],
1365 {
1366 "string": "world"
1367 }
1368]`,
1369 }, {
1370 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001371 input: &structpb.ListValue{
1372 Values: []*structpb.Value{
1373 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001374 },
1375 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001376 wantErr: true,
1377 }, {
1378 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001379 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001380 want: `"0s"`,
1381 }, {
1382 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001383 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001384 want: `"3s"`,
1385 }, {
1386 desc: "Duration with -secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001387 input: &durationpb.Duration{Seconds: -3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001388 want: `"-3s"`,
1389 }, {
1390 desc: "Duration with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001391 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001392 want: `"0.001s"`,
1393 }, {
1394 desc: "Duration with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001395 input: &durationpb.Duration{Nanos: -1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001396 want: `"-0.001s"`,
1397 }, {
1398 desc: "Duration with large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001399 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001400 want: `"10000000000.000000001s"`,
1401 }, {
1402 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001403 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001404 want: `"0.000010s"`,
1405 }, {
1406 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001407 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001408 want: `"0.001s"`,
1409 }, {
1410 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001411 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001412 want: `"-123.000000450s"`,
1413 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001414 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001415 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001416 want: `"315576000000.999999999s"`,
1417 }, {
1418 desc: "Duration min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001419 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001420 want: `"-315576000000.999999999s"`,
1421 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001422 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001423 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001424 wantErr: true,
1425 }, {
1426 desc: "Duration with -secs +nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001427 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001428 wantErr: true,
1429 }, {
1430 desc: "Duration with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001431 input: &durationpb.Duration{Seconds: 315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001432 wantErr: true,
1433 }, {
1434 desc: "Duration with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001435 input: &durationpb.Duration{Seconds: -315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001436 wantErr: true,
1437 }, {
1438 desc: "Duration with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001439 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001440 wantErr: true,
1441 }, {
1442 desc: "Duration with -nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001443 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001444 wantErr: true,
1445 }, {
1446 desc: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001447 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001448 want: `"1970-01-01T00:00:00Z"`,
1449 }, {
1450 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001451 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001452 want: `"2019-03-19T23:03:21Z"`,
1453 }, {
1454 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001455 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001456 want: `"2019-03-19T23:03:21.000000001Z"`,
1457 }, {
1458 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001459 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001460 want: `"1970-01-01T00:00:00.000001Z"`,
1461 }, {
1462 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001463 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001464 want: `"1970-01-01T00:00:00.010Z"`,
1465 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001466 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001467 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001468 want: `"9999-12-31T23:59:59.999999999Z"`,
1469 }, {
1470 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001471 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001472 want: `"0001-01-01T00:00:00Z"`,
1473 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001474 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001475 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001476 wantErr: true,
1477 }, {
1478 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001479 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001480 wantErr: true,
1481 }, {
1482 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001483 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001484 wantErr: true,
1485 }, {
1486 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001487 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001488 wantErr: true,
1489 }, {
1490 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001491 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001492 want: `""`,
1493 }, {
1494 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001495 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001496 Paths: []string{
1497 "foo",
1498 "foo_bar",
1499 "foo.bar_qux",
1500 "_foo",
1501 },
1502 },
1503 want: `"foo,fooBar,foo.barQux,Foo"`,
1504 }, {
1505 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001506 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001507 Paths: []string{"foo_"},
1508 },
1509 wantErr: true,
1510 }, {
1511 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001512 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001513 Paths: []string{"foo__bar"},
1514 },
1515 wantErr: true,
1516 }, {
1517 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001518 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001519 want: `{}`,
1520 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001521 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001522 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001523 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001524 },
1525 input: func() proto.Message {
1526 m := &pb2.Nested{
1527 OptString: scalar.String("embedded inside Any"),
1528 OptNested: &pb2.Nested{
1529 OptString: scalar.String("inception"),
1530 },
1531 }
1532 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1533 if err != nil {
1534 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1535 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001536 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001537 TypeUrl: "foo/pb2.Nested",
1538 Value: b,
1539 }
1540 }(),
1541 want: `{
1542 "@type": "foo/pb2.Nested",
1543 "optString": "embedded inside Any",
1544 "optNested": {
1545 "optString": "inception"
1546 }
1547}`,
1548 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001549 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001550 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001551 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001552 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001553 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001554 want: `{
1555 "@type": "foo/pb2.Nested"
1556}`,
1557 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001558 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001559 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001560 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001561 wantErr: true,
1562 }, {
Damien Neil0c9f0a92019-06-19 10:41:09 -07001563 desc: "Any with missing required",
Damien Neil5c5b5312019-05-14 12:44:37 -07001564 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001565 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001566 },
1567 input: func() proto.Message {
1568 m := &pb2.PartialRequired{
1569 OptString: scalar.String("embedded inside Any"),
1570 }
Damien Neil96c229a2019-04-03 12:17:24 -07001571 b, err := proto.MarshalOptions{
1572 AllowPartial: true,
1573 Deterministic: true,
1574 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001575 if err != nil {
1576 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1577 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001578 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001579 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001580 Value: b,
1581 }
1582 }(),
1583 want: `{
1584 "@type": "pb2.PartialRequired",
1585 "optString": "embedded inside Any"
1586}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001587 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001588 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001589 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001590 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001591 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001592 },
1593 input: func() proto.Message {
1594 m := &pb2.PartialRequired{
1595 OptString: scalar.String("embedded inside Any"),
1596 }
Damien Neil96c229a2019-04-03 12:17:24 -07001597 b, err := proto.MarshalOptions{
1598 AllowPartial: true,
1599 Deterministic: true,
1600 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001601 if err != nil {
1602 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1603 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001604 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001605 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001606 Value: b,
1607 }
1608 }(),
1609 want: `{
1610 "@type": "pb2.PartialRequired",
1611 "optString": "embedded inside Any"
1612}`,
1613 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001614 desc: "Any with invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001615 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001616 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001617 },
1618 input: func() proto.Message {
1619 m := &pb2.Nested{
1620 OptString: scalar.String("abc\xff"),
1621 }
1622 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1623 if err != nil {
1624 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1625 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001626 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001627 TypeUrl: "foo/pb2.Nested",
1628 Value: b,
1629 }
1630 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001631 wantErr: true,
1632 }, {
1633 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001634 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001635 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001636 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001637 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001638 TypeUrl: "foo/pb2.Nested",
1639 Value: dhex("80"),
1640 },
1641 wantErr: true,
1642 }, {
1643 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001644 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001645 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001646 },
1647 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001648 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001649 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1650 if err != nil {
1651 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1652 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001653 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001654 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1655 Value: b,
1656 }
1657 }(),
1658 want: `{
1659 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1660 "value": true
1661}`,
1662 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001663 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001664 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001665 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001666 },
1667 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001668 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001669 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1670 if err != nil {
1671 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1672 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001673 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001674 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1675 Value: b,
1676 }
1677 }(),
1678 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001679 "@type": "type.googleapis.com/google.protobuf.Empty",
1680 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001681}`,
1682 }, {
1683 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001684 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001685 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001686 },
1687 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001688 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001689 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1690 if err != nil {
1691 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1692 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001693 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001694 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001695 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001696 }
1697 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001698 wantErr: true,
1699 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001700 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001701 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001702 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001703 },
1704 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001705 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -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 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001710 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001711 TypeUrl: "google.protobuf.Int64Value",
1712 Value: b,
1713 }
1714 }(),
1715 want: `{
1716 "@type": "google.protobuf.Int64Value",
1717 "value": "42"
1718}`,
1719 }, {
1720 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001721 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001722 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001723 },
1724 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001725 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001726 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1727 if err != nil {
1728 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1729 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001730 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001731 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1732 Value: b,
1733 }
1734 }(),
1735 want: `{
1736 "@type": "type.googleapis.com/google.protobuf.Duration",
1737 "value": "0s"
1738}`,
1739 }, {
1740 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001741 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001742 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001743 },
1744 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001745 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001746 b, err := proto.Marshal(m)
1747 if err != nil {
1748 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1749 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001750 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001751 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1752 Value: b,
1753 }
1754 }(),
1755 wantErr: true,
1756 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001757 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001758 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001759 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001760 },
1761 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001762 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001763 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1764 if err != nil {
1765 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1766 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001767 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001768 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001769 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001770 }
1771 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001772 wantErr: true,
1773 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001774 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001775 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001776 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001777 },
1778 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001779 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001780 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001781 if err != nil {
1782 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1783 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001784 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001785 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1786 Value: b,
1787 }
1788 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001789 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001790 "@type": "type.googleapis.com/google.protobuf.Value",
1791 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001792}`,
1793 }, {
1794 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001795 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001796 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001797 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1798 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1799 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1800 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1801 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001802 ),
1803 },
1804 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001805 m := &structpb.Struct{
1806 Fields: map[string]*structpb.Value{
1807 "bool": {Kind: &structpb.Value_BoolValue{true}},
1808 "null": {Kind: &structpb.Value_NullValue{}},
1809 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001810 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001811 Kind: &structpb.Value_StructValue{
1812 &structpb.Struct{
1813 Fields: map[string]*structpb.Value{
1814 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001815 },
1816 },
1817 },
1818 },
1819 },
1820 }
1821 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1822 if err != nil {
1823 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1824 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001825 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001826 TypeUrl: "google.protobuf.Struct",
1827 Value: b,
1828 }
1829 }(),
1830 want: `{
1831 "@type": "google.protobuf.Struct",
1832 "value": {
1833 "bool": true,
1834 "null": null,
1835 "string": "hello",
1836 "struct": {
1837 "string": "world"
1838 }
1839 }
1840}`,
1841 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001842 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001843 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001844 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001845 },
1846 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001847 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001848 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1849 if err != nil {
1850 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1851 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001852 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001853 Value: b,
1854 }
1855 }(),
1856 wantErr: true,
1857 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001858 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001859 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001860 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001861 },
1862 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001863 OptBool: &wrapperspb.BoolValue{Value: false},
1864 OptInt32: &wrapperspb.Int32Value{Value: 42},
1865 OptInt64: &wrapperspb.Int64Value{Value: 42},
1866 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1867 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1868 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1869 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1870 OptString: &wrapperspb.StringValue{Value: "hello"},
1871 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1872 OptDuration: &durationpb.Duration{Seconds: 123},
1873 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1874 OptStruct: &structpb.Struct{
1875 Fields: map[string]*structpb.Value{
1876 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001877 },
1878 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001879 OptList: &structpb.ListValue{
1880 Values: []*structpb.Value{
1881 {Kind: &structpb.Value_NullValue{}},
1882 {Kind: &structpb.Value_StringValue{}},
1883 {Kind: &structpb.Value_StructValue{}},
1884 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001885 },
1886 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001887 OptValue: &structpb.Value{
1888 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001889 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001890 OptEmpty: &emptypb.Empty{},
1891 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001892 TypeUrl: "google.protobuf.Empty",
1893 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001894 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001895 Paths: []string{"foo_bar", "bar_foo"},
1896 },
1897 },
1898 want: `{
1899 "optBool": false,
1900 "optInt32": 42,
1901 "optInt64": "42",
1902 "optUint32": 42,
1903 "optUint64": "42",
1904 "optFloat": 1.23,
1905 "optDouble": 3.1415,
1906 "optString": "hello",
1907 "optBytes": "aGVsbG8=",
1908 "optDuration": "123s",
1909 "optTimestamp": "2019-03-19T23:03:21Z",
1910 "optStruct": {
1911 "string": "hello"
1912 },
1913 "optList": [
1914 null,
1915 "",
1916 {},
1917 []
1918 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001919 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001920 "optEmpty": {},
1921 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001922 "@type": "google.protobuf.Empty",
1923 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001924 },
1925 "optFieldmask": "fooBar,barFoo"
1926}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001927 }}
1928
1929 for _, tt := range tests {
1930 tt := tt
1931 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001932 // Use 2-space indentation on all MarshalOptions.
1933 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001934 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001935 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001936 t.Errorf("Marshal() returned error: %v\n", err)
1937 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001938 if err == nil && tt.wantErr {
1939 t.Errorf("Marshal() got nil error, want error\n")
1940 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001941 got := string(b)
1942 if got != tt.want {
1943 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1944 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
1945 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1946 }
1947 }
1948 })
1949 }
1950}