xref: /aosp_15_r20/external/golang-protobuf/encoding/protojson/encode_test.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package protojson_test
6
7import (
8	"bytes"
9	"math"
10	"testing"
11
12	"github.com/google/go-cmp/cmp"
13
14	"google.golang.org/protobuf/encoding/protojson"
15	"google.golang.org/protobuf/internal/detrand"
16	"google.golang.org/protobuf/internal/flags"
17	"google.golang.org/protobuf/proto"
18	"google.golang.org/protobuf/reflect/protoregistry"
19	"google.golang.org/protobuf/testing/protopack"
20
21	pb2 "google.golang.org/protobuf/internal/testprotos/textpb2"
22	pb3 "google.golang.org/protobuf/internal/testprotos/textpb3"
23	"google.golang.org/protobuf/types/known/anypb"
24	"google.golang.org/protobuf/types/known/durationpb"
25	"google.golang.org/protobuf/types/known/emptypb"
26	"google.golang.org/protobuf/types/known/fieldmaskpb"
27	"google.golang.org/protobuf/types/known/structpb"
28	"google.golang.org/protobuf/types/known/timestamppb"
29	"google.golang.org/protobuf/types/known/wrapperspb"
30)
31
32// Disable detrand to enable direct comparisons on outputs.
33func init() { detrand.Disable() }
34
35func TestMarshal(t *testing.T) {
36	tests := []struct {
37		desc    string
38		mo      protojson.MarshalOptions
39		input   proto.Message
40		want    string
41		wantErr bool // TODO: Verify error message substring.
42		skip    bool
43	}{{
44		desc:  "proto2 optional scalars not set",
45		input: &pb2.Scalars{},
46		want:  "{}",
47	}, {
48		desc:  "proto3 scalars not set",
49		input: &pb3.Scalars{},
50		want:  "{}",
51	}, {
52		desc:  "proto3 optional not set",
53		input: &pb3.Proto3Optional{},
54		want:  "{}",
55	}, {
56		desc: "proto2 optional scalars set to zero values",
57		input: &pb2.Scalars{
58			OptBool:     proto.Bool(false),
59			OptInt32:    proto.Int32(0),
60			OptInt64:    proto.Int64(0),
61			OptUint32:   proto.Uint32(0),
62			OptUint64:   proto.Uint64(0),
63			OptSint32:   proto.Int32(0),
64			OptSint64:   proto.Int64(0),
65			OptFixed32:  proto.Uint32(0),
66			OptFixed64:  proto.Uint64(0),
67			OptSfixed32: proto.Int32(0),
68			OptSfixed64: proto.Int64(0),
69			OptFloat:    proto.Float32(0),
70			OptDouble:   proto.Float64(0),
71			OptBytes:    []byte{},
72			OptString:   proto.String(""),
73		},
74		want: `{
75  "optBool": false,
76  "optInt32": 0,
77  "optInt64": "0",
78  "optUint32": 0,
79  "optUint64": "0",
80  "optSint32": 0,
81  "optSint64": "0",
82  "optFixed32": 0,
83  "optFixed64": "0",
84  "optSfixed32": 0,
85  "optSfixed64": "0",
86  "optFloat": 0,
87  "optDouble": 0,
88  "optBytes": "",
89  "optString": ""
90}`,
91	}, {
92		desc: "proto3 optional set to zero values",
93		input: &pb3.Proto3Optional{
94			OptBool:    proto.Bool(false),
95			OptInt32:   proto.Int32(0),
96			OptInt64:   proto.Int64(0),
97			OptUint32:  proto.Uint32(0),
98			OptUint64:  proto.Uint64(0),
99			OptFloat:   proto.Float32(0),
100			OptDouble:  proto.Float64(0),
101			OptString:  proto.String(""),
102			OptBytes:   []byte{},
103			OptEnum:    pb3.Enum_ZERO.Enum(),
104			OptMessage: &pb3.Nested{},
105		},
106		want: `{
107  "optBool": false,
108  "optInt32": 0,
109  "optInt64": "0",
110  "optUint32": 0,
111  "optUint64": "0",
112  "optFloat": 0,
113  "optDouble": 0,
114  "optString": "",
115  "optBytes": "",
116  "optEnum": "ZERO",
117  "optMessage": {}
118}`,
119	}, {
120		desc: "proto2 optional scalars set to some values",
121		input: &pb2.Scalars{
122			OptBool:     proto.Bool(true),
123			OptInt32:    proto.Int32(0xff),
124			OptInt64:    proto.Int64(0xdeadbeef),
125			OptUint32:   proto.Uint32(47),
126			OptUint64:   proto.Uint64(0xdeadbeef),
127			OptSint32:   proto.Int32(-1001),
128			OptSint64:   proto.Int64(-0xffff),
129			OptFixed64:  proto.Uint64(64),
130			OptSfixed32: proto.Int32(-32),
131			OptFloat:    proto.Float32(1.02),
132			OptDouble:   proto.Float64(1.234),
133			OptBytes:    []byte("谷歌"),
134			OptString:   proto.String("谷歌"),
135		},
136		want: `{
137  "optBool": true,
138  "optInt32": 255,
139  "optInt64": "3735928559",
140  "optUint32": 47,
141  "optUint64": "3735928559",
142  "optSint32": -1001,
143  "optSint64": "-65535",
144  "optFixed64": "64",
145  "optSfixed32": -32,
146  "optFloat": 1.02,
147  "optDouble": 1.234,
148  "optBytes": "6LC35q2M",
149  "optString": "谷歌"
150}`,
151	}, {
152		desc: "string",
153		input: &pb3.Scalars{
154			SString: "谷歌",
155		},
156		want: `{
157  "sString": "谷歌"
158}`,
159	}, {
160		desc: "string with invalid UTF8",
161		input: &pb3.Scalars{
162			SString: "abc\xff",
163		},
164		wantErr: true,
165	}, {
166		desc: "float nan",
167		input: &pb3.Scalars{
168			SFloat: float32(math.NaN()),
169		},
170		want: `{
171  "sFloat": "NaN"
172}`,
173	}, {
174		desc: "float positive infinity",
175		input: &pb3.Scalars{
176			SFloat: float32(math.Inf(1)),
177		},
178		want: `{
179  "sFloat": "Infinity"
180}`,
181	}, {
182		desc: "float negative infinity",
183		input: &pb3.Scalars{
184			SFloat: float32(math.Inf(-1)),
185		},
186		want: `{
187  "sFloat": "-Infinity"
188}`,
189	}, {
190		desc: "double nan",
191		input: &pb3.Scalars{
192			SDouble: math.NaN(),
193		},
194		want: `{
195  "sDouble": "NaN"
196}`,
197	}, {
198		desc: "double positive infinity",
199		input: &pb3.Scalars{
200			SDouble: math.Inf(1),
201		},
202		want: `{
203  "sDouble": "Infinity"
204}`,
205	}, {
206		desc: "double negative infinity",
207		input: &pb3.Scalars{
208			SDouble: math.Inf(-1),
209		},
210		want: `{
211  "sDouble": "-Infinity"
212}`,
213	}, {
214		desc:  "proto2 enum not set",
215		input: &pb2.Enums{},
216		want:  "{}",
217	}, {
218		desc: "proto2 enum set to zero value",
219		input: &pb2.Enums{
220			OptEnum:       pb2.Enum(0).Enum(),
221			OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
222		},
223		want: `{
224  "optEnum": 0,
225  "optNestedEnum": 0
226}`,
227	}, {
228		desc: "proto2 enum",
229		input: &pb2.Enums{
230			OptEnum:       pb2.Enum_ONE.Enum(),
231			OptNestedEnum: pb2.Enums_UNO.Enum(),
232		},
233		want: `{
234  "optEnum": "ONE",
235  "optNestedEnum": "UNO"
236}`,
237	}, {
238		desc: "proto2 enum set to numeric values",
239		input: &pb2.Enums{
240			OptEnum:       pb2.Enum(2).Enum(),
241			OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
242		},
243		want: `{
244  "optEnum": "TWO",
245  "optNestedEnum": "DOS"
246}`,
247	}, {
248		desc: "proto2 enum set to unnamed numeric values",
249		input: &pb2.Enums{
250			OptEnum:       pb2.Enum(101).Enum(),
251			OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
252		},
253		want: `{
254  "optEnum": 101,
255  "optNestedEnum": -101
256}`,
257	}, {
258		desc:  "proto3 enum not set",
259		input: &pb3.Enums{},
260		want:  "{}",
261	}, {
262		desc: "proto3 enum set to zero value",
263		input: &pb3.Enums{
264			SEnum:       pb3.Enum_ZERO,
265			SNestedEnum: pb3.Enums_CERO,
266		},
267		want: "{}",
268	}, {
269		desc: "proto3 enum",
270		input: &pb3.Enums{
271			SEnum:       pb3.Enum_ONE,
272			SNestedEnum: pb3.Enums_UNO,
273		},
274		want: `{
275  "sEnum": "ONE",
276  "sNestedEnum": "UNO"
277}`,
278	}, {
279		desc: "proto3 enum set to numeric values",
280		input: &pb3.Enums{
281			SEnum:       2,
282			SNestedEnum: 2,
283		},
284		want: `{
285  "sEnum": "TWO",
286  "sNestedEnum": "DOS"
287}`,
288	}, {
289		desc: "proto3 enum set to unnamed numeric values",
290		input: &pb3.Enums{
291			SEnum:       -47,
292			SNestedEnum: 47,
293		},
294		want: `{
295  "sEnum": -47,
296  "sNestedEnum": 47
297}`,
298	}, {
299		desc:  "proto2 nested message not set",
300		input: &pb2.Nests{},
301		want:  "{}",
302	}, {
303		desc: "proto2 nested message set to empty",
304		input: &pb2.Nests{
305			OptNested: &pb2.Nested{},
306			Optgroup:  &pb2.Nests_OptGroup{},
307		},
308		want: `{
309  "optNested": {},
310  "optgroup": {}
311}`,
312	}, {
313		desc: "proto2 nested messages",
314		input: &pb2.Nests{
315			OptNested: &pb2.Nested{
316				OptString: proto.String("nested message"),
317				OptNested: &pb2.Nested{
318					OptString: proto.String("another nested message"),
319				},
320			},
321		},
322		want: `{
323  "optNested": {
324    "optString": "nested message",
325    "optNested": {
326      "optString": "another nested message"
327    }
328  }
329}`,
330	}, {
331		desc: "proto2 groups",
332		input: &pb2.Nests{
333			Optgroup: &pb2.Nests_OptGroup{
334				OptString: proto.String("inside a group"),
335				OptNested: &pb2.Nested{
336					OptString: proto.String("nested message inside a group"),
337				},
338				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
339					OptFixed32: proto.Uint32(47),
340				},
341			},
342		},
343		want: `{
344  "optgroup": {
345    "optString": "inside a group",
346    "optNested": {
347      "optString": "nested message inside a group"
348    },
349    "optnestedgroup": {
350      "optFixed32": 47
351    }
352  }
353}`,
354	}, {
355		desc:  "proto3 nested message not set",
356		input: &pb3.Nests{},
357		want:  "{}",
358	}, {
359		desc: "proto3 nested message set to empty",
360		input: &pb3.Nests{
361			SNested: &pb3.Nested{},
362		},
363		want: `{
364  "sNested": {}
365}`,
366	}, {
367		desc: "proto3 nested message",
368		input: &pb3.Nests{
369			SNested: &pb3.Nested{
370				SString: "nested message",
371				SNested: &pb3.Nested{
372					SString: "another nested message",
373				},
374			},
375		},
376		want: `{
377  "sNested": {
378    "sString": "nested message",
379    "sNested": {
380      "sString": "another nested message"
381    }
382  }
383}`,
384	}, {
385		desc:  "oneof not set",
386		input: &pb3.Oneofs{},
387		want:  "{}",
388	}, {
389		desc: "oneof set to empty string",
390		input: &pb3.Oneofs{
391			Union: &pb3.Oneofs_OneofString{},
392		},
393		want: `{
394  "oneofString": ""
395}`,
396	}, {
397		desc: "oneof set to string",
398		input: &pb3.Oneofs{
399			Union: &pb3.Oneofs_OneofString{
400				OneofString: "hello",
401			},
402		},
403		want: `{
404  "oneofString": "hello"
405}`,
406	}, {
407		desc: "oneof set to enum",
408		input: &pb3.Oneofs{
409			Union: &pb3.Oneofs_OneofEnum{
410				OneofEnum: pb3.Enum_ZERO,
411			},
412		},
413		want: `{
414  "oneofEnum": "ZERO"
415}`,
416	}, {
417		desc: "oneof set to empty message",
418		input: &pb3.Oneofs{
419			Union: &pb3.Oneofs_OneofNested{
420				OneofNested: &pb3.Nested{},
421			},
422		},
423		want: `{
424  "oneofNested": {}
425}`,
426	}, {
427		desc: "oneof set to message",
428		input: &pb3.Oneofs{
429			Union: &pb3.Oneofs_OneofNested{
430				OneofNested: &pb3.Nested{
431					SString: "nested message",
432				},
433			},
434		},
435		want: `{
436  "oneofNested": {
437    "sString": "nested message"
438  }
439}`,
440	}, {
441		desc:  "repeated fields not set",
442		input: &pb2.Repeats{},
443		want:  "{}",
444	}, {
445		desc: "repeated fields set to empty slices",
446		input: &pb2.Repeats{
447			RptBool:   []bool{},
448			RptInt32:  []int32{},
449			RptInt64:  []int64{},
450			RptUint32: []uint32{},
451			RptUint64: []uint64{},
452			RptFloat:  []float32{},
453			RptDouble: []float64{},
454			RptBytes:  [][]byte{},
455		},
456		want: "{}",
457	}, {
458		desc: "repeated fields set to some values",
459		input: &pb2.Repeats{
460			RptBool:   []bool{true, false, true, true},
461			RptInt32:  []int32{1, 6, 0, 0},
462			RptInt64:  []int64{-64, 47},
463			RptUint32: []uint32{0xff, 0xffff},
464			RptUint64: []uint64{0xdeadbeef},
465			RptFloat:  []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
466			RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
467			RptString: []string{"hello", "世界"},
468			RptBytes: [][]byte{
469				[]byte("hello"),
470				[]byte("\xe4\xb8\x96\xe7\x95\x8c"),
471			},
472		},
473		want: `{
474  "rptBool": [
475    true,
476    false,
477    true,
478    true
479  ],
480  "rptInt32": [
481    1,
482    6,
483    0,
484    0
485  ],
486  "rptInt64": [
487    "-64",
488    "47"
489  ],
490  "rptUint32": [
491    255,
492    65535
493  ],
494  "rptUint64": [
495    "3735928559"
496  ],
497  "rptFloat": [
498    "NaN",
499    "Infinity",
500    "-Infinity",
501    1.034
502  ],
503  "rptDouble": [
504    "NaN",
505    "Infinity",
506    "-Infinity",
507    1.23e-308
508  ],
509  "rptString": [
510    "hello",
511    "世界"
512  ],
513  "rptBytes": [
514    "aGVsbG8=",
515    "5LiW55WM"
516  ]
517}`,
518	}, {
519		desc: "repeated enums",
520		input: &pb2.Enums{
521			RptEnum:       []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
522			RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
523		},
524		want: `{
525  "rptEnum": [
526    "ONE",
527    "TWO",
528    "TEN",
529    42
530  ],
531  "rptNestedEnum": [
532    "DOS",
533    47,
534    "DIEZ"
535  ]
536}`,
537	}, {
538		desc: "repeated messages set to empty",
539		input: &pb2.Nests{
540			RptNested: []*pb2.Nested{},
541			Rptgroup:  []*pb2.Nests_RptGroup{},
542		},
543		want: "{}",
544	}, {
545		desc: "repeated messages",
546		input: &pb2.Nests{
547			RptNested: []*pb2.Nested{
548				{
549					OptString: proto.String("repeat nested one"),
550				},
551				{
552					OptString: proto.String("repeat nested two"),
553					OptNested: &pb2.Nested{
554						OptString: proto.String("inside repeat nested two"),
555					},
556				},
557				{},
558			},
559		},
560		want: `{
561  "rptNested": [
562    {
563      "optString": "repeat nested one"
564    },
565    {
566      "optString": "repeat nested two",
567      "optNested": {
568        "optString": "inside repeat nested two"
569      }
570    },
571    {}
572  ]
573}`,
574	}, {
575		desc: "repeated messages contains nil value",
576		input: &pb2.Nests{
577			RptNested: []*pb2.Nested{nil, {}},
578		},
579		want: `{
580  "rptNested": [
581    {},
582    {}
583  ]
584}`,
585	}, {
586		desc: "repeated groups",
587		input: &pb2.Nests{
588			Rptgroup: []*pb2.Nests_RptGroup{
589				{
590					RptString: []string{"hello", "world"},
591				},
592				{},
593				nil,
594			},
595		},
596		want: `{
597  "rptgroup": [
598    {
599      "rptString": [
600        "hello",
601        "world"
602      ]
603    },
604    {},
605    {}
606  ]
607}`,
608	}, {
609		desc:  "map fields not set",
610		input: &pb3.Maps{},
611		want:  "{}",
612	}, {
613		desc: "map fields set to empty",
614		input: &pb3.Maps{
615			Int32ToStr:   map[int32]string{},
616			BoolToUint32: map[bool]uint32{},
617			Uint64ToEnum: map[uint64]pb3.Enum{},
618			StrToNested:  map[string]*pb3.Nested{},
619			StrToOneofs:  map[string]*pb3.Oneofs{},
620		},
621		want: "{}",
622	}, {
623		desc: "map fields 1",
624		input: &pb3.Maps{
625			BoolToUint32: map[bool]uint32{
626				true:  42,
627				false: 101,
628			},
629		},
630		want: `{
631  "boolToUint32": {
632    "false": 101,
633    "true": 42
634  }
635}`,
636	}, {
637		desc: "map fields 2",
638		input: &pb3.Maps{
639			Int32ToStr: map[int32]string{
640				-101: "-101",
641				0xff: "0xff",
642				0:    "zero",
643			},
644		},
645		want: `{
646  "int32ToStr": {
647    "-101": "-101",
648    "0": "zero",
649    "255": "0xff"
650  }
651}`,
652	}, {
653		desc: "map fields 3",
654		input: &pb3.Maps{
655			Uint64ToEnum: map[uint64]pb3.Enum{
656				1:  pb3.Enum_ONE,
657				2:  pb3.Enum_TWO,
658				10: pb3.Enum_TEN,
659				47: 47,
660			},
661		},
662		want: `{
663  "uint64ToEnum": {
664    "1": "ONE",
665    "2": "TWO",
666    "10": "TEN",
667    "47": 47
668  }
669}`,
670	}, {
671		desc: "map fields 4",
672		input: &pb3.Maps{
673			StrToNested: map[string]*pb3.Nested{
674				"nested": &pb3.Nested{
675					SString: "nested in a map",
676				},
677			},
678		},
679		want: `{
680  "strToNested": {
681    "nested": {
682      "sString": "nested in a map"
683    }
684  }
685}`,
686	}, {
687		desc: "map fields 5",
688		input: &pb3.Maps{
689			StrToOneofs: map[string]*pb3.Oneofs{
690				"string": &pb3.Oneofs{
691					Union: &pb3.Oneofs_OneofString{
692						OneofString: "hello",
693					},
694				},
695				"nested": &pb3.Oneofs{
696					Union: &pb3.Oneofs_OneofNested{
697						OneofNested: &pb3.Nested{
698							SString: "nested oneof in map field value",
699						},
700					},
701				},
702			},
703		},
704		want: `{
705  "strToOneofs": {
706    "nested": {
707      "oneofNested": {
708        "sString": "nested oneof in map field value"
709      }
710    },
711    "string": {
712      "oneofString": "hello"
713    }
714  }
715}`,
716	}, {
717		desc: "map field contains nil value",
718		input: &pb3.Maps{
719			StrToNested: map[string]*pb3.Nested{
720				"nil": nil,
721			},
722		},
723		want: `{
724  "strToNested": {
725    "nil": {}
726  }
727}`,
728	}, {
729		desc:    "required fields not set",
730		input:   &pb2.Requireds{},
731		want:    `{}`,
732		wantErr: true,
733	}, {
734		desc: "required fields partially set",
735		input: &pb2.Requireds{
736			ReqBool:     proto.Bool(false),
737			ReqSfixed64: proto.Int64(0),
738			ReqDouble:   proto.Float64(1.23),
739			ReqString:   proto.String("hello"),
740			ReqEnum:     pb2.Enum_ONE.Enum(),
741		},
742		want: `{
743  "reqBool": false,
744  "reqSfixed64": "0",
745  "reqDouble": 1.23,
746  "reqString": "hello",
747  "reqEnum": "ONE"
748}`,
749		wantErr: true,
750	}, {
751		desc: "required fields not set with AllowPartial",
752		mo:   protojson.MarshalOptions{AllowPartial: true},
753		input: &pb2.Requireds{
754			ReqBool:     proto.Bool(false),
755			ReqSfixed64: proto.Int64(0),
756			ReqDouble:   proto.Float64(1.23),
757			ReqString:   proto.String("hello"),
758			ReqEnum:     pb2.Enum_ONE.Enum(),
759		},
760		want: `{
761  "reqBool": false,
762  "reqSfixed64": "0",
763  "reqDouble": 1.23,
764  "reqString": "hello",
765  "reqEnum": "ONE"
766}`,
767	}, {
768		desc: "required fields all set",
769		input: &pb2.Requireds{
770			ReqBool:     proto.Bool(false),
771			ReqSfixed64: proto.Int64(0),
772			ReqDouble:   proto.Float64(1.23),
773			ReqString:   proto.String("hello"),
774			ReqEnum:     pb2.Enum_ONE.Enum(),
775			ReqNested:   &pb2.Nested{},
776		},
777		want: `{
778  "reqBool": false,
779  "reqSfixed64": "0",
780  "reqDouble": 1.23,
781  "reqString": "hello",
782  "reqEnum": "ONE",
783  "reqNested": {}
784}`,
785	}, {
786		desc: "indirect required field",
787		input: &pb2.IndirectRequired{
788			OptNested: &pb2.NestedWithRequired{},
789		},
790		want: `{
791  "optNested": {}
792}`,
793		wantErr: true,
794	}, {
795		desc: "indirect required field with AllowPartial",
796		mo:   protojson.MarshalOptions{AllowPartial: true},
797		input: &pb2.IndirectRequired{
798			OptNested: &pb2.NestedWithRequired{},
799		},
800		want: `{
801  "optNested": {}
802}`,
803	}, {
804		desc: "indirect required field in empty repeated",
805		input: &pb2.IndirectRequired{
806			RptNested: []*pb2.NestedWithRequired{},
807		},
808		want: `{}`,
809	}, {
810		desc: "indirect required field in repeated",
811		input: &pb2.IndirectRequired{
812			RptNested: []*pb2.NestedWithRequired{
813				&pb2.NestedWithRequired{},
814			},
815		},
816		want: `{
817  "rptNested": [
818    {}
819  ]
820}`,
821		wantErr: true,
822	}, {
823		desc: "indirect required field in repeated with AllowPartial",
824		mo:   protojson.MarshalOptions{AllowPartial: true},
825		input: &pb2.IndirectRequired{
826			RptNested: []*pb2.NestedWithRequired{
827				&pb2.NestedWithRequired{},
828			},
829		},
830		want: `{
831  "rptNested": [
832    {}
833  ]
834}`,
835	}, {
836		desc: "indirect required field in empty map",
837		input: &pb2.IndirectRequired{
838			StrToNested: map[string]*pb2.NestedWithRequired{},
839		},
840		want: "{}",
841	}, {
842		desc: "indirect required field in map",
843		input: &pb2.IndirectRequired{
844			StrToNested: map[string]*pb2.NestedWithRequired{
845				"fail": &pb2.NestedWithRequired{},
846			},
847		},
848		want: `{
849  "strToNested": {
850    "fail": {}
851  }
852}`,
853		wantErr: true,
854	}, {
855		desc: "indirect required field in map with AllowPartial",
856		mo:   protojson.MarshalOptions{AllowPartial: true},
857		input: &pb2.IndirectRequired{
858			StrToNested: map[string]*pb2.NestedWithRequired{
859				"fail": &pb2.NestedWithRequired{},
860			},
861		},
862		want: `{
863  "strToNested": {
864    "fail": {}
865  }
866}`,
867	}, {
868		desc: "indirect required field in oneof",
869		input: &pb2.IndirectRequired{
870			Union: &pb2.IndirectRequired_OneofNested{
871				OneofNested: &pb2.NestedWithRequired{},
872			},
873		},
874		want: `{
875  "oneofNested": {}
876}`,
877		wantErr: true,
878	}, {
879		desc: "indirect required field in oneof with AllowPartial",
880		mo:   protojson.MarshalOptions{AllowPartial: true},
881		input: &pb2.IndirectRequired{
882			Union: &pb2.IndirectRequired_OneofNested{
883				OneofNested: &pb2.NestedWithRequired{},
884			},
885		},
886		want: `{
887  "oneofNested": {}
888}`,
889	}, {
890		desc: "unknown fields are ignored",
891		input: func() proto.Message {
892			m := &pb2.Scalars{
893				OptString: proto.String("no unknowns"),
894			}
895			m.ProtoReflect().SetUnknown(protopack.Message{
896				protopack.Tag{101, protopack.BytesType}, protopack.String("hello world"),
897			}.Marshal())
898			return m
899		}(),
900		want: `{
901  "optString": "no unknowns"
902}`,
903	}, {
904		desc: "json_name",
905		input: &pb3.JSONNames{
906			SString: "json_name",
907		},
908		want: `{
909  "foo_bar": "json_name"
910}`,
911	}, {
912		desc: "extensions of non-repeated fields",
913		input: func() proto.Message {
914			m := &pb2.Extensions{
915				OptString: proto.String("non-extension field"),
916				OptBool:   proto.Bool(true),
917				OptInt32:  proto.Int32(42),
918			}
919			proto.SetExtension(m, pb2.E_OptExtBool, true)
920			proto.SetExtension(m, pb2.E_OptExtString, "extension field")
921			proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
922			proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
923				OptString: proto.String("nested in an extension"),
924				OptNested: &pb2.Nested{
925					OptString: proto.String("another nested in an extension"),
926				},
927			})
928			return m
929		}(),
930		want: `{
931  "optString": "non-extension field",
932  "optBool": true,
933  "optInt32": 42,
934  "[pb2.opt_ext_bool]": true,
935  "[pb2.opt_ext_enum]": "TEN",
936  "[pb2.opt_ext_nested]": {
937    "optString": "nested in an extension",
938    "optNested": {
939      "optString": "another nested in an extension"
940    }
941  },
942  "[pb2.opt_ext_string]": "extension field"
943}`,
944	}, {
945		desc: "extensions of repeated fields",
946		input: func() proto.Message {
947			m := &pb2.Extensions{}
948			proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
949			proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
950			proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
951				&pb2.Nested{OptString: proto.String("one")},
952				&pb2.Nested{OptString: proto.String("two")},
953				&pb2.Nested{OptString: proto.String("three")},
954			})
955			return m
956		}(),
957		want: `{
958  "[pb2.rpt_ext_enum]": [
959    "TEN",
960    101,
961    "ONE"
962  ],
963  "[pb2.rpt_ext_fixed32]": [
964    42,
965    47
966  ],
967  "[pb2.rpt_ext_nested]": [
968    {
969      "optString": "one"
970    },
971    {
972      "optString": "two"
973    },
974    {
975      "optString": "three"
976    }
977  ]
978}`,
979	}, {
980		desc: "extensions of non-repeated fields in another message",
981		input: func() proto.Message {
982			m := &pb2.Extensions{}
983			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
984			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
985			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
986			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
987				OptString: proto.String("nested in an extension"),
988				OptNested: &pb2.Nested{
989					OptString: proto.String("another nested in an extension"),
990				},
991			})
992			return m
993		}(),
994		want: `{
995  "[pb2.ExtensionsContainer.opt_ext_bool]": true,
996  "[pb2.ExtensionsContainer.opt_ext_enum]": "TEN",
997  "[pb2.ExtensionsContainer.opt_ext_nested]": {
998    "optString": "nested in an extension",
999    "optNested": {
1000      "optString": "another nested in an extension"
1001    }
1002  },
1003  "[pb2.ExtensionsContainer.opt_ext_string]": "extension field"
1004}`,
1005	}, {
1006		desc: "extensions of repeated fields in another message",
1007		input: func() proto.Message {
1008			m := &pb2.Extensions{
1009				OptString: proto.String("non-extension field"),
1010				OptBool:   proto.Bool(true),
1011				OptInt32:  proto.Int32(42),
1012			}
1013			proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
1014			proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
1015			proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
1016				&pb2.Nested{OptString: proto.String("one")},
1017				&pb2.Nested{OptString: proto.String("two")},
1018				&pb2.Nested{OptString: proto.String("three")},
1019			})
1020			return m
1021		}(),
1022		want: `{
1023  "optString": "non-extension field",
1024  "optBool": true,
1025  "optInt32": 42,
1026  "[pb2.ExtensionsContainer.rpt_ext_enum]": [
1027    "TEN",
1028    101,
1029    "ONE"
1030  ],
1031  "[pb2.ExtensionsContainer.rpt_ext_nested]": [
1032    {
1033      "optString": "one"
1034    },
1035    {
1036      "optString": "two"
1037    },
1038    {
1039      "optString": "three"
1040    }
1041  ],
1042  "[pb2.ExtensionsContainer.rpt_ext_string]": [
1043    "hello",
1044    "world"
1045  ]
1046}`,
1047	}, {
1048		desc: "MessageSet",
1049		input: func() proto.Message {
1050			m := &pb2.MessageSet{}
1051			proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
1052				OptString: proto.String("a messageset extension"),
1053			})
1054			proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
1055				OptString: proto.String("not a messageset extension"),
1056			})
1057			proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
1058				OptString: proto.String("just a regular extension"),
1059			})
1060			return m
1061		}(),
1062		want: `{
1063  "[pb2.MessageSetExtension.ext_nested]": {
1064    "optString": "just a regular extension"
1065  },
1066  "[pb2.MessageSetExtension]": {
1067    "optString": "a messageset extension"
1068  },
1069  "[pb2.MessageSetExtension.not_message_set_extension]": {
1070    "optString": "not a messageset extension"
1071  }
1072}`,
1073		skip: !flags.ProtoLegacy,
1074	}, {
1075		desc: "not real MessageSet 1",
1076		input: func() proto.Message {
1077			m := &pb2.FakeMessageSet{}
1078			proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
1079				OptString: proto.String("not a messageset extension"),
1080			})
1081			return m
1082		}(),
1083		want: `{
1084  "[pb2.FakeMessageSetExtension.message_set_extension]": {
1085    "optString": "not a messageset extension"
1086  }
1087}`,
1088		skip: !flags.ProtoLegacy,
1089	}, {
1090		desc: "not real MessageSet 2",
1091		input: func() proto.Message {
1092			m := &pb2.MessageSet{}
1093			proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
1094				OptString: proto.String("another not a messageset extension"),
1095			})
1096			return m
1097		}(),
1098		want: `{
1099  "[pb2.message_set_extension]": {
1100    "optString": "another not a messageset extension"
1101  }
1102}`,
1103		skip: !flags.ProtoLegacy,
1104	}, {
1105		desc:  "BoolValue empty",
1106		input: &wrapperspb.BoolValue{},
1107		want:  `false`,
1108	}, {
1109		desc:  "BoolValue",
1110		input: &wrapperspb.BoolValue{Value: true},
1111		want:  `true`,
1112	}, {
1113		desc:  "Int32Value empty",
1114		input: &wrapperspb.Int32Value{},
1115		want:  `0`,
1116	}, {
1117		desc:  "Int32Value",
1118		input: &wrapperspb.Int32Value{Value: 42},
1119		want:  `42`,
1120	}, {
1121		desc:  "Int64Value",
1122		input: &wrapperspb.Int64Value{Value: 42},
1123		want:  `"42"`,
1124	}, {
1125		desc:  "UInt32Value",
1126		input: &wrapperspb.UInt32Value{Value: 42},
1127		want:  `42`,
1128	}, {
1129		desc:  "UInt64Value",
1130		input: &wrapperspb.UInt64Value{Value: 42},
1131		want:  `"42"`,
1132	}, {
1133		desc:  "FloatValue",
1134		input: &wrapperspb.FloatValue{Value: 1.02},
1135		want:  `1.02`,
1136	}, {
1137		desc:  "FloatValue Infinity",
1138		input: &wrapperspb.FloatValue{Value: float32(math.Inf(-1))},
1139		want:  `"-Infinity"`,
1140	}, {
1141		desc:  "DoubleValue",
1142		input: &wrapperspb.DoubleValue{Value: 1.02},
1143		want:  `1.02`,
1144	}, {
1145		desc:  "DoubleValue NaN",
1146		input: &wrapperspb.DoubleValue{Value: math.NaN()},
1147		want:  `"NaN"`,
1148	}, {
1149		desc:  "StringValue empty",
1150		input: &wrapperspb.StringValue{},
1151		want:  `""`,
1152	}, {
1153		desc:  "StringValue",
1154		input: &wrapperspb.StringValue{Value: "谷歌"},
1155		want:  `"谷歌"`,
1156	}, {
1157		desc:    "StringValue with invalid UTF8 error",
1158		input:   &wrapperspb.StringValue{Value: "abc\xff"},
1159		wantErr: true,
1160	}, {
1161		desc: "StringValue field with invalid UTF8 error",
1162		input: &pb2.KnownTypes{
1163			OptString: &wrapperspb.StringValue{Value: "abc\xff"},
1164		},
1165		wantErr: true,
1166	}, {
1167		desc:  "BytesValue",
1168		input: &wrapperspb.BytesValue{Value: []byte("hello")},
1169		want:  `"aGVsbG8="`,
1170	}, {
1171		desc:  "Empty",
1172		input: &emptypb.Empty{},
1173		want:  `{}`,
1174	}, {
1175		desc:  "NullValue field",
1176		input: &pb2.KnownTypes{OptNull: new(structpb.NullValue)},
1177		want: `{
1178  "optNull": null
1179}`,
1180	}, {
1181		desc:    "Value empty",
1182		input:   &structpb.Value{},
1183		wantErr: true,
1184	}, {
1185		desc: "Value empty field",
1186		input: &pb2.KnownTypes{
1187			OptValue: &structpb.Value{},
1188		},
1189		wantErr: true,
1190	}, {
1191		desc:  "Value contains NullValue",
1192		input: &structpb.Value{Kind: &structpb.Value_NullValue{}},
1193		want:  `null`,
1194	}, {
1195		desc:  "Value contains BoolValue",
1196		input: &structpb.Value{Kind: &structpb.Value_BoolValue{}},
1197		want:  `false`,
1198	}, {
1199		desc:  "Value contains NumberValue",
1200		input: &structpb.Value{Kind: &structpb.Value_NumberValue{1.02}},
1201		want:  `1.02`,
1202	}, {
1203		desc:  "Value contains StringValue",
1204		input: &structpb.Value{Kind: &structpb.Value_StringValue{"hello"}},
1205		want:  `"hello"`,
1206	}, {
1207		desc:    "Value contains StringValue with invalid UTF8",
1208		input:   &structpb.Value{Kind: &structpb.Value_StringValue{"\xff"}},
1209		wantErr: true,
1210	}, {
1211		desc: "Value contains Struct",
1212		input: &structpb.Value{
1213			Kind: &structpb.Value_StructValue{
1214				&structpb.Struct{
1215					Fields: map[string]*structpb.Value{
1216						"null":   {Kind: &structpb.Value_NullValue{}},
1217						"number": {Kind: &structpb.Value_NumberValue{}},
1218						"string": {Kind: &structpb.Value_StringValue{}},
1219						"struct": {Kind: &structpb.Value_StructValue{}},
1220						"list":   {Kind: &structpb.Value_ListValue{}},
1221						"bool":   {Kind: &structpb.Value_BoolValue{}},
1222					},
1223				},
1224			},
1225		},
1226		want: `{
1227  "bool": false,
1228  "list": [],
1229  "null": null,
1230  "number": 0,
1231  "string": "",
1232  "struct": {}
1233}`,
1234	}, {
1235		desc: "Value contains ListValue",
1236		input: &structpb.Value{
1237			Kind: &structpb.Value_ListValue{
1238				&structpb.ListValue{
1239					Values: []*structpb.Value{
1240						{Kind: &structpb.Value_BoolValue{}},
1241						{Kind: &structpb.Value_NullValue{}},
1242						{Kind: &structpb.Value_NumberValue{}},
1243						{Kind: &structpb.Value_StringValue{}},
1244						{Kind: &structpb.Value_StructValue{}},
1245						{Kind: &structpb.Value_ListValue{}},
1246					},
1247				},
1248			},
1249		},
1250		want: `[
1251  false,
1252  null,
1253  0,
1254  "",
1255  {},
1256  []
1257]`,
1258	}, {
1259		desc:    "Value with NaN",
1260		input:   structpb.NewNumberValue(math.NaN()),
1261		wantErr: true,
1262	}, {
1263		desc:    "Value with -Inf",
1264		input:   structpb.NewNumberValue(math.Inf(-1)),
1265		wantErr: true,
1266	}, {
1267		desc:    "Value with +Inf",
1268		input:   structpb.NewNumberValue(math.Inf(+1)),
1269		wantErr: true,
1270	}, {
1271		desc:  "Struct with nil map",
1272		input: &structpb.Struct{},
1273		want:  `{}`,
1274	}, {
1275		desc: "Struct with empty map",
1276		input: &structpb.Struct{
1277			Fields: map[string]*structpb.Value{},
1278		},
1279		want: `{}`,
1280	}, {
1281		desc: "Struct",
1282		input: &structpb.Struct{
1283			Fields: map[string]*structpb.Value{
1284				"bool":   {Kind: &structpb.Value_BoolValue{true}},
1285				"null":   {Kind: &structpb.Value_NullValue{}},
1286				"number": {Kind: &structpb.Value_NumberValue{3.1415}},
1287				"string": {Kind: &structpb.Value_StringValue{"hello"}},
1288				"struct": {
1289					Kind: &structpb.Value_StructValue{
1290						&structpb.Struct{
1291							Fields: map[string]*structpb.Value{
1292								"string": {Kind: &structpb.Value_StringValue{"world"}},
1293							},
1294						},
1295					},
1296				},
1297				"list": {
1298					Kind: &structpb.Value_ListValue{
1299						&structpb.ListValue{
1300							Values: []*structpb.Value{
1301								{Kind: &structpb.Value_BoolValue{}},
1302								{Kind: &structpb.Value_NullValue{}},
1303								{Kind: &structpb.Value_NumberValue{}},
1304							},
1305						},
1306					},
1307				},
1308			},
1309		},
1310		want: `{
1311  "bool": true,
1312  "list": [
1313    false,
1314    null,
1315    0
1316  ],
1317  "null": null,
1318  "number": 3.1415,
1319  "string": "hello",
1320  "struct": {
1321    "string": "world"
1322  }
1323}`,
1324	}, {
1325		desc: "Struct message with invalid UTF8 string",
1326		input: &structpb.Struct{
1327			Fields: map[string]*structpb.Value{
1328				"string": {Kind: &structpb.Value_StringValue{"\xff"}},
1329			},
1330		},
1331		wantErr: true,
1332	}, {
1333		desc:  "ListValue with nil values",
1334		input: &structpb.ListValue{},
1335		want:  `[]`,
1336	}, {
1337		desc: "ListValue with empty values",
1338		input: &structpb.ListValue{
1339			Values: []*structpb.Value{},
1340		},
1341		want: `[]`,
1342	}, {
1343		desc: "ListValue",
1344		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"}},
1350				{
1351					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{}},
1357							},
1358						},
1359					},
1360				},
1361				{
1362					Kind: &structpb.Value_StructValue{
1363						&structpb.Struct{
1364							Fields: map[string]*structpb.Value{
1365								"string": {Kind: &structpb.Value_StringValue{"world"}},
1366							},
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",
1388		input: &structpb.ListValue{
1389			Values: []*structpb.Value{
1390				{Kind: &structpb.Value_StringValue{"\xff"}},
1391			},
1392		},
1393		wantErr: true,
1394	}, {
1395		desc:  "Duration empty",
1396		input: &durationpb.Duration{},
1397		want:  `"0s"`,
1398	}, {
1399		desc:  "Duration with secs",
1400		input: &durationpb.Duration{Seconds: 3},
1401		want:  `"3s"`,
1402	}, {
1403		desc:  "Duration with -secs",
1404		input: &durationpb.Duration{Seconds: -3},
1405		want:  `"-3s"`,
1406	}, {
1407		desc:  "Duration with nanos",
1408		input: &durationpb.Duration{Nanos: 1e6},
1409		want:  `"0.001s"`,
1410	}, {
1411		desc:  "Duration with -nanos",
1412		input: &durationpb.Duration{Nanos: -1e6},
1413		want:  `"-0.001s"`,
1414	}, {
1415		desc:  "Duration with large secs",
1416		input: &durationpb.Duration{Seconds: 1e10, Nanos: 1},
1417		want:  `"10000000000.000000001s"`,
1418	}, {
1419		desc:  "Duration with 6-digit nanos",
1420		input: &durationpb.Duration{Nanos: 1e4},
1421		want:  `"0.000010s"`,
1422	}, {
1423		desc:  "Duration with 3-digit nanos",
1424		input: &durationpb.Duration{Nanos: 1e6},
1425		want:  `"0.001s"`,
1426	}, {
1427		desc:  "Duration with -secs -nanos",
1428		input: &durationpb.Duration{Seconds: -123, Nanos: -450},
1429		want:  `"-123.000000450s"`,
1430	}, {
1431		desc:  "Duration max value",
1432		input: &durationpb.Duration{Seconds: 315576000000, Nanos: 999999999},
1433		want:  `"315576000000.999999999s"`,
1434	}, {
1435		desc:  "Duration min value",
1436		input: &durationpb.Duration{Seconds: -315576000000, Nanos: -999999999},
1437		want:  `"-315576000000.999999999s"`,
1438	}, {
1439		desc:    "Duration with +secs -nanos",
1440		input:   &durationpb.Duration{Seconds: 1, Nanos: -1},
1441		wantErr: true,
1442	}, {
1443		desc:    "Duration with -secs +nanos",
1444		input:   &durationpb.Duration{Seconds: -1, Nanos: 1},
1445		wantErr: true,
1446	}, {
1447		desc:    "Duration with +secs out of range",
1448		input:   &durationpb.Duration{Seconds: 315576000001},
1449		wantErr: true,
1450	}, {
1451		desc:    "Duration with -secs out of range",
1452		input:   &durationpb.Duration{Seconds: -315576000001},
1453		wantErr: true,
1454	}, {
1455		desc:    "Duration with +nanos out of range",
1456		input:   &durationpb.Duration{Seconds: 0, Nanos: 1e9},
1457		wantErr: true,
1458	}, {
1459		desc:    "Duration with -nanos out of range",
1460		input:   &durationpb.Duration{Seconds: 0, Nanos: -1e9},
1461		wantErr: true,
1462	}, {
1463		desc:  "Timestamp zero",
1464		input: &timestamppb.Timestamp{},
1465		want:  `"1970-01-01T00:00:00Z"`,
1466	}, {
1467		desc:  "Timestamp",
1468		input: &timestamppb.Timestamp{Seconds: 1553036601},
1469		want:  `"2019-03-19T23:03:21Z"`,
1470	}, {
1471		desc:  "Timestamp with nanos",
1472		input: &timestamppb.Timestamp{Seconds: 1553036601, Nanos: 1},
1473		want:  `"2019-03-19T23:03:21.000000001Z"`,
1474	}, {
1475		desc:  "Timestamp with 6-digit nanos",
1476		input: &timestamppb.Timestamp{Nanos: 1e3},
1477		want:  `"1970-01-01T00:00:00.000001Z"`,
1478	}, {
1479		desc:  "Timestamp with 3-digit nanos",
1480		input: &timestamppb.Timestamp{Nanos: 1e7},
1481		want:  `"1970-01-01T00:00:00.010Z"`,
1482	}, {
1483		desc:  "Timestamp max value",
1484		input: &timestamppb.Timestamp{Seconds: 253402300799, Nanos: 999999999},
1485		want:  `"9999-12-31T23:59:59.999999999Z"`,
1486	}, {
1487		desc:  "Timestamp min value",
1488		input: &timestamppb.Timestamp{Seconds: -62135596800},
1489		want:  `"0001-01-01T00:00:00Z"`,
1490	}, {
1491		desc:    "Timestamp with +secs out of range",
1492		input:   &timestamppb.Timestamp{Seconds: 253402300800},
1493		wantErr: true,
1494	}, {
1495		desc:    "Timestamp with -secs out of range",
1496		input:   &timestamppb.Timestamp{Seconds: -62135596801},
1497		wantErr: true,
1498	}, {
1499		desc:    "Timestamp with -nanos",
1500		input:   &timestamppb.Timestamp{Nanos: -1},
1501		wantErr: true,
1502	}, {
1503		desc:    "Timestamp with +nanos out of range",
1504		input:   &timestamppb.Timestamp{Nanos: 1e9},
1505		wantErr: true,
1506	}, {
1507		desc:  "FieldMask empty",
1508		input: &fieldmaskpb.FieldMask{},
1509		want:  `""`,
1510	}, {
1511		desc: "FieldMask",
1512		input: &fieldmaskpb.FieldMask{
1513			Paths: []string{
1514				"foo",
1515				"foo_bar",
1516				"foo.bar_qux",
1517				"_foo",
1518			},
1519		},
1520		want: `"foo,fooBar,foo.barQux,Foo"`,
1521	}, {
1522		desc: "FieldMask empty string path",
1523		input: &fieldmaskpb.FieldMask{
1524			Paths: []string{""},
1525		},
1526		wantErr: true,
1527	}, {
1528		desc: "FieldMask path contains spaces only",
1529		input: &fieldmaskpb.FieldMask{
1530			Paths: []string{"  "},
1531		},
1532		wantErr: true,
1533	}, {
1534		desc: "FieldMask irreversible error 1",
1535		input: &fieldmaskpb.FieldMask{
1536			Paths: []string{"foo_"},
1537		},
1538		wantErr: true,
1539	}, {
1540		desc: "FieldMask irreversible error 2",
1541		input: &fieldmaskpb.FieldMask{
1542			Paths: []string{"foo__bar"},
1543		},
1544		wantErr: true,
1545	}, {
1546		desc: "FieldMask invalid char",
1547		input: &fieldmaskpb.FieldMask{
1548			Paths: []string{"foo@bar"},
1549		},
1550		wantErr: true,
1551	}, {
1552		desc:  "Any empty",
1553		input: &anypb.Any{},
1554		want:  `{}`,
1555	}, {
1556		desc: "Any with non-custom message",
1557		input: func() proto.Message {
1558			m := &pb2.Nested{
1559				OptString: proto.String("embedded inside Any"),
1560				OptNested: &pb2.Nested{
1561					OptString: proto.String("inception"),
1562				},
1563			}
1564			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1565			if err != nil {
1566				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1567			}
1568			return &anypb.Any{
1569				TypeUrl: "foo/pb2.Nested",
1570				Value:   b,
1571			}
1572		}(),
1573		want: `{
1574  "@type": "foo/pb2.Nested",
1575  "optString": "embedded inside Any",
1576  "optNested": {
1577    "optString": "inception"
1578  }
1579}`,
1580	}, {
1581		desc:  "Any with empty embedded message",
1582		input: &anypb.Any{TypeUrl: "foo/pb2.Nested"},
1583		want: `{
1584  "@type": "foo/pb2.Nested"
1585}`,
1586	}, {
1587		desc:    "Any without registered type",
1588		mo:      protojson.MarshalOptions{Resolver: new(protoregistry.Types)},
1589		input:   &anypb.Any{TypeUrl: "foo/pb2.Nested"},
1590		wantErr: true,
1591	}, {
1592		desc: "Any with missing required",
1593		input: func() proto.Message {
1594			m := &pb2.PartialRequired{
1595				OptString: proto.String("embedded inside Any"),
1596			}
1597			b, err := proto.MarshalOptions{
1598				AllowPartial:  true,
1599				Deterministic: true,
1600			}.Marshal(m)
1601			if err != nil {
1602				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1603			}
1604			return &anypb.Any{
1605				TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
1606				Value:   b,
1607			}
1608		}(),
1609		want: `{
1610  "@type": "pb2.PartialRequired",
1611  "optString": "embedded inside Any"
1612}`,
1613	}, {
1614		desc: "Any with partial required and AllowPartial",
1615		mo: protojson.MarshalOptions{
1616			AllowPartial: true,
1617		},
1618		input: func() proto.Message {
1619			m := &pb2.PartialRequired{
1620				OptString: proto.String("embedded inside Any"),
1621			}
1622			b, err := proto.MarshalOptions{
1623				AllowPartial:  true,
1624				Deterministic: true,
1625			}.Marshal(m)
1626			if err != nil {
1627				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1628			}
1629			return &anypb.Any{
1630				TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
1631				Value:   b,
1632			}
1633		}(),
1634		want: `{
1635  "@type": "pb2.PartialRequired",
1636  "optString": "embedded inside Any"
1637}`,
1638	}, {
1639		desc: "Any with EmitUnpopulated",
1640		mo: protojson.MarshalOptions{
1641			EmitUnpopulated: true,
1642		},
1643		input: func() proto.Message {
1644			return &anypb.Any{
1645				TypeUrl: string(new(pb3.Scalars).ProtoReflect().Descriptor().FullName()),
1646			}
1647		}(),
1648		want: `{
1649  "@type": "pb3.Scalars",
1650  "sBool": false,
1651  "sInt32": 0,
1652  "sInt64": "0",
1653  "sUint32": 0,
1654  "sUint64": "0",
1655  "sSint32": 0,
1656  "sSint64": "0",
1657  "sFixed32": 0,
1658  "sFixed64": "0",
1659  "sSfixed32": 0,
1660  "sSfixed64": "0",
1661  "sFloat": 0,
1662  "sDouble": 0,
1663  "sBytes": "",
1664  "sString": ""
1665}`,
1666	}, {
1667		desc: "Any with invalid UTF8",
1668		input: func() proto.Message {
1669			m := &pb2.Nested{
1670				OptString: proto.String("abc\xff"),
1671			}
1672			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			}
1676			return &anypb.Any{
1677				TypeUrl: "foo/pb2.Nested",
1678				Value:   b,
1679			}
1680		}(),
1681		wantErr: true,
1682	}, {
1683		desc: "Any with invalid value",
1684		input: &anypb.Any{
1685			TypeUrl: "foo/pb2.Nested",
1686			Value:   []byte("\x80"),
1687		},
1688		wantErr: true,
1689	}, {
1690		desc: "Any with BoolValue",
1691		input: func() proto.Message {
1692			m := &wrapperspb.BoolValue{Value: true}
1693			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1694			if err != nil {
1695				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1696			}
1697			return &anypb.Any{
1698				TypeUrl: "type.googleapis.com/google.protobuf.BoolValue",
1699				Value:   b,
1700			}
1701		}(),
1702		want: `{
1703  "@type": "type.googleapis.com/google.protobuf.BoolValue",
1704  "value": true
1705}`,
1706	}, {
1707		desc: "Any with Empty",
1708		input: func() proto.Message {
1709			m := &emptypb.Empty{}
1710			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1711			if err != nil {
1712				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1713			}
1714			return &anypb.Any{
1715				TypeUrl: "type.googleapis.com/google.protobuf.Empty",
1716				Value:   b,
1717			}
1718		}(),
1719		want: `{
1720  "@type": "type.googleapis.com/google.protobuf.Empty",
1721  "value": {}
1722}`,
1723	}, {
1724		desc: "Any with StringValue containing invalid UTF8",
1725		input: func() proto.Message {
1726			m := &wrapperspb.StringValue{Value: "abcd"}
1727			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1728			if err != nil {
1729				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1730			}
1731			return &anypb.Any{
1732				TypeUrl: "google.protobuf.StringValue",
1733				Value:   bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
1734			}
1735		}(),
1736		wantErr: true,
1737	}, {
1738		desc: "Any with Int64Value",
1739		input: func() proto.Message {
1740			m := &wrapperspb.Int64Value{Value: 42}
1741			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1742			if err != nil {
1743				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1744			}
1745			return &anypb.Any{
1746				TypeUrl: "google.protobuf.Int64Value",
1747				Value:   b,
1748			}
1749		}(),
1750		want: `{
1751  "@type": "google.protobuf.Int64Value",
1752  "value": "42"
1753}`,
1754	}, {
1755		desc: "Any with Duration",
1756		input: func() proto.Message {
1757			m := &durationpb.Duration{}
1758			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1759			if err != nil {
1760				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1761			}
1762			return &anypb.Any{
1763				TypeUrl: "type.googleapis.com/google.protobuf.Duration",
1764				Value:   b,
1765			}
1766		}(),
1767		want: `{
1768  "@type": "type.googleapis.com/google.protobuf.Duration",
1769  "value": "0s"
1770}`,
1771	}, {
1772		desc: "Any with empty Value",
1773		input: func() proto.Message {
1774			m := &structpb.Value{}
1775			b, err := proto.Marshal(m)
1776			if err != nil {
1777				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1778			}
1779			return &anypb.Any{
1780				TypeUrl: "type.googleapis.com/google.protobuf.Value",
1781				Value:   b,
1782			}
1783		}(),
1784		wantErr: true,
1785	}, {
1786		desc: "Any with Value of StringValue",
1787		input: func() proto.Message {
1788			m := &structpb.Value{Kind: &structpb.Value_StringValue{"abcd"}}
1789			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1790			if err != nil {
1791				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1792			}
1793			return &anypb.Any{
1794				TypeUrl: "type.googleapis.com/google.protobuf.Value",
1795				Value:   bytes.Replace(b, []byte("abcd"), []byte("abc\xff"), -1),
1796			}
1797		}(),
1798		wantErr: true,
1799	}, {
1800		desc: "Any with Value of NullValue",
1801		input: func() proto.Message {
1802			m := &structpb.Value{Kind: &structpb.Value_NullValue{}}
1803			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1804			if err != nil {
1805				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1806			}
1807			return &anypb.Any{
1808				TypeUrl: "type.googleapis.com/google.protobuf.Value",
1809				Value:   b,
1810			}
1811		}(),
1812		want: `{
1813  "@type": "type.googleapis.com/google.protobuf.Value",
1814  "value": null
1815}`,
1816	}, {
1817		desc: "Any with Struct",
1818		input: func() proto.Message {
1819			m := &structpb.Struct{
1820				Fields: map[string]*structpb.Value{
1821					"bool":   {Kind: &structpb.Value_BoolValue{true}},
1822					"null":   {Kind: &structpb.Value_NullValue{}},
1823					"string": {Kind: &structpb.Value_StringValue{"hello"}},
1824					"struct": {
1825						Kind: &structpb.Value_StructValue{
1826							&structpb.Struct{
1827								Fields: map[string]*structpb.Value{
1828									"string": {Kind: &structpb.Value_StringValue{"world"}},
1829								},
1830							},
1831						},
1832					},
1833				},
1834			}
1835			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1836			if err != nil {
1837				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1838			}
1839			return &anypb.Any{
1840				TypeUrl: "google.protobuf.Struct",
1841				Value:   b,
1842			}
1843		}(),
1844		want: `{
1845  "@type": "google.protobuf.Struct",
1846  "value": {
1847    "bool": true,
1848    "null": null,
1849    "string": "hello",
1850    "struct": {
1851      "string": "world"
1852    }
1853  }
1854}`,
1855	}, {
1856		desc: "Any with missing type_url",
1857		input: func() proto.Message {
1858			m := &wrapperspb.BoolValue{Value: true}
1859			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
1860			if err != nil {
1861				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
1862			}
1863			return &anypb.Any{
1864				Value: b,
1865			}
1866		}(),
1867		wantErr: true,
1868	}, {
1869		desc: "well known types as field values",
1870		input: &pb2.KnownTypes{
1871			OptBool:      &wrapperspb.BoolValue{Value: false},
1872			OptInt32:     &wrapperspb.Int32Value{Value: 42},
1873			OptInt64:     &wrapperspb.Int64Value{Value: 42},
1874			OptUint32:    &wrapperspb.UInt32Value{Value: 42},
1875			OptUint64:    &wrapperspb.UInt64Value{Value: 42},
1876			OptFloat:     &wrapperspb.FloatValue{Value: 1.23},
1877			OptDouble:    &wrapperspb.DoubleValue{Value: 3.1415},
1878			OptString:    &wrapperspb.StringValue{Value: "hello"},
1879			OptBytes:     &wrapperspb.BytesValue{Value: []byte("hello")},
1880			OptDuration:  &durationpb.Duration{Seconds: 123},
1881			OptTimestamp: &timestamppb.Timestamp{Seconds: 1553036601},
1882			OptStruct: &structpb.Struct{
1883				Fields: map[string]*structpb.Value{
1884					"string": {Kind: &structpb.Value_StringValue{"hello"}},
1885				},
1886			},
1887			OptList: &structpb.ListValue{
1888				Values: []*structpb.Value{
1889					{Kind: &structpb.Value_NullValue{}},
1890					{Kind: &structpb.Value_StringValue{}},
1891					{Kind: &structpb.Value_StructValue{}},
1892					{Kind: &structpb.Value_ListValue{}},
1893				},
1894			},
1895			OptValue: &structpb.Value{
1896				Kind: &structpb.Value_StringValue{"world"},
1897			},
1898			OptEmpty: &emptypb.Empty{},
1899			OptAny: &anypb.Any{
1900				TypeUrl: "google.protobuf.Empty",
1901			},
1902			OptFieldmask: &fieldmaskpb.FieldMask{
1903				Paths: []string{"foo_bar", "bar_foo"},
1904			},
1905		},
1906		want: `{
1907  "optBool": false,
1908  "optInt32": 42,
1909  "optInt64": "42",
1910  "optUint32": 42,
1911  "optUint64": "42",
1912  "optFloat": 1.23,
1913  "optDouble": 3.1415,
1914  "optString": "hello",
1915  "optBytes": "aGVsbG8=",
1916  "optDuration": "123s",
1917  "optTimestamp": "2019-03-19T23:03:21Z",
1918  "optStruct": {
1919    "string": "hello"
1920  },
1921  "optList": [
1922    null,
1923    "",
1924    {},
1925    []
1926  ],
1927  "optValue": "world",
1928  "optEmpty": {},
1929  "optAny": {
1930    "@type": "google.protobuf.Empty",
1931    "value": {}
1932  },
1933  "optFieldmask": "fooBar,barFoo"
1934}`,
1935	}, {
1936		desc:  "EmitUnpopulated: proto2 optional scalars",
1937		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
1938		input: &pb2.Scalars{},
1939		want: `{
1940  "optBool": null,
1941  "optInt32": null,
1942  "optInt64": null,
1943  "optUint32": null,
1944  "optUint64": null,
1945  "optSint32": null,
1946  "optSint64": null,
1947  "optFixed32": null,
1948  "optFixed64": null,
1949  "optSfixed32": null,
1950  "optSfixed64": null,
1951  "optFloat": null,
1952  "optDouble": null,
1953  "optBytes": null,
1954  "optString": null
1955}`,
1956	}, {
1957		desc:  "EmitUnpopulated: proto3 scalars",
1958		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
1959		input: &pb3.Scalars{},
1960		want: `{
1961  "sBool": false,
1962  "sInt32": 0,
1963  "sInt64": "0",
1964  "sUint32": 0,
1965  "sUint64": "0",
1966  "sSint32": 0,
1967  "sSint64": "0",
1968  "sFixed32": 0,
1969  "sFixed64": "0",
1970  "sSfixed32": 0,
1971  "sSfixed64": "0",
1972  "sFloat": 0,
1973  "sDouble": 0,
1974  "sBytes": "",
1975  "sString": ""
1976}`,
1977	}, {
1978		desc:  "EmitUnpopulated: proto2 enum",
1979		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
1980		input: &pb2.Enums{},
1981		want: `{
1982  "optEnum": null,
1983  "rptEnum": [],
1984  "optNestedEnum": null,
1985  "rptNestedEnum": []
1986}`,
1987	}, {
1988		desc:  "EmitUnpopulated: proto3 enum",
1989		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
1990		input: &pb3.Enums{},
1991		want: `{
1992  "sEnum": "ZERO",
1993  "sNestedEnum": "CERO"
1994}`,
1995	}, {
1996		desc:  "EmitUnpopulated: proto2 message and group fields",
1997		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
1998		input: &pb2.Nests{},
1999		want: `{
2000  "optNested": null,
2001  "optgroup": null,
2002  "rptNested": [],
2003  "rptgroup": []
2004}`,
2005	}, {
2006		desc:  "EmitUnpopulated: proto3 message field",
2007		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
2008		input: &pb3.Nests{},
2009		want: `{
2010  "sNested": null
2011}`,
2012	}, {
2013		desc: "EmitUnpopulated: proto2 empty message and group fields",
2014		mo:   protojson.MarshalOptions{EmitUnpopulated: true},
2015		input: &pb2.Nests{
2016			OptNested: &pb2.Nested{},
2017			Optgroup:  &pb2.Nests_OptGroup{},
2018		},
2019		want: `{
2020  "optNested": {
2021    "optString": null,
2022    "optNested": null
2023  },
2024  "optgroup": {
2025    "optString": null,
2026    "optNested": null,
2027    "optnestedgroup": null
2028  },
2029  "rptNested": [],
2030  "rptgroup": []
2031}`,
2032	}, {
2033		desc: "EmitUnpopulated: proto3 empty message field",
2034		mo:   protojson.MarshalOptions{EmitUnpopulated: true},
2035		input: &pb3.Nests{
2036			SNested: &pb3.Nested{},
2037		},
2038		want: `{
2039  "sNested": {
2040    "sString": "",
2041    "sNested": null
2042  }
2043}`,
2044	}, {
2045		desc: "EmitUnpopulated: proto2 required fields",
2046		mo: protojson.MarshalOptions{
2047			AllowPartial:    true,
2048			EmitUnpopulated: true,
2049		},
2050		input: &pb2.Requireds{},
2051		want: `{
2052  "reqBool": null,
2053  "reqSfixed64": null,
2054  "reqDouble": null,
2055  "reqString": null,
2056  "reqEnum": null,
2057  "reqNested": null
2058}`,
2059	}, {
2060		desc:  "EmitUnpopulated: repeated fields",
2061		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
2062		input: &pb2.Repeats{},
2063		want: `{
2064  "rptBool": [],
2065  "rptInt32": [],
2066  "rptInt64": [],
2067  "rptUint32": [],
2068  "rptUint64": [],
2069  "rptFloat": [],
2070  "rptDouble": [],
2071  "rptString": [],
2072  "rptBytes": []
2073}`,
2074	}, {
2075		desc: "EmitUnpopulated: repeated containing empty message",
2076		mo:   protojson.MarshalOptions{EmitUnpopulated: true},
2077		input: &pb2.Nests{
2078			RptNested: []*pb2.Nested{nil, {}},
2079		},
2080		want: `{
2081  "optNested": null,
2082  "optgroup": null,
2083  "rptNested": [
2084    {
2085      "optString": null,
2086      "optNested": null
2087    },
2088    {
2089      "optString": null,
2090      "optNested": null
2091    }
2092  ],
2093  "rptgroup": []
2094}`,
2095	}, {
2096		desc:  "EmitUnpopulated: map fields",
2097		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
2098		input: &pb3.Maps{},
2099		want: `{
2100  "int32ToStr": {},
2101  "boolToUint32": {},
2102  "uint64ToEnum": {},
2103  "strToNested": {},
2104  "strToOneofs": {}
2105}`,
2106	}, {
2107		desc: "EmitUnpopulated: map containing empty message",
2108		mo:   protojson.MarshalOptions{EmitUnpopulated: true},
2109		input: &pb3.Maps{
2110			StrToNested: map[string]*pb3.Nested{
2111				"nested": &pb3.Nested{},
2112			},
2113			StrToOneofs: map[string]*pb3.Oneofs{
2114				"nested": &pb3.Oneofs{},
2115			},
2116		},
2117		want: `{
2118  "int32ToStr": {},
2119  "boolToUint32": {},
2120  "uint64ToEnum": {},
2121  "strToNested": {
2122    "nested": {
2123      "sString": "",
2124      "sNested": null
2125    }
2126  },
2127  "strToOneofs": {
2128    "nested": {}
2129  }
2130}`,
2131	}, {
2132		desc:  "EmitUnpopulated: oneof fields",
2133		mo:    protojson.MarshalOptions{EmitUnpopulated: true},
2134		input: &pb3.Oneofs{},
2135		want:  `{}`,
2136	}, {
2137		desc: "EmitUnpopulated: extensions",
2138		mo:   protojson.MarshalOptions{EmitUnpopulated: true},
2139		input: func() proto.Message {
2140			m := &pb2.Extensions{}
2141			proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{})
2142			proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
2143				nil,
2144				{},
2145			})
2146			return m
2147		}(),
2148		want: `{
2149  "optString": null,
2150  "optBool": null,
2151  "optInt32": null,
2152  "[pb2.opt_ext_nested]": {
2153    "optString": null,
2154    "optNested": null
2155  },
2156  "[pb2.rpt_ext_nested]": [
2157    {
2158      "optString": null,
2159      "optNested": null
2160    },
2161    {
2162      "optString": null,
2163      "optNested": null
2164    }
2165  ]
2166}`,
2167	}, {
2168		desc: "EmitUnpopulated: with populated fields",
2169		mo:   protojson.MarshalOptions{EmitUnpopulated: true},
2170		input: &pb2.Scalars{
2171			OptInt32:    proto.Int32(0xff),
2172			OptUint32:   proto.Uint32(47),
2173			OptSint32:   proto.Int32(-1001),
2174			OptFixed32:  proto.Uint32(32),
2175			OptSfixed32: proto.Int32(-32),
2176			OptFloat:    proto.Float32(1.02),
2177			OptBytes:    []byte("谷歌"),
2178		},
2179		want: `{
2180  "optBool": null,
2181  "optInt32": 255,
2182  "optInt64": null,
2183  "optUint32": 47,
2184  "optUint64": null,
2185  "optSint32": -1001,
2186  "optSint64": null,
2187  "optFixed32": 32,
2188  "optFixed64": null,
2189  "optSfixed32": -32,
2190  "optSfixed64": null,
2191  "optFloat": 1.02,
2192  "optDouble": null,
2193  "optBytes": "6LC35q2M",
2194  "optString": null
2195}`,
2196	}, {
2197		desc: "UseEnumNumbers in singular field",
2198		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
2199		input: &pb2.Enums{
2200			OptEnum:       pb2.Enum_ONE.Enum(),
2201			OptNestedEnum: pb2.Enums_UNO.Enum(),
2202		},
2203		want: `{
2204  "optEnum": 1,
2205  "optNestedEnum": 1
2206}`,
2207	}, {
2208		desc: "UseEnumNumbers in repeated field",
2209		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
2210		input: &pb2.Enums{
2211			RptEnum:       []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
2212			RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_UNO, pb2.Enums_DOS, 47},
2213		},
2214		want: `{
2215  "rptEnum": [
2216    1,
2217    2,
2218    10,
2219    42
2220  ],
2221  "rptNestedEnum": [
2222    1,
2223    2,
2224    47
2225  ]
2226}`,
2227	}, {
2228		desc: "UseEnumNumbers in map field",
2229		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
2230		input: &pb3.Maps{
2231			Uint64ToEnum: map[uint64]pb3.Enum{
2232				1:  pb3.Enum_ONE,
2233				2:  pb3.Enum_TWO,
2234				10: pb3.Enum_TEN,
2235				47: 47,
2236			},
2237		},
2238		want: `{
2239  "uint64ToEnum": {
2240    "1": 1,
2241    "2": 2,
2242    "10": 10,
2243    "47": 47
2244  }
2245}`,
2246	}, {
2247		desc: "UseProtoNames",
2248		mo:   protojson.MarshalOptions{UseProtoNames: true},
2249		input: &pb2.Nests{
2250			OptNested: &pb2.Nested{},
2251			Optgroup: &pb2.Nests_OptGroup{
2252				OptString: proto.String("inside a group"),
2253				OptNested: &pb2.Nested{
2254					OptString: proto.String("nested message inside a group"),
2255				},
2256				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
2257					OptFixed32: proto.Uint32(47),
2258				},
2259			},
2260			Rptgroup: []*pb2.Nests_RptGroup{
2261				{
2262					RptString: []string{"hello", "world"},
2263				},
2264			},
2265		},
2266		want: `{
2267  "opt_nested": {},
2268  "OptGroup": {
2269    "opt_string": "inside a group",
2270    "opt_nested": {
2271      "opt_string": "nested message inside a group"
2272    },
2273    "OptNestedGroup": {
2274      "opt_fixed32": 47
2275    }
2276  },
2277  "RptGroup": [
2278    {
2279      "rpt_string": [
2280        "hello",
2281        "world"
2282      ]
2283    }
2284  ]
2285}`,
2286	}}
2287
2288	for _, tt := range tests {
2289		tt := tt
2290		if tt.skip {
2291			continue
2292		}
2293		t.Run(tt.desc, func(t *testing.T) {
2294			// Use 2-space indentation on all MarshalOptions.
2295			tt.mo.Indent = "  "
2296			b, err := tt.mo.Marshal(tt.input)
2297			if err != nil && !tt.wantErr {
2298				t.Errorf("Marshal() returned error: %v\n", err)
2299			}
2300			if err == nil && tt.wantErr {
2301				t.Errorf("Marshal() got nil error, want error\n")
2302			}
2303			got := string(b)
2304			if got != tt.want {
2305				t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
2306				if diff := cmp.Diff(tt.want, got); diff != "" {
2307					t.Errorf("Marshal() diff -want +got\n%v\n", diff)
2308				}
2309			}
2310		})
2311	}
2312}
2313