blob: 3483aed99dd6b705a4d1046002297e41c69f2334 [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"
18 "google.golang.org/protobuf/internal/encoding/wire"
19 pimpl "google.golang.org/protobuf/internal/impl"
20 "google.golang.org/protobuf/internal/scalar"
21 "google.golang.org/protobuf/proto"
22 preg "google.golang.org/protobuf/reflect/protoregistry"
23 "google.golang.org/protobuf/runtime/protoiface"
Herbie Ong7b828bc2019-02-08 19:56:24 -080024
Damien Neile89e6242019-05-13 23:55:40 -070025 "google.golang.org/protobuf/encoding/testprotos/pb2"
26 "google.golang.org/protobuf/encoding/testprotos/pb3"
Joe Tsaia95b29f2019-05-16 12:47:20 -070027 "google.golang.org/protobuf/types/known/anypb"
28 "google.golang.org/protobuf/types/known/durationpb"
29 "google.golang.org/protobuf/types/known/emptypb"
30 "google.golang.org/protobuf/types/known/fieldmaskpb"
31 "google.golang.org/protobuf/types/known/structpb"
32 "google.golang.org/protobuf/types/known/timestamppb"
33 "google.golang.org/protobuf/types/known/wrapperspb"
Herbie Ong7b828bc2019-02-08 19:56:24 -080034)
35
36// splitLines is a cmpopts.Option for comparing strings with line breaks.
37var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string {
38 return strings.Split(s, "\n")
39})
40
41func pb2Enum(i int32) *pb2.Enum {
42 p := new(pb2.Enum)
43 *p = pb2.Enum(i)
44 return p
45}
46
47func pb2Enums_NestedEnum(i int32) *pb2.Enums_NestedEnum {
48 p := new(pb2.Enums_NestedEnum)
49 *p = pb2.Enums_NestedEnum(i)
50 return p
51}
52
Joe Tsai4fddeba2019-03-20 18:29:32 -070053func setExtension(m proto.Message, xd *protoiface.ExtensionDescV1, val interface{}) {
Herbie Ongf83d5bb2019-03-14 00:01:27 -070054 knownFields := m.ProtoReflect().KnownFields()
55 extTypes := knownFields.ExtensionTypes()
56 extTypes.Register(xd.Type)
57 if val == nil {
58 return
59 }
60 pval := xd.Type.ValueOf(val)
61 knownFields.Set(wire.Number(xd.Field), pval)
62}
63
Herbie Ong0b0f4032019-03-18 19:06:15 -070064// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
65func dhex(s string) []byte {
66 b, err := hex.DecodeString(s)
67 if err != nil {
68 panic(err)
69 }
70 return b
71}
72
Herbie Ong7b828bc2019-02-08 19:56:24 -080073func TestMarshal(t *testing.T) {
74 tests := []struct {
Herbie Ong0b0f4032019-03-18 19:06:15 -070075 desc string
Damien Neil5c5b5312019-05-14 12:44:37 -070076 mo protojson.MarshalOptions
Herbie Ong0b0f4032019-03-18 19:06:15 -070077 input proto.Message
78 want string
79 wantErr bool // TODO: Verify error message substring.
Herbie Ong7b828bc2019-02-08 19:56:24 -080080 }{{
81 desc: "proto2 optional scalars not set",
82 input: &pb2.Scalars{},
83 want: "{}",
84 }, {
85 desc: "proto3 scalars not set",
86 input: &pb3.Scalars{},
87 want: "{}",
88 }, {
89 desc: "proto2 optional scalars set to zero values",
90 input: &pb2.Scalars{
91 OptBool: scalar.Bool(false),
92 OptInt32: scalar.Int32(0),
93 OptInt64: scalar.Int64(0),
94 OptUint32: scalar.Uint32(0),
95 OptUint64: scalar.Uint64(0),
96 OptSint32: scalar.Int32(0),
97 OptSint64: scalar.Int64(0),
98 OptFixed32: scalar.Uint32(0),
99 OptFixed64: scalar.Uint64(0),
100 OptSfixed32: scalar.Int32(0),
101 OptSfixed64: scalar.Int64(0),
102 OptFloat: scalar.Float32(0),
103 OptDouble: scalar.Float64(0),
104 OptBytes: []byte{},
105 OptString: scalar.String(""),
106 },
107 want: `{
108 "optBool": false,
109 "optInt32": 0,
110 "optInt64": "0",
111 "optUint32": 0,
112 "optUint64": "0",
113 "optSint32": 0,
114 "optSint64": "0",
115 "optFixed32": 0,
116 "optFixed64": "0",
117 "optSfixed32": 0,
118 "optSfixed64": "0",
119 "optFloat": 0,
120 "optDouble": 0,
121 "optBytes": "",
122 "optString": ""
123}`,
124 }, {
125 desc: "proto2 optional scalars set to some values",
126 input: &pb2.Scalars{
127 OptBool: scalar.Bool(true),
128 OptInt32: scalar.Int32(0xff),
129 OptInt64: scalar.Int64(0xdeadbeef),
130 OptUint32: scalar.Uint32(47),
131 OptUint64: scalar.Uint64(0xdeadbeef),
132 OptSint32: scalar.Int32(-1001),
133 OptSint64: scalar.Int64(-0xffff),
134 OptFixed64: scalar.Uint64(64),
135 OptSfixed32: scalar.Int32(-32),
136 OptFloat: scalar.Float32(1.02),
137 OptDouble: scalar.Float64(1.234),
Herbie Ong87608a72019-03-06 14:32:24 -0800138 OptBytes: []byte("谷歌"),
Herbie Ong7b828bc2019-02-08 19:56:24 -0800139 OptString: scalar.String("谷歌"),
140 },
141 want: `{
142 "optBool": true,
143 "optInt32": 255,
144 "optInt64": "3735928559",
145 "optUint32": 47,
146 "optUint64": "3735928559",
147 "optSint32": -1001,
148 "optSint64": "-65535",
149 "optFixed64": "64",
150 "optSfixed32": -32,
151 "optFloat": 1.02,
152 "optDouble": 1.234,
153 "optBytes": "6LC35q2M",
154 "optString": "谷歌"
155}`,
156 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -0700157 desc: "string",
158 input: &pb3.Scalars{
159 SString: "谷歌",
160 },
161 want: `{
162 "sString": "谷歌"
163}`,
164 }, {
165 desc: "string with invalid UTF8",
166 input: &pb3.Scalars{
167 SString: "abc\xff",
168 },
169 want: "{\n \"sString\": \"abc\xff\"\n}",
170 wantErr: true,
171 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800172 desc: "float nan",
173 input: &pb3.Scalars{
174 SFloat: float32(math.NaN()),
175 },
176 want: `{
177 "sFloat": "NaN"
178}`,
179 }, {
180 desc: "float positive infinity",
181 input: &pb3.Scalars{
182 SFloat: float32(math.Inf(1)),
183 },
184 want: `{
185 "sFloat": "Infinity"
186}`,
187 }, {
188 desc: "float negative infinity",
189 input: &pb3.Scalars{
190 SFloat: float32(math.Inf(-1)),
191 },
192 want: `{
193 "sFloat": "-Infinity"
194}`,
195 }, {
196 desc: "double nan",
197 input: &pb3.Scalars{
198 SDouble: math.NaN(),
199 },
200 want: `{
201 "sDouble": "NaN"
202}`,
203 }, {
204 desc: "double positive infinity",
205 input: &pb3.Scalars{
206 SDouble: math.Inf(1),
207 },
208 want: `{
209 "sDouble": "Infinity"
210}`,
211 }, {
212 desc: "double negative infinity",
213 input: &pb3.Scalars{
214 SDouble: math.Inf(-1),
215 },
216 want: `{
217 "sDouble": "-Infinity"
218}`,
219 }, {
220 desc: "proto2 enum not set",
221 input: &pb2.Enums{},
222 want: "{}",
223 }, {
224 desc: "proto2 enum set to zero value",
225 input: &pb2.Enums{
226 OptEnum: pb2Enum(0),
227 OptNestedEnum: pb2Enums_NestedEnum(0),
228 },
229 want: `{
230 "optEnum": 0,
231 "optNestedEnum": 0
232}`,
233 }, {
234 desc: "proto2 enum",
235 input: &pb2.Enums{
236 OptEnum: pb2.Enum_ONE.Enum(),
237 OptNestedEnum: pb2.Enums_UNO.Enum(),
238 },
239 want: `{
240 "optEnum": "ONE",
241 "optNestedEnum": "UNO"
242}`,
243 }, {
244 desc: "proto2 enum set to numeric values",
245 input: &pb2.Enums{
246 OptEnum: pb2Enum(2),
247 OptNestedEnum: pb2Enums_NestedEnum(2),
248 },
249 want: `{
250 "optEnum": "TWO",
251 "optNestedEnum": "DOS"
252}`,
253 }, {
254 desc: "proto2 enum set to unnamed numeric values",
255 input: &pb2.Enums{
256 OptEnum: pb2Enum(101),
257 OptNestedEnum: pb2Enums_NestedEnum(-101),
258 },
259 want: `{
260 "optEnum": 101,
261 "optNestedEnum": -101
262}`,
263 }, {
264 desc: "proto3 enum not set",
265 input: &pb3.Enums{},
266 want: "{}",
267 }, {
268 desc: "proto3 enum set to zero value",
269 input: &pb3.Enums{
270 SEnum: pb3.Enum_ZERO,
271 SNestedEnum: pb3.Enums_CERO,
272 },
273 want: "{}",
274 }, {
275 desc: "proto3 enum",
276 input: &pb3.Enums{
277 SEnum: pb3.Enum_ONE,
278 SNestedEnum: pb3.Enums_UNO,
279 },
280 want: `{
281 "sEnum": "ONE",
282 "sNestedEnum": "UNO"
283}`,
284 }, {
285 desc: "proto3 enum set to numeric values",
286 input: &pb3.Enums{
287 SEnum: 2,
288 SNestedEnum: 2,
289 },
290 want: `{
291 "sEnum": "TWO",
292 "sNestedEnum": "DOS"
293}`,
294 }, {
295 desc: "proto3 enum set to unnamed numeric values",
296 input: &pb3.Enums{
297 SEnum: -47,
298 SNestedEnum: 47,
299 },
300 want: `{
301 "sEnum": -47,
302 "sNestedEnum": 47
303}`,
304 }, {
305 desc: "proto2 nested message not set",
306 input: &pb2.Nests{},
307 want: "{}",
308 }, {
309 desc: "proto2 nested message set to empty",
310 input: &pb2.Nests{
311 OptNested: &pb2.Nested{},
312 Optgroup: &pb2.Nests_OptGroup{},
313 },
314 want: `{
315 "optNested": {},
316 "optgroup": {}
317}`,
318 }, {
319 desc: "proto2 nested messages",
320 input: &pb2.Nests{
321 OptNested: &pb2.Nested{
322 OptString: scalar.String("nested message"),
323 OptNested: &pb2.Nested{
324 OptString: scalar.String("another nested message"),
325 },
326 },
327 },
328 want: `{
329 "optNested": {
330 "optString": "nested message",
331 "optNested": {
332 "optString": "another nested message"
333 }
334 }
335}`,
336 }, {
337 desc: "proto2 groups",
338 input: &pb2.Nests{
339 Optgroup: &pb2.Nests_OptGroup{
340 OptString: scalar.String("inside a group"),
341 OptNested: &pb2.Nested{
342 OptString: scalar.String("nested message inside a group"),
343 },
344 Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
345 OptFixed32: scalar.Uint32(47),
346 },
347 },
348 },
349 want: `{
350 "optgroup": {
351 "optString": "inside a group",
352 "optNested": {
353 "optString": "nested message inside a group"
354 },
355 "optnestedgroup": {
356 "optFixed32": 47
357 }
358 }
359}`,
360 }, {
361 desc: "proto3 nested message not set",
362 input: &pb3.Nests{},
363 want: "{}",
364 }, {
365 desc: "proto3 nested message set to empty",
366 input: &pb3.Nests{
367 SNested: &pb3.Nested{},
368 },
369 want: `{
370 "sNested": {}
371}`,
372 }, {
373 desc: "proto3 nested message",
374 input: &pb3.Nests{
375 SNested: &pb3.Nested{
376 SString: "nested message",
377 SNested: &pb3.Nested{
378 SString: "another nested message",
379 },
380 },
381 },
382 want: `{
383 "sNested": {
384 "sString": "nested message",
385 "sNested": {
386 "sString": "another nested message"
387 }
388 }
389}`,
390 }, {
391 desc: "oneof not set",
392 input: &pb3.Oneofs{},
393 want: "{}",
394 }, {
395 desc: "oneof set to empty string",
396 input: &pb3.Oneofs{
397 Union: &pb3.Oneofs_OneofString{},
398 },
399 want: `{
400 "oneofString": ""
401}`,
402 }, {
403 desc: "oneof set to string",
404 input: &pb3.Oneofs{
405 Union: &pb3.Oneofs_OneofString{
406 OneofString: "hello",
407 },
408 },
409 want: `{
410 "oneofString": "hello"
411}`,
412 }, {
413 desc: "oneof set to enum",
414 input: &pb3.Oneofs{
415 Union: &pb3.Oneofs_OneofEnum{
416 OneofEnum: pb3.Enum_ZERO,
417 },
418 },
419 want: `{
420 "oneofEnum": "ZERO"
421}`,
422 }, {
423 desc: "oneof set to empty message",
424 input: &pb3.Oneofs{
425 Union: &pb3.Oneofs_OneofNested{
426 OneofNested: &pb3.Nested{},
427 },
428 },
429 want: `{
430 "oneofNested": {}
431}`,
432 }, {
433 desc: "oneof set to message",
434 input: &pb3.Oneofs{
435 Union: &pb3.Oneofs_OneofNested{
436 OneofNested: &pb3.Nested{
437 SString: "nested message",
438 },
439 },
440 },
441 want: `{
442 "oneofNested": {
443 "sString": "nested message"
444 }
445}`,
446 }, {
447 desc: "repeated fields not set",
448 input: &pb2.Repeats{},
449 want: "{}",
450 }, {
451 desc: "repeated fields set to empty slices",
452 input: &pb2.Repeats{
453 RptBool: []bool{},
454 RptInt32: []int32{},
455 RptInt64: []int64{},
456 RptUint32: []uint32{},
457 RptUint64: []uint64{},
458 RptFloat: []float32{},
459 RptDouble: []float64{},
460 RptBytes: [][]byte{},
461 },
462 want: "{}",
463 }, {
464 desc: "repeated fields set to some values",
465 input: &pb2.Repeats{
466 RptBool: []bool{true, false, true, true},
467 RptInt32: []int32{1, 6, 0, 0},
468 RptInt64: []int64{-64, 47},
469 RptUint32: []uint32{0xff, 0xffff},
470 RptUint64: []uint64{0xdeadbeef},
471 RptFloat: []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
472 RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
473 RptString: []string{"hello", "世界"},
474 RptBytes: [][]byte{
475 []byte("hello"),
476 []byte("\xe4\xb8\x96\xe7\x95\x8c"),
477 },
478 },
479 want: `{
480 "rptBool": [
481 true,
482 false,
483 true,
484 true
485 ],
486 "rptInt32": [
487 1,
488 6,
489 0,
490 0
491 ],
492 "rptInt64": [
493 "-64",
494 "47"
495 ],
496 "rptUint32": [
497 255,
498 65535
499 ],
500 "rptUint64": [
501 "3735928559"
502 ],
503 "rptFloat": [
504 "NaN",
505 "Infinity",
506 "-Infinity",
507 1.034
508 ],
509 "rptDouble": [
510 "NaN",
511 "Infinity",
512 "-Infinity",
513 1.23e-308
514 ],
515 "rptString": [
516 "hello",
517 "世界"
518 ],
519 "rptBytes": [
520 "aGVsbG8=",
521 "5LiW55WM"
522 ]
523}`,
524 }, {
525 desc: "repeated enums",
526 input: &pb2.Enums{
527 RptEnum: []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
528 RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
529 },
530 want: `{
531 "rptEnum": [
532 "ONE",
533 "TWO",
534 "TEN",
535 42
536 ],
537 "rptNestedEnum": [
538 "DOS",
539 47,
540 "DIEZ"
541 ]
542}`,
543 }, {
544 desc: "repeated messages set to empty",
545 input: &pb2.Nests{
546 RptNested: []*pb2.Nested{},
547 Rptgroup: []*pb2.Nests_RptGroup{},
548 },
549 want: "{}",
550 }, {
551 desc: "repeated messages",
552 input: &pb2.Nests{
553 RptNested: []*pb2.Nested{
554 {
555 OptString: scalar.String("repeat nested one"),
556 },
557 {
558 OptString: scalar.String("repeat nested two"),
559 OptNested: &pb2.Nested{
560 OptString: scalar.String("inside repeat nested two"),
561 },
562 },
563 {},
564 },
565 },
566 want: `{
567 "rptNested": [
568 {
569 "optString": "repeat nested one"
570 },
571 {
572 "optString": "repeat nested two",
573 "optNested": {
574 "optString": "inside repeat nested two"
575 }
576 },
577 {}
578 ]
579}`,
580 }, {
581 desc: "repeated messages contains nil value",
582 input: &pb2.Nests{
583 RptNested: []*pb2.Nested{nil, {}},
584 },
585 want: `{
586 "rptNested": [
587 {},
588 {}
589 ]
590}`,
591 }, {
592 desc: "repeated groups",
593 input: &pb2.Nests{
594 Rptgroup: []*pb2.Nests_RptGroup{
595 {
596 RptString: []string{"hello", "world"},
597 },
598 {},
599 nil,
600 },
601 },
602 want: `{
603 "rptgroup": [
604 {
605 "rptString": [
606 "hello",
607 "world"
608 ]
609 },
610 {},
611 {}
612 ]
613}`,
614 }, {
615 desc: "map fields not set",
616 input: &pb3.Maps{},
617 want: "{}",
618 }, {
619 desc: "map fields set to empty",
620 input: &pb3.Maps{
621 Int32ToStr: map[int32]string{},
622 BoolToUint32: map[bool]uint32{},
623 Uint64ToEnum: map[uint64]pb3.Enum{},
624 StrToNested: map[string]*pb3.Nested{},
625 StrToOneofs: map[string]*pb3.Oneofs{},
626 },
627 want: "{}",
628 }, {
629 desc: "map fields 1",
630 input: &pb3.Maps{
631 BoolToUint32: map[bool]uint32{
632 true: 42,
633 false: 101,
634 },
635 },
636 want: `{
637 "boolToUint32": {
638 "false": 101,
639 "true": 42
640 }
641}`,
642 }, {
643 desc: "map fields 2",
644 input: &pb3.Maps{
645 Int32ToStr: map[int32]string{
646 -101: "-101",
647 0xff: "0xff",
648 0: "zero",
649 },
650 },
651 want: `{
652 "int32ToStr": {
653 "-101": "-101",
654 "0": "zero",
655 "255": "0xff"
656 }
657}`,
658 }, {
659 desc: "map fields 3",
660 input: &pb3.Maps{
661 Uint64ToEnum: map[uint64]pb3.Enum{
662 1: pb3.Enum_ONE,
663 2: pb3.Enum_TWO,
664 10: pb3.Enum_TEN,
665 47: 47,
666 },
667 },
668 want: `{
669 "uint64ToEnum": {
670 "1": "ONE",
671 "2": "TWO",
672 "10": "TEN",
673 "47": 47
674 }
675}`,
676 }, {
677 desc: "map fields 4",
678 input: &pb3.Maps{
679 StrToNested: map[string]*pb3.Nested{
680 "nested": &pb3.Nested{
681 SString: "nested in a map",
682 },
683 },
684 },
685 want: `{
686 "strToNested": {
687 "nested": {
688 "sString": "nested in a map"
689 }
690 }
691}`,
692 }, {
693 desc: "map fields 5",
694 input: &pb3.Maps{
695 StrToOneofs: map[string]*pb3.Oneofs{
696 "string": &pb3.Oneofs{
697 Union: &pb3.Oneofs_OneofString{
698 OneofString: "hello",
699 },
700 },
701 "nested": &pb3.Oneofs{
702 Union: &pb3.Oneofs_OneofNested{
703 OneofNested: &pb3.Nested{
704 SString: "nested oneof in map field value",
705 },
706 },
707 },
708 },
709 },
710 want: `{
711 "strToOneofs": {
712 "nested": {
713 "oneofNested": {
714 "sString": "nested oneof in map field value"
715 }
716 },
717 "string": {
718 "oneofString": "hello"
719 }
720 }
721}`,
722 }, {
723 desc: "map field contains nil value",
724 input: &pb3.Maps{
725 StrToNested: map[string]*pb3.Nested{
726 "nil": nil,
727 },
728 },
729 want: `{
730 "strToNested": {
731 "nil": {}
732 }
733}`,
734 }, {
Herbie Ong329be5b2019-03-27 14:47:59 -0700735 desc: "required fields not set",
736 input: &pb2.Requireds{},
737 want: `{}`,
738 wantErr: true,
739 }, {
740 desc: "required fields partially set",
741 input: &pb2.Requireds{
742 ReqBool: scalar.Bool(false),
743 ReqSfixed64: scalar.Int64(0),
744 ReqDouble: scalar.Float64(1.23),
745 ReqString: scalar.String("hello"),
746 ReqEnum: pb2.Enum_ONE.Enum(),
747 },
748 want: `{
749 "reqBool": false,
750 "reqSfixed64": "0",
751 "reqDouble": 1.23,
752 "reqString": "hello",
753 "reqEnum": "ONE"
754}`,
755 wantErr: true,
756 }, {
757 desc: "required fields not set with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700758 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700759 input: &pb2.Requireds{
760 ReqBool: scalar.Bool(false),
761 ReqSfixed64: scalar.Int64(0),
762 ReqDouble: scalar.Float64(1.23),
763 ReqString: scalar.String("hello"),
764 ReqEnum: pb2.Enum_ONE.Enum(),
765 },
766 want: `{
767 "reqBool": false,
768 "reqSfixed64": "0",
769 "reqDouble": 1.23,
770 "reqString": "hello",
771 "reqEnum": "ONE"
772}`,
773 }, {
774 desc: "required fields all set",
775 input: &pb2.Requireds{
776 ReqBool: scalar.Bool(false),
777 ReqSfixed64: scalar.Int64(0),
778 ReqDouble: scalar.Float64(1.23),
779 ReqString: scalar.String("hello"),
780 ReqEnum: pb2.Enum_ONE.Enum(),
781 ReqNested: &pb2.Nested{},
782 },
783 want: `{
784 "reqBool": false,
785 "reqSfixed64": "0",
786 "reqDouble": 1.23,
787 "reqString": "hello",
788 "reqEnum": "ONE",
789 "reqNested": {}
790}`,
791 }, {
792 desc: "indirect required field",
793 input: &pb2.IndirectRequired{
794 OptNested: &pb2.NestedWithRequired{},
795 },
796 want: `{
797 "optNested": {}
798}`,
799 wantErr: true,
800 }, {
801 desc: "indirect required field with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700802 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700803 input: &pb2.IndirectRequired{
804 OptNested: &pb2.NestedWithRequired{},
805 },
806 want: `{
807 "optNested": {}
808}`,
809 }, {
810 desc: "indirect required field in empty repeated",
811 input: &pb2.IndirectRequired{
812 RptNested: []*pb2.NestedWithRequired{},
813 },
814 want: `{}`,
815 }, {
816 desc: "indirect required field in repeated",
817 input: &pb2.IndirectRequired{
818 RptNested: []*pb2.NestedWithRequired{
819 &pb2.NestedWithRequired{},
820 },
821 },
822 want: `{
823 "rptNested": [
824 {}
825 ]
826}`,
827 wantErr: true,
828 }, {
829 desc: "indirect required field in repeated with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700830 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700831 input: &pb2.IndirectRequired{
832 RptNested: []*pb2.NestedWithRequired{
833 &pb2.NestedWithRequired{},
834 },
835 },
836 want: `{
837 "rptNested": [
838 {}
839 ]
840}`,
841 }, {
842 desc: "indirect required field in empty map",
843 input: &pb2.IndirectRequired{
844 StrToNested: map[string]*pb2.NestedWithRequired{},
845 },
846 want: "{}",
847 }, {
848 desc: "indirect required field in map",
849 input: &pb2.IndirectRequired{
850 StrToNested: map[string]*pb2.NestedWithRequired{
851 "fail": &pb2.NestedWithRequired{},
852 },
853 },
854 want: `{
855 "strToNested": {
856 "fail": {}
857 }
858}`,
859 wantErr: true,
860 }, {
861 desc: "indirect required field in map with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700862 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700863 input: &pb2.IndirectRequired{
864 StrToNested: map[string]*pb2.NestedWithRequired{
865 "fail": &pb2.NestedWithRequired{},
866 },
867 },
868 want: `{
869 "strToNested": {
870 "fail": {}
871 }
872}`,
873 }, {
874 desc: "indirect required field in oneof",
875 input: &pb2.IndirectRequired{
876 Union: &pb2.IndirectRequired_OneofNested{
877 OneofNested: &pb2.NestedWithRequired{},
878 },
879 },
880 want: `{
881 "oneofNested": {}
882}`,
883 wantErr: true,
884 }, {
885 desc: "indirect required field in oneof with AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -0700886 mo: protojson.MarshalOptions{AllowPartial: true},
Herbie Ong329be5b2019-03-27 14:47:59 -0700887 input: &pb2.IndirectRequired{
888 Union: &pb2.IndirectRequired_OneofNested{
889 OneofNested: &pb2.NestedWithRequired{},
890 },
891 },
892 want: `{
893 "oneofNested": {}
894}`,
895 }, {
Herbie Ong7b828bc2019-02-08 19:56:24 -0800896 desc: "unknown fields are ignored",
897 input: &pb2.Scalars{
898 OptString: scalar.String("no unknowns"),
899 XXX_unrecognized: pack.Message{
900 pack.Tag{101, pack.BytesType}, pack.String("hello world"),
901 }.Marshal(),
902 },
903 want: `{
904 "optString": "no unknowns"
905}`,
906 }, {
907 desc: "json_name",
908 input: &pb3.JSONNames{
909 SString: "json_name",
910 },
911 want: `{
912 "foo_bar": "json_name"
913}`,
Herbie Ongf83d5bb2019-03-14 00:01:27 -0700914 }, {
915 desc: "extensions of non-repeated fields",
916 input: func() proto.Message {
917 m := &pb2.Extensions{
918 OptString: scalar.String("non-extension field"),
919 OptBool: scalar.Bool(true),
920 OptInt32: scalar.Int32(42),
921 }
922 setExtension(m, pb2.E_OptExtBool, true)
923 setExtension(m, pb2.E_OptExtString, "extension field")
924 setExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
925 setExtension(m, pb2.E_OptExtNested, &pb2.Nested{
926 OptString: scalar.String("nested in an extension"),
927 OptNested: &pb2.Nested{
928 OptString: scalar.String("another nested in an extension"),
929 },
930 })
931 return m
932 }(),
933 want: `{
934 "optString": "non-extension field",
935 "optBool": true,
936 "optInt32": 42,
937 "[pb2.opt_ext_bool]": true,
938 "[pb2.opt_ext_enum]": "TEN",
939 "[pb2.opt_ext_nested]": {
940 "optString": "nested in an extension",
941 "optNested": {
942 "optString": "another nested in an extension"
943 }
944 },
945 "[pb2.opt_ext_string]": "extension field"
946}`,
947 }, {
948 desc: "extension message field set to nil",
949 input: func() proto.Message {
950 m := &pb2.Extensions{}
951 setExtension(m, pb2.E_OptExtNested, nil)
952 return m
953 }(),
954 want: "{}",
955 }, {
956 desc: "extensions of repeated fields",
957 input: func() proto.Message {
958 m := &pb2.Extensions{}
959 setExtension(m, pb2.E_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
960 setExtension(m, pb2.E_RptExtFixed32, &[]uint32{42, 47})
961 setExtension(m, pb2.E_RptExtNested, &[]*pb2.Nested{
962 &pb2.Nested{OptString: scalar.String("one")},
963 &pb2.Nested{OptString: scalar.String("two")},
964 &pb2.Nested{OptString: scalar.String("three")},
965 })
966 return m
967 }(),
968 want: `{
969 "[pb2.rpt_ext_enum]": [
970 "TEN",
971 101,
972 "ONE"
973 ],
974 "[pb2.rpt_ext_fixed32]": [
975 42,
976 47
977 ],
978 "[pb2.rpt_ext_nested]": [
979 {
980 "optString": "one"
981 },
982 {
983 "optString": "two"
984 },
985 {
986 "optString": "three"
987 }
988 ]
989}`,
990 }, {
991 desc: "extensions of non-repeated fields in another message",
992 input: func() proto.Message {
993 m := &pb2.Extensions{}
994 setExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
995 setExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
996 setExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
997 setExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
998 OptString: scalar.String("nested in an extension"),
999 OptNested: &pb2.Nested{
1000 OptString: scalar.String("another nested in an extension"),
1001 },
1002 })
1003 return m
1004 }(),
1005 want: `{
1006 "[pb2.ExtensionsContainer.opt_ext_bool]": true,
1007 "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
1008 "[pb2.ExtensionsContainer.opt_ext_nested]": {
1009 "optString": "nested in an extension",
1010 "optNested": {
1011 "optString": "another nested in an extension"
1012 }
1013 },
1014 "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
1015}`,
1016 }, {
1017 desc: "extensions of repeated fields in another message",
1018 input: func() proto.Message {
1019 m := &pb2.Extensions{
1020 OptString: scalar.String("non-extension field"),
1021 OptBool: scalar.Bool(true),
1022 OptInt32: scalar.Int32(42),
1023 }
1024 setExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, &[]pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1025 setExtension(m, pb2.E_ExtensionsContainer_RptExtString, &[]string{"hello", "world"})
1026 setExtension(m, pb2.E_ExtensionsContainer_RptExtNested, &[]*pb2.Nested{
1027 &pb2.Nested{OptString: scalar.String("one")},
1028 &pb2.Nested{OptString: scalar.String("two")},
1029 &pb2.Nested{OptString: scalar.String("three")},
1030 })
1031 return m
1032 }(),
1033 want: `{
1034 "optString": "non-extension field",
1035 "optBool": true,
1036 "optInt32": 42,
1037 "[pb2.ExtensionsContainer.rpt_ext_enum]": [
1038 "TEN",
1039 101,
1040 "ONE"
1041 ],
1042 "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1043 {
1044 "optString": "one"
1045 },
1046 {
1047 "optString": "two"
1048 },
1049 {
1050 "optString": "three"
1051 }
1052 ],
1053 "[pb2.ExtensionsContainer.rpt_ext_string]": [
1054 "hello",
1055 "world"
1056 ]
1057}`,
1058 }, {
1059 desc: "MessageSet",
1060 input: func() proto.Message {
1061 m := &pb2.MessageSet{}
1062 setExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1063 OptString: scalar.String("a messageset extension"),
1064 })
1065 setExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1066 OptString: scalar.String("not a messageset extension"),
1067 })
1068 setExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1069 OptString: scalar.String("just a regular extension"),
1070 })
1071 return m
1072 }(),
1073 want: `{
1074 "[pb2.MessageSetExtension]": {
1075 "optString": "a messageset extension"
1076 },
1077 "[pb2.MessageSetExtension.ext_nested]": {
1078 "optString": "just a regular extension"
1079 },
1080 "[pb2.MessageSetExtension.not_message_set_extension]": {
1081 "optString": "not a messageset extension"
1082 }
1083}`,
1084 }, {
1085 desc: "not real MessageSet 1",
1086 input: func() proto.Message {
1087 m := &pb2.FakeMessageSet{}
1088 setExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1089 OptString: scalar.String("not a messageset extension"),
1090 })
1091 return m
1092 }(),
1093 want: `{
1094 "[pb2.FakeMessageSetExtension.message_set_extension]": {
1095 "optString": "not a messageset extension"
1096 }
1097}`,
1098 }, {
1099 desc: "not real MessageSet 2",
1100 input: func() proto.Message {
1101 m := &pb2.MessageSet{}
1102 setExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1103 OptString: scalar.String("another not a messageset extension"),
1104 })
1105 return m
1106 }(),
1107 want: `{
1108 "[pb2.message_set_extension]": {
1109 "optString": "another not a messageset extension"
1110 }
1111}`,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001112 }, {
1113 desc: "BoolValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001114 input: &wrapperspb.BoolValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001115 want: `false`,
1116 }, {
1117 desc: "BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001118 input: &wrapperspb.BoolValue{Value: true},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001119 want: `true`,
1120 }, {
1121 desc: "Int32Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001122 input: &wrapperspb.Int32Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001123 want: `0`,
1124 }, {
1125 desc: "Int32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001126 input: &wrapperspb.Int32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001127 want: `42`,
1128 }, {
1129 desc: "Int64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001130 input: &wrapperspb.Int64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001131 want: `"42"`,
1132 }, {
1133 desc: "UInt32Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001134 input: &wrapperspb.UInt32Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001135 want: `42`,
1136 }, {
1137 desc: "UInt64Value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001138 input: &wrapperspb.UInt64Value{Value: 42},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001139 want: `"42"`,
1140 }, {
1141 desc: "FloatValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001142 input: &wrapperspb.FloatValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001143 want: `1.02`,
1144 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001145 desc: "FloatValue Infinity",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001146 input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
Herbie Onge63c4c42019-03-22 22:20:22 -07001147 want: `"-Infinity"`,
1148 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001149 desc: "DoubleValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001150 input: &wrapperspb.DoubleValue{Value: 1.02},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001151 want: `1.02`,
1152 }, {
Herbie Onge63c4c42019-03-22 22:20:22 -07001153 desc: "DoubleValue NaN",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001154 input: &wrapperspb.DoubleValue{Value: math.NaN()},
Herbie Onge63c4c42019-03-22 22:20:22 -07001155 want: `"NaN"`,
1156 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001157 desc: "StringValue empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001158 input: &wrapperspb.StringValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001159 want: `""`,
1160 }, {
1161 desc: "StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001162 input: &wrapperspb.StringValue{Value: "谷歌"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001163 want: `"谷歌"`,
1164 }, {
1165 desc: "StringValue with invalid UTF8 error",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001166 input: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001167 want: "\"abc\xff\"",
1168 wantErr: true,
1169 }, {
1170 desc: "StringValue field with invalid UTF8 error",
1171 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001172 OptString: &wrapperspb.StringValue{Value: "abc\xff"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001173 },
1174 want: "{\n \"optString\": \"abc\xff\"\n}",
1175 wantErr: true,
1176 }, {
1177 desc: "BytesValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001178 input: &wrapperspb.BytesValue{Value: []byte("hello")},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001179 want: `"aGVsbG8="`,
1180 }, {
1181 desc: "Empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001182 input: &emptypb.Empty{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001183 want: `{}`,
1184 }, {
Herbie Ong300b9fe2019-03-29 15:42:20 -07001185 desc: "NullValue field",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001186 input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
Herbie Ong300b9fe2019-03-29 15:42:20 -07001187 want: `{
1188 "optNull": null
1189}`,
1190 }, {
Herbie Ong1c7462c2019-03-22 17:56:55 -07001191 desc: "Value empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001192 input: &structpb.Value{},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001193 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001194 }, {
1195 desc: "Value empty field",
1196 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001197 OptValue: &structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001198 },
Herbie Ong1c7462c2019-03-22 17:56:55 -07001199 wantErr: true,
Herbie Ong0b0f4032019-03-18 19:06:15 -07001200 }, {
1201 desc: "Value contains NullValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001202 input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001203 want: `null`,
1204 }, {
1205 desc: "Value contains BoolValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001206 input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001207 want: `false`,
1208 }, {
1209 desc: "Value contains NumberValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001210 input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001211 want: `1.02`,
1212 }, {
1213 desc: "Value contains StringValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001214 input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001215 want: `"hello"`,
1216 }, {
1217 desc: "Value contains StringValue with invalid UTF8",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001218 input: &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001219 want: "\"\xff\"",
1220 wantErr: true,
1221 }, {
1222 desc: "Value contains Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001223 input: &structpb.Value{
1224 Kind: &structpb.Value_StructValue{
1225 &structpb.Struct{
1226 Fields: map[string]*structpb.Value{
1227 "null": {Kind: &structpb.Value_NullValue{}},
1228 "number": {Kind: &structpb.Value_NumberValue{}},
1229 "string": {Kind: &structpb.Value_StringValue{}},
1230 "struct": {Kind: &structpb.Value_StructValue{}},
1231 "list": {Kind: &structpb.Value_ListValue{}},
1232 "bool": {Kind: &structpb.Value_BoolValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001233 },
1234 },
1235 },
1236 },
1237 want: `{
1238 "bool": false,
1239 "list": [],
1240 "null": null,
1241 "number": 0,
1242 "string": "",
1243 "struct": {}
1244}`,
1245 }, {
1246 desc: "Value contains ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001247 input: &structpb.Value{
1248 Kind: &structpb.Value_ListValue{
1249 &structpb.ListValue{
1250 Values: []*structpb.Value{
1251 {Kind: &structpb.Value_BoolValue{}},
1252 {Kind: &structpb.Value_NullValue{}},
1253 {Kind: &structpb.Value_NumberValue{}},
1254 {Kind: &structpb.Value_StringValue{}},
1255 {Kind: &structpb.Value_StructValue{}},
1256 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001257 },
1258 },
1259 },
1260 },
1261 want: `[
1262 false,
1263 null,
1264 0,
1265 "",
1266 {},
1267 []
1268]`,
1269 }, {
1270 desc: "Struct with nil map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001271 input: &structpb.Struct{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001272 want: `{}`,
1273 }, {
1274 desc: "Struct with empty map",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001275 input: &structpb.Struct{
1276 Fields: map[string]*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001277 },
1278 want: `{}`,
1279 }, {
1280 desc: "Struct",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001281 input: &structpb.Struct{
1282 Fields: map[string]*structpb.Value{
1283 "bool": {Kind: &structpb.Value_BoolValue{true}},
1284 "null": {Kind: &structpb.Value_NullValue{}},
1285 "number": {Kind: &structpb.Value_NumberValue{3.1415}},
1286 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001287 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001288 Kind: &structpb.Value_StructValue{
1289 &structpb.Struct{
1290 Fields: map[string]*structpb.Value{
1291 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001292 },
1293 },
1294 },
1295 },
1296 "list": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001297 Kind: &structpb.Value_ListValue{
1298 &structpb.ListValue{
1299 Values: []*structpb.Value{
1300 {Kind: &structpb.Value_BoolValue{}},
1301 {Kind: &structpb.Value_NullValue{}},
1302 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001303 },
1304 },
1305 },
1306 },
1307 },
1308 },
1309 want: `{
1310 "bool": true,
1311 "list": [
1312 false,
1313 null,
1314 0
1315 ],
1316 "null": null,
1317 "number": 3.1415,
1318 "string": "hello",
1319 "struct": {
1320 "string": "world"
1321 }
1322}`,
1323 }, {
1324 desc: "Struct message with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001325 input: &structpb.Struct{
1326 Fields: map[string]*structpb.Value{
1327 "string": {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001328 },
1329 },
1330 want: "{\n \"string\": \"\xff\"\n}",
1331 wantErr: true,
1332 }, {
1333 desc: "ListValue with nil values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001334 input: &structpb.ListValue{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001335 want: `[]`,
1336 }, {
1337 desc: "ListValue with empty values",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001338 input: &structpb.ListValue{
1339 Values: []*structpb.Value{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001340 },
1341 want: `[]`,
1342 }, {
1343 desc: "ListValue",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001344 input: &structpb.ListValue{
1345 Values: []*structpb.Value{
1346 {Kind: &structpb.Value_BoolValue{true}},
1347 {Kind: &structpb.Value_NullValue{}},
1348 {Kind: &structpb.Value_NumberValue{3.1415}},
1349 {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001350 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001351 Kind: &structpb.Value_ListValue{
1352 &structpb.ListValue{
1353 Values: []*structpb.Value{
1354 {Kind: &structpb.Value_BoolValue{}},
1355 {Kind: &structpb.Value_NullValue{}},
1356 {Kind: &structpb.Value_NumberValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001357 },
1358 },
1359 },
1360 },
1361 {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001362 Kind: &structpb.Value_StructValue{
1363 &structpb.Struct{
1364 Fields: map[string]*structpb.Value{
1365 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001366 },
1367 },
1368 },
1369 },
1370 },
1371 },
1372 want: `[
1373 true,
1374 null,
1375 3.1415,
1376 "hello",
1377 [
1378 false,
1379 null,
1380 0
1381 ],
1382 {
1383 "string": "world"
1384 }
1385]`,
1386 }, {
1387 desc: "ListValue with invalid UTF8 string",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001388 input: &structpb.ListValue{
1389 Values: []*structpb.Value{
1390 {Kind: &structpb.Value_StringValue{"\xff"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001391 },
1392 },
1393 want: "[\n \"\xff\"\n]",
1394 wantErr: true,
1395 }, {
1396 desc: "Duration empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001397 input: &durationpb.Duration{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001398 want: `"0s"`,
1399 }, {
1400 desc: "Duration with secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001401 input: &durationpb.Duration{Seconds: 3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001402 want: `"3s"`,
1403 }, {
1404 desc: "Duration with -secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001405 input: &durationpb.Duration{Seconds: -3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001406 want: `"-3s"`,
1407 }, {
1408 desc: "Duration with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001409 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001410 want: `"0.001s"`,
1411 }, {
1412 desc: "Duration with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001413 input: &durationpb.Duration{Nanos: -1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001414 want: `"-0.001s"`,
1415 }, {
1416 desc: "Duration with large secs",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001417 input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001418 want: `"10000000000.000000001s"`,
1419 }, {
1420 desc: "Duration with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001421 input: &durationpb.Duration{Nanos: 1e4},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001422 want: `"0.000010s"`,
1423 }, {
1424 desc: "Duration with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001425 input: &durationpb.Duration{Nanos: 1e6},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001426 want: `"0.001s"`,
1427 }, {
1428 desc: "Duration with -secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001429 input: &durationpb.Duration{Seconds: -123, Nanos: -450},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001430 want: `"-123.000000450s"`,
1431 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001432 desc: "Duration max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001433 input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001434 want: `"315576000000.999999999s"`,
1435 }, {
1436 desc: "Duration min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001437 input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001438 want: `"-315576000000.999999999s"`,
1439 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001440 desc: "Duration with +secs -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001441 input: &durationpb.Duration{Seconds: 1, Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001442 wantErr: true,
1443 }, {
1444 desc: "Duration with -secs +nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001445 input: &durationpb.Duration{Seconds: -1, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001446 wantErr: true,
1447 }, {
1448 desc: "Duration with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001449 input: &durationpb.Duration{Seconds: 315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001450 wantErr: true,
1451 }, {
1452 desc: "Duration with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001453 input: &durationpb.Duration{Seconds: -315576000001},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001454 wantErr: true,
1455 }, {
1456 desc: "Duration with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001457 input: &durationpb.Duration{Seconds: 0, Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001458 wantErr: true,
1459 }, {
1460 desc: "Duration with -nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001461 input: &durationpb.Duration{Seconds: 0, Nanos: -1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001462 wantErr: true,
1463 }, {
1464 desc: "Timestamp zero",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001465 input: &timestamppb.Timestamp{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001466 want: `"1970-01-01T00:00:00Z"`,
1467 }, {
1468 desc: "Timestamp",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001469 input: &timestamppb.Timestamp{Seconds: 1553036601},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001470 want: `"2019-03-19T23:03:21Z"`,
1471 }, {
1472 desc: "Timestamp with nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001473 input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001474 want: `"2019-03-19T23:03:21.000000001Z"`,
1475 }, {
1476 desc: "Timestamp with 6-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001477 input: &timestamppb.Timestamp{Nanos: 1e3},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001478 want: `"1970-01-01T00:00:00.000001Z"`,
1479 }, {
1480 desc: "Timestamp with 3-digit nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001481 input: &timestamppb.Timestamp{Nanos: 1e7},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001482 want: `"1970-01-01T00:00:00.010Z"`,
1483 }, {
Herbie Ongad9c1252019-04-24 20:51:28 -07001484 desc: "Timestamp max value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001485 input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
Herbie Ongad9c1252019-04-24 20:51:28 -07001486 want: `"9999-12-31T23:59:59.999999999Z"`,
1487 }, {
1488 desc: "Timestamp min value",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001489 input: &timestamppb.Timestamp{Seconds: -62135596800},
Herbie Ongad9c1252019-04-24 20:51:28 -07001490 want: `"0001-01-01T00:00:00Z"`,
1491 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001492 desc: "Timestamp with +secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001493 input: &timestamppb.Timestamp{Seconds: 253402300800},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001494 wantErr: true,
1495 }, {
1496 desc: "Timestamp with -secs out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001497 input: &timestamppb.Timestamp{Seconds: -62135596801},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001498 wantErr: true,
1499 }, {
1500 desc: "Timestamp with -nanos",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001501 input: &timestamppb.Timestamp{Nanos: -1},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001502 wantErr: true,
1503 }, {
1504 desc: "Timestamp with +nanos out of range",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001505 input: &timestamppb.Timestamp{Nanos: 1e9},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001506 wantErr: true,
1507 }, {
1508 desc: "FieldMask empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001509 input: &fieldmaskpb.FieldMask{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001510 want: `""`,
1511 }, {
1512 desc: "FieldMask",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001513 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001514 Paths: []string{
1515 "foo",
1516 "foo_bar",
1517 "foo.bar_qux",
1518 "_foo",
1519 },
1520 },
1521 want: `"foo,fooBar,foo.barQux,Foo"`,
1522 }, {
1523 desc: "FieldMask error 1",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001524 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001525 Paths: []string{"foo_"},
1526 },
1527 wantErr: true,
1528 }, {
1529 desc: "FieldMask error 2",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001530 input: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001531 Paths: []string{"foo__bar"},
1532 },
1533 wantErr: true,
1534 }, {
1535 desc: "Any empty",
Joe Tsaia95b29f2019-05-16 12:47:20 -07001536 input: &anypb.Any{},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001537 want: `{}`,
1538 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001539 desc: "Any with non-custom message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001540 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001541 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001542 },
1543 input: func() proto.Message {
1544 m := &pb2.Nested{
1545 OptString: scalar.String("embedded inside Any"),
1546 OptNested: &pb2.Nested{
1547 OptString: scalar.String("inception"),
1548 },
1549 }
1550 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1551 if err != nil {
1552 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1553 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001554 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001555 TypeUrl: "foo/pb2.Nested",
1556 Value: b,
1557 }
1558 }(),
1559 want: `{
1560 "@type": "foo/pb2.Nested",
1561 "optString": "embedded inside Any",
1562 "optNested": {
1563 "optString": "inception"
1564 }
1565}`,
1566 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001567 desc: "Any with empty embedded message",
Damien Neil5c5b5312019-05-14 12:44:37 -07001568 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001569 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001570 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001571 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001572 want: `{
1573 "@type": "foo/pb2.Nested"
1574}`,
1575 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001576 desc: "Any without registered type",
Damien Neil5c5b5312019-05-14 12:44:37 -07001577 mo: protojson.MarshalOptions{Resolver: preg.NewTypes()},
Joe Tsaia95b29f2019-05-16 12:47:20 -07001578 input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001579 wantErr: true,
1580 }, {
1581 desc: "Any with missing required error",
Damien Neil5c5b5312019-05-14 12:44:37 -07001582 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001583 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001584 },
1585 input: func() proto.Message {
1586 m := &pb2.PartialRequired{
1587 OptString: scalar.String("embedded inside Any"),
1588 }
Damien Neil96c229a2019-04-03 12:17:24 -07001589 b, err := proto.MarshalOptions{
1590 AllowPartial: true,
1591 Deterministic: true,
1592 }.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001593 if err != nil {
1594 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1595 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001596 return &anypb.Any{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001597 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001598 Value: b,
1599 }
1600 }(),
1601 want: `{
1602 "@type": "pb2.PartialRequired",
1603 "optString": "embedded inside Any"
1604}`,
1605 wantErr: true,
1606 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001607 desc: "Any with partial required and AllowPartial",
Damien Neil5c5b5312019-05-14 12:44:37 -07001608 mo: protojson.MarshalOptions{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001609 AllowPartial: true,
Joe Tsai0fc49f82019-05-01 12:29:25 -07001610 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.PartialRequired{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001611 },
1612 input: func() proto.Message {
1613 m := &pb2.PartialRequired{
1614 OptString: scalar.String("embedded inside Any"),
1615 }
Damien Neil96c229a2019-04-03 12:17:24 -07001616 b, err := proto.MarshalOptions{
1617 AllowPartial: true,
1618 Deterministic: true,
1619 }.Marshal(m)
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001620 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{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001624 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001625 Value: b,
1626 }
1627 }(),
1628 want: `{
1629 "@type": "pb2.PartialRequired",
1630 "optString": "embedded inside Any"
1631}`,
1632 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001633 desc: "Any with invalid UTF8",
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 },
1637 input: func() proto.Message {
1638 m := &pb2.Nested{
1639 OptString: scalar.String("abc\xff"),
1640 }
1641 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1642 if err != nil {
1643 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1644 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001645 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001646 TypeUrl: "foo/pb2.Nested",
1647 Value: b,
1648 }
1649 }(),
1650 want: `{
1651 "@type": "foo/pb2.Nested",
1652 "optString": "` + "abc\xff" + `"
1653}`,
1654 wantErr: true,
1655 }, {
1656 desc: "Any with invalid value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001657 mo: protojson.MarshalOptions{
Joe Tsai0fc49f82019-05-01 12:29:25 -07001658 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&pb2.Nested{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001659 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001660 input: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001661 TypeUrl: "foo/pb2.Nested",
1662 Value: dhex("80"),
1663 },
1664 wantErr: true,
1665 }, {
1666 desc: "Any with BoolValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001667 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001668 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001669 },
1670 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001671 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001672 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1673 if err != nil {
1674 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1675 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001676 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001677 TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1678 Value: b,
1679 }
1680 }(),
1681 want: `{
1682 "@type": "type.googleapis.com/google.protobuf.BoolValue",
1683 "value": true
1684}`,
1685 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001686 desc: "Any with Empty",
Damien Neil5c5b5312019-05-14 12:44:37 -07001687 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001688 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001689 },
1690 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001691 m := &emptypb.Empty{}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001692 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1693 if err != nil {
1694 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1695 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001696 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001697 TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1698 Value: b,
1699 }
1700 }(),
1701 want: `{
Herbie Ong82014a52019-03-27 15:21:43 -07001702 "@type": "type.googleapis.com/google.protobuf.Empty",
1703 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001704}`,
1705 }, {
1706 desc: "Any with StringValue containing invalid UTF8",
Damien Neil5c5b5312019-05-14 12:44:37 -07001707 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001708 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001709 },
1710 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001711 m := &wrapperspb.StringValue{Value: "abcd"}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001712 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1713 if err != nil {
1714 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1715 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001716 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001717 TypeUrl: "google.protobuf.StringValue",
Damien Neilbc310b52019-04-11 11:46:55 -07001718 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001719 }
1720 }(),
1721 want: `{
1722 "@type": "google.protobuf.StringValue",
1723 "value": "` + "abc\xff" + `"
1724}`,
1725 wantErr: true,
1726 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001727 desc: "Any with Int64Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001728 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001729 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.Int64Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001730 },
1731 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001732 m := &wrapperspb.Int64Value{Value: 42}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001733 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1734 if err != nil {
1735 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1736 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001737 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001738 TypeUrl: "google.protobuf.Int64Value",
1739 Value: b,
1740 }
1741 }(),
1742 want: `{
1743 "@type": "google.protobuf.Int64Value",
1744 "value": "42"
1745}`,
1746 }, {
1747 desc: "Any with Duration",
Damien Neil5c5b5312019-05-14 12:44:37 -07001748 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001749 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&durationpb.Duration{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001750 },
1751 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001752 m := &durationpb.Duration{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001753 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1754 if err != nil {
1755 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1756 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001757 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001758 TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1759 Value: b,
1760 }
1761 }(),
1762 want: `{
1763 "@type": "type.googleapis.com/google.protobuf.Duration",
1764 "value": "0s"
1765}`,
1766 }, {
1767 desc: "Any with empty Value",
Damien Neil5c5b5312019-05-14 12:44:37 -07001768 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001769 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001770 },
1771 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001772 m := &structpb.Value{}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001773 b, err := proto.Marshal(m)
1774 if err != nil {
1775 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1776 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001777 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001778 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1779 Value: b,
1780 }
1781 }(),
1782 wantErr: true,
1783 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001784 desc: "Any with Value of StringValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001785 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001786 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001787 },
1788 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001789 m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001790 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1791 if err != nil {
1792 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1793 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001794 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001795 TypeUrl: "type.googleapis.com/google.protobuf.Value",
Damien Neilbc310b52019-04-11 11:46:55 -07001796 Value: bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001797 }
1798 }(),
1799 want: `{
1800 "@type": "type.googleapis.com/google.protobuf.Value",
1801 "value": "` + "abc\xff" + `"
1802}`,
1803 wantErr: true,
1804 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001805 desc: "Any with Value of NullValue",
Damien Neil5c5b5312019-05-14 12:44:37 -07001806 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001807 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&structpb.Value{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001808 },
1809 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001810 m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001811 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001812 if err != nil {
1813 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1814 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001815 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001816 TypeUrl: "type.googleapis.com/google.protobuf.Value",
1817 Value: b,
1818 }
1819 }(),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001820 want: `{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001821 "@type": "type.googleapis.com/google.protobuf.Value",
1822 "value": null
Herbie Ong0b0f4032019-03-18 19:06:15 -07001823}`,
1824 }, {
1825 desc: "Any with Struct",
Damien Neil5c5b5312019-05-14 12:44:37 -07001826 mo: protojson.MarshalOptions{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001827 Resolver: preg.NewTypes(
Joe Tsaia95b29f2019-05-16 12:47:20 -07001828 pimpl.Export{}.MessageTypeOf(&structpb.Struct{}),
1829 pimpl.Export{}.MessageTypeOf(&structpb.Value{}),
1830 pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{}),
1831 pimpl.Export{}.EnumTypeOf(structpb.NullValue_NULL_VALUE),
1832 pimpl.Export{}.MessageTypeOf(&wrapperspb.StringValue{}),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001833 ),
1834 },
1835 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001836 m := &structpb.Struct{
1837 Fields: map[string]*structpb.Value{
1838 "bool": {Kind: &structpb.Value_BoolValue{true}},
1839 "null": {Kind: &structpb.Value_NullValue{}},
1840 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001841 "struct": {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001842 Kind: &structpb.Value_StructValue{
1843 &structpb.Struct{
1844 Fields: map[string]*structpb.Value{
1845 "string": {Kind: &structpb.Value_StringValue{"world"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001846 },
1847 },
1848 },
1849 },
1850 },
1851 }
1852 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1853 if err != nil {
1854 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1855 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001856 return &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001857 TypeUrl: "google.protobuf.Struct",
1858 Value: b,
1859 }
1860 }(),
1861 want: `{
1862 "@type": "google.protobuf.Struct",
1863 "value": {
1864 "bool": true,
1865 "null": null,
1866 "string": "hello",
1867 "struct": {
1868 "string": "world"
1869 }
1870 }
1871}`,
1872 }, {
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001873 desc: "Any with missing type_url",
Damien Neil5c5b5312019-05-14 12:44:37 -07001874 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001875 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&wrapperspb.BoolValue{})),
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001876 },
1877 input: func() proto.Message {
Joe Tsaia95b29f2019-05-16 12:47:20 -07001878 m := &wrapperspb.BoolValue{Value: true}
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001879 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1880 if err != nil {
1881 t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1882 }
Joe Tsaia95b29f2019-05-16 12:47:20 -07001883 return &anypb.Any{
Herbie Ong8ac9dd22019-03-27 12:20:50 -07001884 Value: b,
1885 }
1886 }(),
1887 wantErr: true,
1888 }, {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001889 desc: "well known types as field values",
Damien Neil5c5b5312019-05-14 12:44:37 -07001890 mo: protojson.MarshalOptions{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001891 Resolver: preg.NewTypes(pimpl.Export{}.MessageTypeOf(&emptypb.Empty{})),
Herbie Ong0b0f4032019-03-18 19:06:15 -07001892 },
1893 input: &pb2.KnownTypes{
Joe Tsaia95b29f2019-05-16 12:47:20 -07001894 OptBool: &wrapperspb.BoolValue{Value: false},
1895 OptInt32: &wrapperspb.Int32Value{Value: 42},
1896 OptInt64: &wrapperspb.Int64Value{Value: 42},
1897 OptUint32: &wrapperspb.UInt32Value{Value: 42},
1898 OptUint64: &wrapperspb.UInt64Value{Value: 42},
1899 OptFloat: &wrapperspb.FloatValue{Value: 1.23},
1900 OptDouble: &wrapperspb.DoubleValue{Value: 3.1415},
1901 OptString: &wrapperspb.StringValue{Value: "hello"},
1902 OptBytes: &wrapperspb.BytesValue{Value: []byte("hello")},
1903 OptDuration: &durationpb.Duration{Seconds: 123},
1904 OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1905 OptStruct: &structpb.Struct{
1906 Fields: map[string]*structpb.Value{
1907 "string": {Kind: &structpb.Value_StringValue{"hello"}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001908 },
1909 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001910 OptList: &structpb.ListValue{
1911 Values: []*structpb.Value{
1912 {Kind: &structpb.Value_NullValue{}},
1913 {Kind: &structpb.Value_StringValue{}},
1914 {Kind: &structpb.Value_StructValue{}},
1915 {Kind: &structpb.Value_ListValue{}},
Herbie Ong0b0f4032019-03-18 19:06:15 -07001916 },
1917 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001918 OptValue: &structpb.Value{
1919 Kind: &structpb.Value_StringValue{"world"},
Herbie Ong1c7462c2019-03-22 17:56:55 -07001920 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001921 OptEmpty: &emptypb.Empty{},
1922 OptAny: &anypb.Any{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001923 TypeUrl: "google.protobuf.Empty",
1924 },
Joe Tsaia95b29f2019-05-16 12:47:20 -07001925 OptFieldmask: &fieldmaskpb.FieldMask{
Herbie Ong0b0f4032019-03-18 19:06:15 -07001926 Paths: []string{"foo_bar", "bar_foo"},
1927 },
1928 },
1929 want: `{
1930 "optBool": false,
1931 "optInt32": 42,
1932 "optInt64": "42",
1933 "optUint32": 42,
1934 "optUint64": "42",
1935 "optFloat": 1.23,
1936 "optDouble": 3.1415,
1937 "optString": "hello",
1938 "optBytes": "aGVsbG8=",
1939 "optDuration": "123s",
1940 "optTimestamp": "2019-03-19T23:03:21Z",
1941 "optStruct": {
1942 "string": "hello"
1943 },
1944 "optList": [
1945 null,
1946 "",
1947 {},
1948 []
1949 ],
Herbie Ong1c7462c2019-03-22 17:56:55 -07001950 "optValue": "world",
Herbie Ong0b0f4032019-03-18 19:06:15 -07001951 "optEmpty": {},
1952 "optAny": {
Herbie Ong82014a52019-03-27 15:21:43 -07001953 "@type": "google.protobuf.Empty",
1954 "value": {}
Herbie Ong0b0f4032019-03-18 19:06:15 -07001955 },
1956 "optFieldmask": "fooBar,barFoo"
1957}`,
Herbie Ong7b828bc2019-02-08 19:56:24 -08001958 }}
1959
1960 for _, tt := range tests {
1961 tt := tt
1962 t.Run(tt.desc, func(t *testing.T) {
Herbie Ong0b0f4032019-03-18 19:06:15 -07001963 // Use 2-space indentation on all MarshalOptions.
1964 tt.mo.Indent = " "
Herbie Ong7b828bc2019-02-08 19:56:24 -08001965 b, err := tt.mo.Marshal(tt.input)
Herbie Ong0b0f4032019-03-18 19:06:15 -07001966 if err != nil && !tt.wantErr {
Herbie Ong7b828bc2019-02-08 19:56:24 -08001967 t.Errorf("Marshal() returned error: %v\n", err)
1968 }
Herbie Ong0b0f4032019-03-18 19:06:15 -07001969 if err == nil && tt.wantErr {
1970 t.Errorf("Marshal() got nil error, want error\n")
1971 }
Herbie Ong7b828bc2019-02-08 19:56:24 -08001972 got := string(b)
1973 if got != tt.want {
1974 t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
1975 if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
1976 t.Errorf("Marshal() diff -want +got\n%v\n", diff)
1977 }
1978 }
1979 })
1980 }
1981}