...

Source file src/google.golang.org/protobuf/encoding/protojson/encode_test.go

Documentation: google.golang.org/protobuf/encoding/protojson

     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  
     5  package protojson_test
     6  
     7  import (
     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.
    33  func init() { detrand.Disable() }
    34  
    35  func 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: "EmitUnpopulated overrides EmitDefaultValues",
  2198  		mo:   protojson.MarshalOptions{EmitUnpopulated: true, EmitDefaultValues: true},
  2199  		input: &pb2.Nests{
  2200  			RptNested: []*pb2.Nested{nil, {}},
  2201  		},
  2202  		want: `{
  2203    "optNested": null,
  2204    "optgroup": null,
  2205    "rptNested": [
  2206      {
  2207        "optString": null,
  2208        "optNested": null
  2209      },
  2210      {
  2211        "optString": null,
  2212        "optNested": null
  2213      }
  2214    ],
  2215    "rptgroup": []
  2216  }`,
  2217  	}, {
  2218  		desc:  "EmitDefaultValues: proto2 optional scalars",
  2219  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2220  		input: &pb2.Scalars{},
  2221  		want:  `{}`,
  2222  	}, {
  2223  		desc:  "EmitDefaultValues: proto3 scalars",
  2224  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2225  		input: &pb3.Scalars{},
  2226  		want: `{
  2227    "sBool": false,
  2228    "sInt32": 0,
  2229    "sInt64": "0",
  2230    "sUint32": 0,
  2231    "sUint64": "0",
  2232    "sSint32": 0,
  2233    "sSint64": "0",
  2234    "sFixed32": 0,
  2235    "sFixed64": "0",
  2236    "sSfixed32": 0,
  2237    "sSfixed64": "0",
  2238    "sFloat": 0,
  2239    "sDouble": 0,
  2240    "sBytes": "",
  2241    "sString": ""
  2242  }`,
  2243  	}, {
  2244  		desc:  "EmitDefaultValues: proto2 enum",
  2245  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2246  		input: &pb2.Enums{},
  2247  		want: `{
  2248    "rptEnum": [],
  2249    "rptNestedEnum": []
  2250  }`,
  2251  	}, {
  2252  		desc:  "EmitDefaultValues: proto3 enum",
  2253  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2254  		input: &pb3.Enums{},
  2255  		want: `{
  2256    "sEnum": "ZERO",
  2257    "sNestedEnum": "CERO"
  2258  }`,
  2259  	}, {
  2260  		desc:  "EmitDefaultValues: proto2 message and group fields",
  2261  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2262  		input: &pb2.Nests{},
  2263  		want: `{
  2264    "rptNested": [],
  2265    "rptgroup": []
  2266  }`,
  2267  	}, {
  2268  		desc:  "EmitDefaultValues: proto3 message field",
  2269  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2270  		input: &pb3.Nests{},
  2271  		want:  `{}`,
  2272  	}, {
  2273  		desc: "EmitDefaultValues: proto2 empty message and group fields",
  2274  		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
  2275  		input: &pb2.Nests{
  2276  			OptNested: &pb2.Nested{},
  2277  			Optgroup:  &pb2.Nests_OptGroup{},
  2278  		},
  2279  		want: `{
  2280    "optNested": {},
  2281    "optgroup": {},
  2282    "rptNested": [],
  2283    "rptgroup": []
  2284  }`,
  2285  	}, {
  2286  		desc: "EmitDefaultValues: proto3 empty message field",
  2287  		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
  2288  		input: &pb3.Nests{
  2289  			SNested: &pb3.Nested{},
  2290  		},
  2291  		want: `{
  2292    "sNested": {
  2293      "sString": ""
  2294    }
  2295  }`,
  2296  	}, {
  2297  		desc: "EmitDefaultValues: proto2 required fields",
  2298  		mo: protojson.MarshalOptions{
  2299  			AllowPartial:      true,
  2300  			EmitDefaultValues: true,
  2301  		},
  2302  		input: &pb2.Requireds{},
  2303  		want:  `{}`,
  2304  	}, {
  2305  		desc:  "EmitDefaultValues: repeated fields",
  2306  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2307  		input: &pb2.Repeats{},
  2308  		want: `{
  2309    "rptBool": [],
  2310    "rptInt32": [],
  2311    "rptInt64": [],
  2312    "rptUint32": [],
  2313    "rptUint64": [],
  2314    "rptFloat": [],
  2315    "rptDouble": [],
  2316    "rptString": [],
  2317    "rptBytes": []
  2318  }`,
  2319  	}, {
  2320  		desc: "EmitDefaultValues: repeated containing empty message",
  2321  		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
  2322  		input: &pb2.Nests{
  2323  			RptNested: []*pb2.Nested{nil, {}},
  2324  		},
  2325  		want: `{
  2326    "rptNested": [
  2327      {},
  2328      {}
  2329    ],
  2330    "rptgroup": []
  2331  }`,
  2332  	}, {
  2333  		desc:  "EmitDefaultValues: map fields",
  2334  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2335  		input: &pb3.Maps{},
  2336  		want: `{
  2337    "int32ToStr": {},
  2338    "boolToUint32": {},
  2339    "uint64ToEnum": {},
  2340    "strToNested": {},
  2341    "strToOneofs": {}
  2342  }`,
  2343  	}, {
  2344  		desc: "EmitDefaultValues: map containing empty message",
  2345  		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
  2346  		input: &pb3.Maps{
  2347  			StrToNested: map[string]*pb3.Nested{
  2348  				"nested": &pb3.Nested{},
  2349  			},
  2350  			StrToOneofs: map[string]*pb3.Oneofs{
  2351  				"nested": &pb3.Oneofs{},
  2352  			},
  2353  		},
  2354  		want: `{
  2355    "int32ToStr": {},
  2356    "boolToUint32": {},
  2357    "uint64ToEnum": {},
  2358    "strToNested": {
  2359      "nested": {
  2360        "sString": ""
  2361      }
  2362    },
  2363    "strToOneofs": {
  2364      "nested": {}
  2365    }
  2366  }`,
  2367  	}, {
  2368  		desc:  "EmitDefaultValues: oneof fields",
  2369  		mo:    protojson.MarshalOptions{EmitDefaultValues: true},
  2370  		input: &pb3.Oneofs{},
  2371  		want:  `{}`,
  2372  	}, {
  2373  		desc: "EmitDefaultValues: extensions",
  2374  		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
  2375  		input: func() proto.Message {
  2376  			m := &pb2.Extensions{}
  2377  			proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{})
  2378  			proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
  2379  				nil,
  2380  				{},
  2381  			})
  2382  			return m
  2383  		}(),
  2384  		want: `{
  2385    "[pb2.opt_ext_nested]": {},
  2386    "[pb2.rpt_ext_nested]": [
  2387      {},
  2388      {}
  2389    ]
  2390  }`,
  2391  	}, {
  2392  		desc: "EmitDefaultValues: with populated fields",
  2393  		mo:   protojson.MarshalOptions{EmitDefaultValues: true},
  2394  		input: &pb2.Scalars{
  2395  			OptInt32:    proto.Int32(0xff),
  2396  			OptUint32:   proto.Uint32(47),
  2397  			OptSint32:   proto.Int32(-1001),
  2398  			OptFixed32:  proto.Uint32(32),
  2399  			OptSfixed32: proto.Int32(-32),
  2400  			OptFloat:    proto.Float32(1.02),
  2401  			OptBytes:    []byte("谷歌"),
  2402  		},
  2403  		want: `{
  2404    "optInt32": 255,
  2405    "optUint32": 47,
  2406    "optSint32": -1001,
  2407    "optFixed32": 32,
  2408    "optSfixed32": -32,
  2409    "optFloat": 1.02,
  2410    "optBytes": "6LC35q2M"
  2411  }`,
  2412  	}, {
  2413  		desc: "UseEnumNumbers in singular field",
  2414  		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
  2415  		input: &pb2.Enums{
  2416  			OptEnum:       pb2.Enum_ONE.Enum(),
  2417  			OptNestedEnum: pb2.Enums_UNO.Enum(),
  2418  		},
  2419  		want: `{
  2420    "optEnum": 1,
  2421    "optNestedEnum": 1
  2422  }`,
  2423  	}, {
  2424  		desc: "UseEnumNumbers in repeated field",
  2425  		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
  2426  		input: &pb2.Enums{
  2427  			RptEnum:       []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
  2428  			RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_UNO, pb2.Enums_DOS, 47},
  2429  		},
  2430  		want: `{
  2431    "rptEnum": [
  2432      1,
  2433      2,
  2434      10,
  2435      42
  2436    ],
  2437    "rptNestedEnum": [
  2438      1,
  2439      2,
  2440      47
  2441    ]
  2442  }`,
  2443  	}, {
  2444  		desc: "UseEnumNumbers in map field",
  2445  		mo:   protojson.MarshalOptions{UseEnumNumbers: true},
  2446  		input: &pb3.Maps{
  2447  			Uint64ToEnum: map[uint64]pb3.Enum{
  2448  				1:  pb3.Enum_ONE,
  2449  				2:  pb3.Enum_TWO,
  2450  				10: pb3.Enum_TEN,
  2451  				47: 47,
  2452  			},
  2453  		},
  2454  		want: `{
  2455    "uint64ToEnum": {
  2456      "1": 1,
  2457      "2": 2,
  2458      "10": 10,
  2459      "47": 47
  2460    }
  2461  }`,
  2462  	}, {
  2463  		desc: "UseProtoNames",
  2464  		mo:   protojson.MarshalOptions{UseProtoNames: true},
  2465  		input: &pb2.Nests{
  2466  			OptNested: &pb2.Nested{},
  2467  			Optgroup: &pb2.Nests_OptGroup{
  2468  				OptString: proto.String("inside a group"),
  2469  				OptNested: &pb2.Nested{
  2470  					OptString: proto.String("nested message inside a group"),
  2471  				},
  2472  				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
  2473  					OptFixed32: proto.Uint32(47),
  2474  				},
  2475  			},
  2476  			Rptgroup: []*pb2.Nests_RptGroup{
  2477  				{
  2478  					RptString: []string{"hello", "world"},
  2479  				},
  2480  			},
  2481  		},
  2482  		want: `{
  2483    "opt_nested": {},
  2484    "OptGroup": {
  2485      "opt_string": "inside a group",
  2486      "opt_nested": {
  2487        "opt_string": "nested message inside a group"
  2488      },
  2489      "OptNestedGroup": {
  2490        "opt_fixed32": 47
  2491      }
  2492    },
  2493    "RptGroup": [
  2494      {
  2495        "rpt_string": [
  2496          "hello",
  2497          "world"
  2498        ]
  2499      }
  2500    ]
  2501  }`,
  2502  	}}
  2503  
  2504  	for _, tt := range tests {
  2505  		tt := tt
  2506  		if tt.skip {
  2507  			continue
  2508  		}
  2509  		t.Run(tt.desc, func(t *testing.T) {
  2510  			// Use 2-space indentation on all MarshalOptions.
  2511  			tt.mo.Indent = "  "
  2512  			b, err := tt.mo.Marshal(tt.input)
  2513  			if err != nil && !tt.wantErr {
  2514  				t.Errorf("Marshal() returned error: %v\n", err)
  2515  			}
  2516  			if err == nil && tt.wantErr {
  2517  				t.Errorf("Marshal() got nil error, want error\n")
  2518  			}
  2519  			got := string(b)
  2520  			if got != tt.want {
  2521  				t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
  2522  				if diff := cmp.Diff(tt.want, got); diff != "" {
  2523  					t.Errorf("Marshal() diff -want +got\n%v\n", diff)
  2524  				}
  2525  			}
  2526  		})
  2527  	}
  2528  }
  2529  
  2530  func TestEncodeAppend(t *testing.T) {
  2531  	want := []byte("prefix")
  2532  	got := append([]byte(nil), want...)
  2533  	got, err := protojson.MarshalOptions{}.MarshalAppend(got, &pb3.Scalars{
  2534  		SString: "value",
  2535  	})
  2536  	if err != nil {
  2537  		t.Fatal(err)
  2538  	}
  2539  	if !bytes.HasPrefix(got, want) {
  2540  		t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
  2541  	}
  2542  }
  2543  
  2544  func TestMarshalAppendAllocations(t *testing.T) {
  2545  	m := &pb3.Scalars{SInt32: 1}
  2546  	const count = 1000
  2547  	size := 12
  2548  	b := make([]byte, size)
  2549  	// AllocsPerRun returns an integral value.
  2550  	marshalAllocs := testing.AllocsPerRun(count, func() {
  2551  		_, err := protojson.MarshalOptions{}.MarshalAppend(b[:0], m)
  2552  		if err != nil {
  2553  			t.Fatal(err)
  2554  		}
  2555  	})
  2556  	b = nil
  2557  	marshalAppendAllocs := testing.AllocsPerRun(count, func() {
  2558  		var err error
  2559  		b, err = protojson.MarshalOptions{}.MarshalAppend(b, m)
  2560  		if err != nil {
  2561  			t.Fatal(err)
  2562  		}
  2563  	})
  2564  	if marshalAllocs != marshalAppendAllocs {
  2565  		t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
  2566  		t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
  2567  		t.Errorf("expect amortized allocs/op to be identical")
  2568  	}
  2569  }
  2570  

View as plain text