blob: bc6650cbd74858a8042be5f43c0d763afddeffc4 [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",
889 input: &pb2.Scalars{
890 OptString: scalar.String("no unknowns"),
891 XXX_unrecognized: pack.Message{
892 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
893 }.Marshal(),
894 },
895 want: `{
896 "optString": "no unknowns"
897}`,
898 }, {
899 desc: "json_name",
900 input: &pb3.JSONNames{
901 SString: "json_name",
902 },
903 want: `{
904 "foo_bar": "json_name"
905}`,
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700906 }, {
907 desc: "extensions of non-repeated fields",
908 input: func() proto.Message {
909 m := &pb2.Extensions{
910 OptString: scalar.String("non-extension field"),
911 OptBool: scalar.Bool(true),
912 OptInt32: scalar.Int32(42),
913 }
914 setExtension(m, pb2.E_OptExtBool, true)
915 setExtension(m, pb2.E_OptExtString, "extension field")
916 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
917 setExtension(m, pb2.E_OptExtNested, &pb2.Nested{
918 OptString: scalar.String("nested in an extension"),
919 OptNested: &pb2.Nested{
920 OptString: scalar.String("another nested in an extension"),
921 },
922 })
923 return m
924 }(),
925 want: `{
926 "optString": "non-extension field",
927 "optBool": true,
928 "optInt32": 42,
929 "[pb2.opt_ext_bool]": true,
930 "[pb2.opt_ext_enum]": "TEN",
931 "[pb2.opt_ext_nested]": {
932 "optString": "nested in an extension",
933 "optNested": {
934 "optString": "another nested in an extension"
935 }
936 },
937 "[pb2.opt_ext_string]": "extension field"
938}`,
939 }, {
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700940 desc: "extensions of repeated fields",
941 input: func() proto.Message {
942 m := &pb2.Extensions{}
943 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
944 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
945 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
946 &pb2.Nested{OptString: scalar.String("one")},
947 &pb2.Nested{OptString: scalar.String("two")},
948 &pb2.Nested{OptString: scalar.String("three")},
949 })
950 return m
951 }(),
952 want: `{
953 "[pb2.rpt_ext_enum]": [
954 "TEN",
955 101,
956 "ONE"
957 ],
958 "[pb2.rpt_ext_fixed32]": [
959 42,
960 47
961 ],
962 "[pb2.rpt_ext_nested]": [
963 {
964 "optString": "one"
965 },
966 {
967 "optString": "two"
968 },
969 {
970 "optString": "three"
971 }
972 ]
973}`,
974 }, {
975 desc: "extensions of non-repeated fields in another message",
976 input: func() proto.Message {
977 m := &pb2.Extensions{}
978 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
979 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
980 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
981 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
982 OptString: scalar.String("nested in an extension"),
983 OptNested: &pb2.Nested{
984 OptString: scalar.String("another nested in an extension"),
985 },
986 })
987 return m
988 }(),
989 want: `{
990 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
991 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
992 "[pb2.ExtensionsContainer.opt_ext_nested]": {
993 "optString": "nested in an extension",
994 "optNested": {
995 "optString": "another nested in an extension"
996 }
997 },
998 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
999}`,
1000 }, {
1001 desc: "extensions of repeated fields in another message",
1002 input: func() proto.Message {
1003 m := &pb2.Extensions{
1004 OptString: scalar.String("non-extension field"),
1005 OptBool: scalar.Bool(true),
1006 OptInt32: scalar.Int32(42),
1007 }
1008 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1009 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
1010 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
1011 &pb2.Nested{OptString: scalar.String("one")},
1012 &pb2.Nested{OptString: scalar.String("two")},
1013 &pb2.Nested{OptString: scalar.String("three")},
1014 })
1015 return m
1016 }(),
1017 want: `{
1018 "optString": "non-extension field",
1019 "optBool": true,
1020 "optInt32": 42,
1021 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
1022 "TEN",
1023 101,
1024 "ONE"
1025 ],
1026 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1027 {
1028 "optString": "one"
1029 },
1030 {
1031 "optString": "two"
1032 },
1033 {
1034 "optString": "three"
1035 }
1036 ],
1037 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1038 "hello",
1039 "world"
1040 ]
1041}`,
1042 }, {
1043 desc: "MessageSet",
1044 input: func() proto.Message {
1045 m := &pb2.MessageSet{}
1046 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1047 OptString: scalar.String("a messageset extension"),
1048 })
1049 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1050 OptString: scalar.String("not a messageset extension"),
1051 })
1052 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1053 OptString: scalar.String("just a regular extension"),
1054 })
1055 return m
1056 }(),
1057 want: `{
1058 "[pb2.MessageSetExtension]": {
1059 "optString": "a messageset extension"
1060 },
1061 "[pb2.MessageSetExtension.ext_nested]": {
1062 "optString": "just a regular extension"
1063 },
1064 "[pb2.MessageSetExtension.not_message_set_extension]": {
1065 "optString": "not a messageset extension"
1066 }
1067}`,
1068 }, {
1069 desc: "not real MessageSet 1",
1070 input: func() proto.Message {
1071 m := &pb2.FakeMessageSet{}
1072 setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1073 OptString: scalar.String("not a messageset extension"),
1074 })
1075 return m
1076 }(),
1077 want: `{
1078 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1079 "optString": "not a messageset extension"
1080 }
1081}`,
1082 }, {
1083 desc: "not real MessageSet 2",
1084 input: func() proto.Message {
1085 m := &pb2.MessageSet{}
1086 setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1087 OptString: scalar.String("another not a messageset extension"),
1088 })
1089 return m
1090 }(),
1091 want: `{
1092 "[pb2.message_set_extension]": {
1093 "optString": "another not a messageset extension"
1094 }
1095}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001096 }, {
1097 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001098 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001099 want: `false`,
1100 }, {
1101 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001102 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001103 want: `true`,
1104 }, {
1105 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001106 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001107 want: `0`,
1108 }, {
1109 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001110 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001111 want: `42`,
1112 }, {
1113 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001114 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001115 want: `"42"`,
1116 }, {
1117 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001118 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001119 want: `42`,
1120 }, {
1121 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001122 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001123 want: `"42"`,
1124 }, {
1125 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001126 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001127 want: `1.02`,
1128 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001129 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001130 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001131 want: `"-Infinity"`,
1132 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001133 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001134 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001135 want: `1.02`,
1136 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001137 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001138 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001139 want: `"NaN"`,
1140 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001141 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001142 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001143 want: `""`,
1144 }, {
1145 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001146 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001147 want: `"谷歌"`,
1148 }, {
1149 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001150 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001151 wantErr: true,
1152 }, {
1153 desc: "StringValue field with invalid UTF8 error",
1154 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001155 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001156 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001157 wantErr: true,
1158 }, {
1159 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001160 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001161 want: `"aGVsbG8="`,
1162 }, {
1163 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001164 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001165 want: `{}`,
1166 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001167 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001168 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001169 want: `{
1170 "optNull": null
1171}`,
1172 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001173 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001174 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001175 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001176 }, {
1177 desc: "Value empty field",
1178 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001179 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001180 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001181 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001182 }, {
1183 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001184 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001185 want: `null`,
1186 }, {
1187 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001188 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001189 want: `false`,
1190 }, {
1191 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001192 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001193 want: `1.02`,
1194 }, {
1195 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001196 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001197 want: `"hello"`,
1198 }, {
1199 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001200 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001201 wantErr: true,
1202 }, {
1203 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001204 input: &structpb.Value{
1205 Kind: &structpb.Value_StructValue{
1206 &structpb.Struct{
1207 Fields: map[string]*structpb.Value{
1208 "null": {Kind: &structpb.Value_NullValue{}},
1209 "number": {Kind: &structpb.Value_NumberValue{}},
1210 "string": {Kind: &structpb.Value_StringValue{}},
1211 "struct": {Kind: &structpb.Value_StructValue{}},
1212 "list": {Kind: &structpb.Value_ListValue{}},
1213 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001214 },
1215 },
1216 },
1217 },
1218 want: `{
1219 "bool": false,
1220 "list": [],
1221 "null": null,
1222 "number": 0,
1223 "string": "",
1224 "struct": {}
1225}`,
1226 }, {
1227 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001228 input: &structpb.Value{
1229 Kind: &structpb.Value_ListValue{
1230 &structpb.ListValue{
1231 Values: []*structpb.Value{
1232 {Kind: &structpb.Value_BoolValue{}},
1233 {Kind: &structpb.Value_NullValue{}},
1234 {Kind: &structpb.Value_NumberValue{}},
1235 {Kind: &structpb.Value_StringValue{}},
1236 {Kind: &structpb.Value_StructValue{}},
1237 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001238 },
1239 },
1240 },
1241 },
1242 want: `[
1243 false,
1244 null,
1245 0,
1246 "",
1247 {},
1248 []
1249]`,
1250 }, {
1251 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001252 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001253 want: `{}`,
1254 }, {
1255 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001256 input: &structpb.Struct{
1257 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001258 },
1259 want: `{}`,
1260 }, {
1261 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001262 input: &structpb.Struct{
1263 Fields: map[string]*structpb.Value{
1264 "bool": {Kind: &structpb.Value_BoolValue{true}},
1265 "null": {Kind: &structpb.Value_NullValue{}},
1266 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1267 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001268 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001269 Kind: &structpb.Value_StructValue{
1270 &structpb.Struct{
1271 Fields: map[string]*structpb.Value{
1272 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001273 },
1274 },
1275 },
1276 },
1277 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001278 Kind: &structpb.Value_ListValue{
1279 &structpb.ListValue{
1280 Values: []*structpb.Value{
1281 {Kind: &structpb.Value_BoolValue{}},
1282 {Kind: &structpb.Value_NullValue{}},
1283 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001284 },
1285 },
1286 },
1287 },
1288 },
1289 },
1290 want: `{
1291 "bool": true,
1292 "list": [
1293 false,
1294 null,
1295 0
1296 ],
1297 "null": null,
1298 "number": 3.1415,
1299 "string": "hello",
1300 "struct": {
1301 "string": "world"
1302 }
1303}`,
1304 }, {
1305 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001306 input: &structpb.Struct{
1307 Fields: map[string]*structpb.Value{
1308 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001309 },
1310 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001311 wantErr: true,
1312 }, {
1313 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001314 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001315 want: `[]`,
1316 }, {
1317 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001318 input: &structpb.ListValue{
1319 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001320 },
1321 want: `[]`,
1322 }, {
1323 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001324 input: &structpb.ListValue{
1325 Values: []*structpb.Value{
1326 {Kind: &structpb.Value_BoolValue{true}},
1327 {Kind: &structpb.Value_NullValue{}},
1328 {Kind: &structpb.Value_NumberValue{3.1415}},
1329 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001330 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001331 Kind: &structpb.Value_ListValue{
1332 &structpb.ListValue{
1333 Values: []*structpb.Value{
1334 {Kind: &structpb.Value_BoolValue{}},
1335 {Kind: &structpb.Value_NullValue{}},
1336 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001337 },
1338 },
1339 },
1340 },
1341 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001342 Kind: &structpb.Value_StructValue{
1343 &structpb.Struct{
1344 Fields: map[string]*structpb.Value{
1345 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001346 },
1347 },
1348 },
1349 },
1350 },
1351 },
1352 want: `[
1353 true,
1354 null,
1355 3.1415,
1356 "hello",
1357 [
1358 false,
1359 null,
1360 0
1361 ],
1362 {
1363 "string": "world"
1364 }
1365]`,
1366 }, {
1367 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001368 input: &structpb.ListValue{
1369 Values: []*structpb.Value{
1370 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001371 },
1372 },
Herbie Ong0b0f4032019-03-18 19:06:15 -07001373 wantErr: true,
1374 }, {
1375 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001376 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001377 want: `"0s"`,
1378 }, {
1379 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001380 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001381 want: `"3s"`,
1382 }, {
1383 desc: "Duration with -secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001384 input: &durationpb.Duration{Seconds: -3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001385 want: `"-3s"`,
1386 }, {
1387 desc: "Duration with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001388 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001389 want: `"0.001s"`,
1390 }, {
1391 desc: "Duration with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001392 input: &durationpb.Duration{Nanos: -1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001393 want: `"-0.001s"`,
1394 }, {
1395 desc: "Duration with large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001396 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001397 want: `"10000000000.000000001s"`,
1398 }, {
1399 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001400 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001401 want: `"0.000010s"`,
1402 }, {
1403 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001404 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001405 want: `"0.001s"`,
1406 }, {
1407 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001408 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001409 want: `"-123.000000450s"`,
1410 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001411 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001412 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001413 want: `"315576000000.999999999s"`,
1414 }, {
1415 desc: "Duration min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001416 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001417 want: `"-315576000000.999999999s"`,
1418 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001419 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001420 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001421 wantErr: true,
1422 }, {
1423 desc: "Duration with -secs +nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001424 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001425 wantErr: true,
1426 }, {
1427 desc: "Duration with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001428 input: &durationpb.Duration{Seconds: 315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001429 wantErr: true,
1430 }, {
1431 desc: "Duration with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001432 input: &durationpb.Duration{Seconds: -315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001433 wantErr: true,
1434 }, {
1435 desc: "Duration with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001436 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001437 wantErr: true,
1438 }, {
1439 desc: "Duration with -nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001440 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001441 wantErr: true,
1442 }, {
1443 desc: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001444 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001445 want: `"1970-01-01T00:00:00Z"`,
1446 }, {
1447 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001448 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001449 want: `"2019-03-19T23:03:21Z"`,
1450 }, {
1451 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001452 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001453 want: `"2019-03-19T23:03:21.000000001Z"`,
1454 }, {
1455 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001456 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001457 want: `"1970-01-01T00:00:00.000001Z"`,
1458 }, {
1459 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001460 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001461 want: `"1970-01-01T00:00:00.010Z"`,
1462 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001463 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001464 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001465 want: `"9999-12-31T23:59:59.999999999Z"`,
1466 }, {
1467 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001468 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001469 want: `"0001-01-01T00:00:00Z"`,
1470 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001471 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001472 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001473 wantErr: true,
1474 }, {
1475 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001476 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001477 wantErr: true,
1478 }, {
1479 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001480 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001481 wantErr: true,
1482 }, {
1483 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001484 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001485 wantErr: true,
1486 }, {
1487 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001488 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001489 want: `""`,
1490 }, {
1491 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001492 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001493 Paths: []string{
1494 "foo",
1495 "foo_bar",
1496 "foo.bar_qux",
1497 "_foo",
1498 },
1499 },
1500 want: `"foo,fooBar,foo.barQux,Foo"`,
1501 }, {
1502 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001503 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001504 Paths: []string{"foo_"},
1505 },
1506 wantErr: true,
1507 }, {
1508 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001509 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001510 Paths: []string{"foo__bar"},
1511 },
1512 wantErr: true,
1513 }, {
1514 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001515 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001516 want: `{}`,
1517 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001518 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001519 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001520 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001521 },
1522 input: func() proto.Message {
1523 m := &pb2.Nested{
1524 OptString: scalar.String("embedded inside Any"),
1525 OptNested: &pb2.Nested{
1526 OptString: scalar.String("inception"),
1527 },
1528 }
1529 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1530 if err != nil {
1531 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1532 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001533 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001534 TypeUrl: "foo/pb2.Nested",
1535 Value: b,
1536 }
1537 }(),
1538 want: `{
1539 "@type": "foo/pb2.Nested",
1540 "optString": "embedded inside Any",
1541 "optNested": {
1542 "optString": "inception"
1543 }
1544}`,
1545 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001546 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001547 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001548 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001549 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001550 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001551 want: `{
1552 "@type": "foo/pb2.Nested"
1553}`,
1554 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001555 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001556 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001557 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001558 wantErr: true,
1559 }, {
Damien Neil0c9f0a92019-06-19 10:41:09 -07001560 desc: "Any with missing required",
Damien Neil5c5b5312019-05-14 12:44:37 -07001561 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001562 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001563 },
1564 input: func() proto.Message {
1565 m := &pb2.PartialRequired{
1566 OptString: scalar.String("embedded inside Any"),
1567 }
Damien Neil96c229a2019-04-03 12:17:24 -07001568 b, err := proto.MarshalOptions{
1569 AllowPartial: true,
1570 Deterministic: true,
1571 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001572 if err != nil {
1573 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1574 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001575 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001576 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001577 Value: b,
1578 }
1579 }(),
1580 want: `{
1581 "@type": "pb2.PartialRequired",
1582 "optString": "embedded inside Any"
1583}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001584 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001585 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001586 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001587 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001588 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001589 },
1590 input: func() proto.Message {
1591 m := &pb2.PartialRequired{
1592 OptString: scalar.String("embedded inside Any"),
1593 }
Damien Neil96c229a2019-04-03 12:17:24 -07001594 b, err := proto.MarshalOptions{
1595 AllowPartial: true,
1596 Deterministic: true,
1597 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001598 if err != nil {
1599 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1600 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001601 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001602 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001603 Value: b,
1604 }
1605 }(),
1606 want: `{
1607 "@type": "pb2.PartialRequired",
1608 "optString": "embedded inside Any"
1609}`,
1610 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001611 desc: "Any with invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001612 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001613 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001614 },
1615 input: func() proto.Message {
1616 m := &pb2.Nested{
1617 OptString: scalar.String("abc\xff"),
1618 }
1619 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1620 if err != nil {
1621 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1622 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001623 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001624 TypeUrl: "foo/pb2.Nested",
1625 Value: b,
1626 }
1627 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001628 wantErr: true,
1629 }, {
1630 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001631 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001632 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001633 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001634 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001635 TypeUrl: "foo/pb2.Nested",
1636 Value: dhex("80"),
1637 },
1638 wantErr: true,
1639 }, {
1640 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001641 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001642 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001643 },
1644 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001645 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001646 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1647 if err != nil {
1648 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1649 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001650 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001651 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1652 Value: b,
1653 }
1654 }(),
1655 want: `{
1656 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1657 "value": true
1658}`,
1659 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001660 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001661 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001662 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001663 },
1664 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001665 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001666 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 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001670 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001671 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1672 Value: b,
1673 }
1674 }(),
1675 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001676 "@type": "type.googleapis.com/google.protobuf.Empty",
1677 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001678}`,
1679 }, {
1680 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001681 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001682 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001683 },
1684 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001685 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001686 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 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001690 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001691 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001692 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001693 }
1694 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001695 wantErr: true,
1696 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001697 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001698 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001699 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001700 },
1701 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001702 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001703 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1704 if err != nil {
1705 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1706 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001707 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001708 TypeUrl: "google.protobuf.Int64Value",
1709 Value: b,
1710 }
1711 }(),
1712 want: `{
1713 "@type": "google.protobuf.Int64Value",
1714 "value": "42"
1715}`,
1716 }, {
1717 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001718 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001719 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001720 },
1721 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001722 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001723 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1724 if err != nil {
1725 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1726 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001727 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001728 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1729 Value: b,
1730 }
1731 }(),
1732 want: `{
1733 "@type": "type.googleapis.com/google.protobuf.Duration",
1734 "value": "0s"
1735}`,
1736 }, {
1737 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001738 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001739 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001740 },
1741 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001742 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001743 b, err := proto.Marshal(m)
1744 if err != nil {
1745 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1746 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001747 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001748 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1749 Value: b,
1750 }
1751 }(),
1752 wantErr: true,
1753 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001754 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001755 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001756 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001757 },
1758 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001759 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001760 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1761 if err != nil {
1762 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1763 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001764 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001765 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001766 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001767 }
1768 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001769 wantErr: true,
1770 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001771 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001772 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001773 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001774 },
1775 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001776 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001777 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001778 if err != nil {
1779 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1780 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001781 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001782 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1783 Value: b,
1784 }
1785 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001786 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001787 "@type": "type.googleapis.com/google.protobuf.Value",
1788 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001789}`,
1790 }, {
1791 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001792 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001793 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001794 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1795 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1796 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1797 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1798 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001799 ),
1800 },
1801 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001802 m := &structpb.Struct{
1803 Fields: map[string]*structpb.Value{
1804 "bool": {Kind: &structpb.Value_BoolValue{true}},
1805 "null": {Kind: &structpb.Value_NullValue{}},
1806 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001807 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001808 Kind: &structpb.Value_StructValue{
1809 &structpb.Struct{
1810 Fields: map[string]*structpb.Value{
1811 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001812 },
1813 },
1814 },
1815 },
1816 },
1817 }
1818 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1819 if err != nil {
1820 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1821 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001822 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001823 TypeUrl: "google.protobuf.Struct",
1824 Value: b,
1825 }
1826 }(),
1827 want: `{
1828 "@type": "google.protobuf.Struct",
1829 "value": {
1830 "bool": true,
1831 "null": null,
1832 "string": "hello",
1833 "struct": {
1834 "string": "world"
1835 }
1836 }
1837}`,
1838 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001839 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001840 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001841 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001842 },
1843 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001844 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001845 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1846 if err != nil {
1847 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1848 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001849 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001850 Value: b,
1851 }
1852 }(),
1853 wantErr: true,
1854 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001855 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001856 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001857 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001858 },
1859 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001860 OptBool: &wrapperspb.BoolValue{Value: false},
1861 OptInt32: &wrapperspb.Int32Value{Value: 42},
1862 OptInt64: &wrapperspb.Int64Value{Value: 42},
1863 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1864 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1865 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1866 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1867 OptString: &wrapperspb.StringValue{Value: "hello"},
1868 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1869 OptDuration: &durationpb.Duration{Seconds: 123},
1870 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1871 OptStruct: &structpb.Struct{
1872 Fields: map[string]*structpb.Value{
1873 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001874 },
1875 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001876 OptList: &structpb.ListValue{
1877 Values: []*structpb.Value{
1878 {Kind: &structpb.Value_NullValue{}},
1879 {Kind: &structpb.Value_StringValue{}},
1880 {Kind: &structpb.Value_StructValue{}},
1881 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001882 },
1883 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001884 OptValue: &structpb.Value{
1885 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001886 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001887 OptEmpty: &emptypb.Empty{},
1888 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001889 TypeUrl: "google.protobuf.Empty",
1890 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001891 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001892 Paths: []string{"foo_bar", "bar_foo"},
1893 },
1894 },
1895 want: `{
1896 "optBool": false,
1897 "optInt32": 42,
1898 "optInt64": "42",
1899 "optUint32": 42,
1900 "optUint64": "42",
1901 "optFloat": 1.23,
1902 "optDouble": 3.1415,
1903 "optString": "hello",
1904 "optBytes": "aGVsbG8=",
1905 "optDuration": "123s",
1906 "optTimestamp": "2019-03-19T23:03:21Z",
1907 "optStruct": {
1908 "string": "hello"
1909 },
1910 "optList": [
1911 null,
1912 "",
1913 {},
1914 []
1915 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001916 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001917 "optEmpty": {},
1918 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001919 "@type": "google.protobuf.Empty",
1920 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001921 },
1922 "optFieldmask": "fooBar,barFoo"
1923}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001924 }}
1925
1926 for _, tt := range tests {
1927 tt := tt
1928 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001929 // Use 2-space indentation on all MarshalOptions.
1930 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001931 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001932 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001933 t.Errorf("Marshal() returned error: %v\n", err)
1934 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001935 if err == nil && tt.wantErr {
1936 t.Errorf("Marshal() got nil error, want error\n")
1937 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001938 got := string(b)
1939 if got != tt.want {
1940 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1941 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
1942 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1943 }
1944 }
1945 })
1946 }
1947}