github.com/whiteCcinn/protobuf-go@v1.0.9/encoding/prototext/encode_test.go (about)

     1  // Copyright 2018 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 prototext_test
     6  
     7  import (
     8  	"math"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  
    13  	"github.com/whiteCcinn/protobuf-go/encoding/prototext"
    14  	"github.com/whiteCcinn/protobuf-go/internal/detrand"
    15  	"github.com/whiteCcinn/protobuf-go/internal/flags"
    16  	"github.com/whiteCcinn/protobuf-go/proto"
    17  	"github.com/whiteCcinn/protobuf-go/reflect/protoregistry"
    18  	"github.com/whiteCcinn/protobuf-go/testing/protopack"
    19  
    20  	pb2 "github.com/whiteCcinn/protobuf-go/internal/testprotos/textpb2"
    21  	pb3 "github.com/whiteCcinn/protobuf-go/internal/testprotos/textpb3"
    22  	"github.com/whiteCcinn/protobuf-go/types/known/anypb"
    23  )
    24  
    25  func init() {
    26  	// Disable detrand to enable direct comparisons on outputs.
    27  	detrand.Disable()
    28  }
    29  
    30  func TestMarshal(t *testing.T) {
    31  	tests := []struct {
    32  		desc    string
    33  		mo      prototext.MarshalOptions
    34  		input   proto.Message
    35  		want    string
    36  		wantErr bool // TODO: Verify error message content.
    37  		skip    bool
    38  	}{{
    39  		desc:  "proto2 optional scalars not set",
    40  		input: &pb2.Scalars{},
    41  		want:  "",
    42  	}, {
    43  		desc:  "proto3 scalars not set",
    44  		input: &pb3.Scalars{},
    45  		want:  "",
    46  	}, {
    47  		desc:  "proto3 optional not set",
    48  		input: &pb3.Proto3Optional{},
    49  		want:  "",
    50  	}, {
    51  		desc: "proto2 optional scalars set to zero values",
    52  		input: &pb2.Scalars{
    53  			OptBool:     proto.Bool(false),
    54  			OptInt32:    proto.Int32(0),
    55  			OptInt64:    proto.Int64(0),
    56  			OptUint32:   proto.Uint32(0),
    57  			OptUint64:   proto.Uint64(0),
    58  			OptSint32:   proto.Int32(0),
    59  			OptSint64:   proto.Int64(0),
    60  			OptFixed32:  proto.Uint32(0),
    61  			OptFixed64:  proto.Uint64(0),
    62  			OptSfixed32: proto.Int32(0),
    63  			OptSfixed64: proto.Int64(0),
    64  			OptFloat:    proto.Float32(0),
    65  			OptDouble:   proto.Float64(0),
    66  			OptBytes:    []byte{},
    67  			OptString:   proto.String(""),
    68  		},
    69  		want: `opt_bool: false
    70  opt_int32: 0
    71  opt_int64: 0
    72  opt_uint32: 0
    73  opt_uint64: 0
    74  opt_sint32: 0
    75  opt_sint64: 0
    76  opt_fixed32: 0
    77  opt_fixed64: 0
    78  opt_sfixed32: 0
    79  opt_sfixed64: 0
    80  opt_float: 0
    81  opt_double: 0
    82  opt_bytes: ""
    83  opt_string: ""
    84  `,
    85  	}, {
    86  		desc: "proto3 optional set to zero values",
    87  		input: &pb3.Proto3Optional{
    88  			OptBool:    proto.Bool(false),
    89  			OptInt32:   proto.Int32(0),
    90  			OptInt64:   proto.Int64(0),
    91  			OptUint32:  proto.Uint32(0),
    92  			OptUint64:  proto.Uint64(0),
    93  			OptFloat:   proto.Float32(0),
    94  			OptDouble:  proto.Float64(0),
    95  			OptString:  proto.String(""),
    96  			OptBytes:   []byte{},
    97  			OptEnum:    pb3.Enum_ZERO.Enum(),
    98  			OptMessage: &pb3.Nested{},
    99  		},
   100  		want: `opt_bool: false
   101  opt_int32: 0
   102  opt_int64: 0
   103  opt_uint32: 0
   104  opt_uint64: 0
   105  opt_float: 0
   106  opt_double: 0
   107  opt_string: ""
   108  opt_bytes: ""
   109  opt_enum: ZERO
   110  opt_message: {}
   111  `,
   112  	}, {
   113  		desc: "proto3 scalars set to zero values",
   114  		input: &pb3.Scalars{
   115  			SBool:     false,
   116  			SInt32:    0,
   117  			SInt64:    0,
   118  			SUint32:   0,
   119  			SUint64:   0,
   120  			SSint32:   0,
   121  			SSint64:   0,
   122  			SFixed32:  0,
   123  			SFixed64:  0,
   124  			SSfixed32: 0,
   125  			SSfixed64: 0,
   126  			SFloat:    0,
   127  			SDouble:   0,
   128  			SBytes:    []byte{},
   129  			SString:   "",
   130  		},
   131  		want: "",
   132  	}, {
   133  		desc: "proto2 optional scalars set to some values",
   134  		input: &pb2.Scalars{
   135  			OptBool:     proto.Bool(true),
   136  			OptInt32:    proto.Int32(0xff),
   137  			OptInt64:    proto.Int64(0xdeadbeef),
   138  			OptUint32:   proto.Uint32(47),
   139  			OptUint64:   proto.Uint64(0xdeadbeef),
   140  			OptSint32:   proto.Int32(-1001),
   141  			OptSint64:   proto.Int64(-0xffff),
   142  			OptFixed64:  proto.Uint64(64),
   143  			OptSfixed32: proto.Int32(-32),
   144  			OptFloat:    proto.Float32(1.02),
   145  			OptDouble:   proto.Float64(1.0199999809265137),
   146  			OptBytes:    []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
   147  			OptString:   proto.String("谷歌"),
   148  		},
   149  		want: `opt_bool: true
   150  opt_int32: 255
   151  opt_int64: 3735928559
   152  opt_uint32: 47
   153  opt_uint64: 3735928559
   154  opt_sint32: -1001
   155  opt_sint64: -65535
   156  opt_fixed64: 64
   157  opt_sfixed32: -32
   158  opt_float: 1.02
   159  opt_double: 1.0199999809265137
   160  opt_bytes: "谷歌"
   161  opt_string: "谷歌"
   162  `,
   163  	}, {
   164  		desc: "proto2 string with invalid UTF-8",
   165  		input: &pb2.Scalars{
   166  			OptString: proto.String("abc\xff"),
   167  		},
   168  		want: `opt_string: "abc\xff"
   169  `,
   170  	}, {
   171  		desc: "proto3 string with invalid UTF-8",
   172  		input: &pb3.Scalars{
   173  			SString: "abc\xff",
   174  		},
   175  		wantErr: true,
   176  	}, {
   177  		desc: "float nan",
   178  		input: &pb3.Scalars{
   179  			SFloat: float32(math.NaN()),
   180  		},
   181  		want: "s_float: nan\n",
   182  	}, {
   183  		desc: "float positive infinity",
   184  		input: &pb3.Scalars{
   185  			SFloat: float32(math.Inf(1)),
   186  		},
   187  		want: "s_float: inf\n",
   188  	}, {
   189  		desc: "float negative infinity",
   190  		input: &pb3.Scalars{
   191  			SFloat: float32(math.Inf(-1)),
   192  		},
   193  		want: "s_float: -inf\n",
   194  	}, {
   195  		desc: "double nan",
   196  		input: &pb3.Scalars{
   197  			SDouble: math.NaN(),
   198  		},
   199  		want: "s_double: nan\n",
   200  	}, {
   201  		desc: "double positive infinity",
   202  		input: &pb3.Scalars{
   203  			SDouble: math.Inf(1),
   204  		},
   205  		want: "s_double: inf\n",
   206  	}, {
   207  		desc: "double negative infinity",
   208  		input: &pb3.Scalars{
   209  			SDouble: math.Inf(-1),
   210  		},
   211  		want: "s_double: -inf\n",
   212  	}, {
   213  		desc:  "proto2 enum not set",
   214  		input: &pb2.Enums{},
   215  		want:  "",
   216  	}, {
   217  		desc: "proto2 enum set to zero value",
   218  		input: &pb2.Enums{
   219  			OptEnum:       pb2.Enum(0).Enum(),
   220  			OptNestedEnum: pb2.Enums_NestedEnum(0).Enum(),
   221  		},
   222  		want: `opt_enum: 0
   223  opt_nested_enum: 0
   224  `,
   225  	}, {
   226  		desc: "proto2 enum",
   227  		input: &pb2.Enums{
   228  			OptEnum:       pb2.Enum_ONE.Enum(),
   229  			OptNestedEnum: pb2.Enums_UNO.Enum(),
   230  		},
   231  		want: `opt_enum: ONE
   232  opt_nested_enum: UNO
   233  `,
   234  	}, {
   235  		desc: "proto2 enum set to numeric values",
   236  		input: &pb2.Enums{
   237  			OptEnum:       pb2.Enum(2).Enum(),
   238  			OptNestedEnum: pb2.Enums_NestedEnum(2).Enum(),
   239  		},
   240  		want: `opt_enum: TWO
   241  opt_nested_enum: DOS
   242  `,
   243  	}, {
   244  		desc: "proto2 enum set to unnamed numeric values",
   245  		input: &pb2.Enums{
   246  			OptEnum:       pb2.Enum(101).Enum(),
   247  			OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(),
   248  		},
   249  		want: `opt_enum: 101
   250  opt_nested_enum: -101
   251  `,
   252  	}, {
   253  		desc:  "proto3 enum not set",
   254  		input: &pb3.Enums{},
   255  		want:  "",
   256  	}, {
   257  		desc: "proto3 enum set to zero value",
   258  		input: &pb3.Enums{
   259  			SEnum:       pb3.Enum_ZERO,
   260  			SNestedEnum: pb3.Enums_CERO,
   261  		},
   262  		want: "",
   263  	}, {
   264  		desc: "proto3 enum",
   265  		input: &pb3.Enums{
   266  			SEnum:       pb3.Enum_ONE,
   267  			SNestedEnum: pb3.Enums_UNO,
   268  		},
   269  		want: `s_enum: ONE
   270  s_nested_enum: UNO
   271  `,
   272  	}, {
   273  		desc: "proto3 enum set to numeric values",
   274  		input: &pb3.Enums{
   275  			SEnum:       2,
   276  			SNestedEnum: 2,
   277  		},
   278  		want: `s_enum: TWO
   279  s_nested_enum: DOS
   280  `,
   281  	}, {
   282  		desc: "proto3 enum set to unnamed numeric values",
   283  		input: &pb3.Enums{
   284  			SEnum:       -47,
   285  			SNestedEnum: 47,
   286  		},
   287  		want: `s_enum: -47
   288  s_nested_enum: 47
   289  `,
   290  	}, {
   291  		desc:  "proto2 nested message not set",
   292  		input: &pb2.Nests{},
   293  		want:  "",
   294  	}, {
   295  		desc: "proto2 nested message set to empty",
   296  		input: &pb2.Nests{
   297  			OptNested: &pb2.Nested{},
   298  			Optgroup:  &pb2.Nests_OptGroup{},
   299  		},
   300  		want: `opt_nested: {}
   301  OptGroup: {}
   302  `,
   303  	}, {
   304  		desc: "proto2 nested messages",
   305  		input: &pb2.Nests{
   306  			OptNested: &pb2.Nested{
   307  				OptString: proto.String("nested message"),
   308  				OptNested: &pb2.Nested{
   309  					OptString: proto.String("another nested message"),
   310  				},
   311  			},
   312  		},
   313  		want: `opt_nested: {
   314    opt_string: "nested message"
   315    opt_nested: {
   316      opt_string: "another nested message"
   317    }
   318  }
   319  `,
   320  	}, {
   321  		desc: "proto2 groups",
   322  		input: &pb2.Nests{
   323  			Optgroup: &pb2.Nests_OptGroup{
   324  				OptString: proto.String("inside a group"),
   325  				OptNested: &pb2.Nested{
   326  					OptString: proto.String("nested message inside a group"),
   327  				},
   328  				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
   329  					OptFixed32: proto.Uint32(47),
   330  				},
   331  			},
   332  		},
   333  		want: `OptGroup: {
   334    opt_string: "inside a group"
   335    opt_nested: {
   336      opt_string: "nested message inside a group"
   337    }
   338    OptNestedGroup: {
   339      opt_fixed32: 47
   340    }
   341  }
   342  `,
   343  	}, {
   344  		desc:  "proto3 nested message not set",
   345  		input: &pb3.Nests{},
   346  		want:  "",
   347  	}, {
   348  		desc: "proto3 nested message set to empty",
   349  		input: &pb3.Nests{
   350  			SNested: &pb3.Nested{},
   351  		},
   352  		want: "s_nested: {}\n",
   353  	}, {
   354  		desc: "proto3 nested message",
   355  		input: &pb3.Nests{
   356  			SNested: &pb3.Nested{
   357  				SString: "nested message",
   358  				SNested: &pb3.Nested{
   359  					SString: "another nested message",
   360  				},
   361  			},
   362  		},
   363  		want: `s_nested: {
   364    s_string: "nested message"
   365    s_nested: {
   366      s_string: "another nested message"
   367    }
   368  }
   369  `,
   370  	}, {
   371  		desc: "proto3 nested message contains invalid UTF-8",
   372  		input: &pb3.Nests{
   373  			SNested: &pb3.Nested{
   374  				SString: "abc\xff",
   375  			},
   376  		},
   377  		wantErr: true,
   378  	}, {
   379  		desc:  "oneof not set",
   380  		input: &pb3.Oneofs{},
   381  		want:  "",
   382  	}, {
   383  		desc: "oneof set to empty string",
   384  		input: &pb3.Oneofs{
   385  			Union: &pb3.Oneofs_OneofString{},
   386  		},
   387  		want: `oneof_string: ""
   388  `,
   389  	}, {
   390  		desc: "oneof set to string",
   391  		input: &pb3.Oneofs{
   392  			Union: &pb3.Oneofs_OneofString{
   393  				OneofString: "hello",
   394  			},
   395  		},
   396  		want: `oneof_string: "hello"
   397  `,
   398  	}, {
   399  		desc: "oneof set to enum",
   400  		input: &pb3.Oneofs{
   401  			Union: &pb3.Oneofs_OneofEnum{
   402  				OneofEnum: pb3.Enum_ZERO,
   403  			},
   404  		},
   405  		want: `oneof_enum: ZERO
   406  `,
   407  	}, {
   408  		desc: "oneof set to empty message",
   409  		input: &pb3.Oneofs{
   410  			Union: &pb3.Oneofs_OneofNested{
   411  				OneofNested: &pb3.Nested{},
   412  			},
   413  		},
   414  		want: "oneof_nested: {}\n",
   415  	}, {
   416  		desc: "oneof set to message",
   417  		input: &pb3.Oneofs{
   418  			Union: &pb3.Oneofs_OneofNested{
   419  				OneofNested: &pb3.Nested{
   420  					SString: "nested message",
   421  				},
   422  			},
   423  		},
   424  		want: `oneof_nested: {
   425    s_string: "nested message"
   426  }
   427  `,
   428  	}, {
   429  		desc:  "repeated fields not set",
   430  		input: &pb2.Repeats{},
   431  		want:  "",
   432  	}, {
   433  		desc: "repeated fields set to empty slices",
   434  		input: &pb2.Repeats{
   435  			RptBool:   []bool{},
   436  			RptInt32:  []int32{},
   437  			RptInt64:  []int64{},
   438  			RptUint32: []uint32{},
   439  			RptUint64: []uint64{},
   440  			RptFloat:  []float32{},
   441  			RptDouble: []float64{},
   442  			RptBytes:  [][]byte{},
   443  		},
   444  		want: "",
   445  	}, {
   446  		desc: "repeated fields set to some values",
   447  		input: &pb2.Repeats{
   448  			RptBool:   []bool{true, false, true, true},
   449  			RptInt32:  []int32{1, 6, 0, 0},
   450  			RptInt64:  []int64{-64, 47},
   451  			RptUint32: []uint32{0xff, 0xffff},
   452  			RptUint64: []uint64{0xdeadbeef},
   453  			RptFloat:  []float32{float32(math.NaN()), float32(math.Inf(1)), float32(math.Inf(-1)), 1.034},
   454  			RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308},
   455  			RptString: []string{"hello", "世界"},
   456  			RptBytes: [][]byte{
   457  				[]byte("hello"),
   458  				[]byte("\xe4\xb8\x96\xe7\x95\x8c"),
   459  			},
   460  		},
   461  		want: `rpt_bool: true
   462  rpt_bool: false
   463  rpt_bool: true
   464  rpt_bool: true
   465  rpt_int32: 1
   466  rpt_int32: 6
   467  rpt_int32: 0
   468  rpt_int32: 0
   469  rpt_int64: -64
   470  rpt_int64: 47
   471  rpt_uint32: 255
   472  rpt_uint32: 65535
   473  rpt_uint64: 3735928559
   474  rpt_float: nan
   475  rpt_float: inf
   476  rpt_float: -inf
   477  rpt_float: 1.034
   478  rpt_double: nan
   479  rpt_double: inf
   480  rpt_double: -inf
   481  rpt_double: 1.23e-308
   482  rpt_string: "hello"
   483  rpt_string: "世界"
   484  rpt_bytes: "hello"
   485  rpt_bytes: "世界"
   486  `,
   487  	}, {
   488  		desc: "repeated proto2 contains invalid UTF-8",
   489  		input: &pb2.Repeats{
   490  			RptString: []string{"abc\xff"},
   491  		},
   492  		want: `rpt_string: "abc\xff"
   493  `,
   494  	}, {
   495  		desc: "repeated proto3 contains invalid UTF-8",
   496  		input: &pb3.Repeats{
   497  			RptString: []string{"abc\xff"},
   498  		},
   499  		wantErr: true,
   500  	}, {
   501  		desc: "repeated enums",
   502  		input: &pb2.Enums{
   503  			RptEnum:       []pb2.Enum{pb2.Enum_ONE, 2, pb2.Enum_TEN, 42},
   504  			RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
   505  		},
   506  		want: `rpt_enum: ONE
   507  rpt_enum: TWO
   508  rpt_enum: TEN
   509  rpt_enum: 42
   510  rpt_nested_enum: DOS
   511  rpt_nested_enum: 47
   512  rpt_nested_enum: DIEZ
   513  `,
   514  	}, {
   515  		desc: "repeated messages set to empty",
   516  		input: &pb2.Nests{
   517  			RptNested: []*pb2.Nested{},
   518  			Rptgroup:  []*pb2.Nests_RptGroup{},
   519  		},
   520  		want: "",
   521  	}, {
   522  		desc: "repeated messages",
   523  		input: &pb2.Nests{
   524  			RptNested: []*pb2.Nested{
   525  				{
   526  					OptString: proto.String("repeat nested one"),
   527  				},
   528  				{
   529  					OptString: proto.String("repeat nested two"),
   530  					OptNested: &pb2.Nested{
   531  						OptString: proto.String("inside repeat nested two"),
   532  					},
   533  				},
   534  				{},
   535  			},
   536  		},
   537  		want: `rpt_nested: {
   538    opt_string: "repeat nested one"
   539  }
   540  rpt_nested: {
   541    opt_string: "repeat nested two"
   542    opt_nested: {
   543      opt_string: "inside repeat nested two"
   544    }
   545  }
   546  rpt_nested: {}
   547  `,
   548  	}, {
   549  		desc: "repeated messages contains nil value",
   550  		input: &pb2.Nests{
   551  			RptNested: []*pb2.Nested{nil, {}},
   552  		},
   553  		want: `rpt_nested: {}
   554  rpt_nested: {}
   555  `,
   556  	}, {
   557  		desc: "repeated groups",
   558  		input: &pb2.Nests{
   559  			Rptgroup: []*pb2.Nests_RptGroup{
   560  				{
   561  					RptString: []string{"hello", "world"},
   562  				},
   563  				{},
   564  				nil,
   565  			},
   566  		},
   567  		want: `RptGroup: {
   568    rpt_string: "hello"
   569    rpt_string: "world"
   570  }
   571  RptGroup: {}
   572  RptGroup: {}
   573  `,
   574  	}, {
   575  		desc:  "map fields not set",
   576  		input: &pb3.Maps{},
   577  		want:  "",
   578  	}, {
   579  		desc: "map fields set to empty",
   580  		input: &pb3.Maps{
   581  			Int32ToStr:   map[int32]string{},
   582  			BoolToUint32: map[bool]uint32{},
   583  			Uint64ToEnum: map[uint64]pb3.Enum{},
   584  			StrToNested:  map[string]*pb3.Nested{},
   585  			StrToOneofs:  map[string]*pb3.Oneofs{},
   586  		},
   587  		want: "",
   588  	}, {
   589  		desc: "map fields 1",
   590  		input: &pb3.Maps{
   591  			Int32ToStr: map[int32]string{
   592  				-101: "-101",
   593  				0xff: "0xff",
   594  				0:    "zero",
   595  			},
   596  			BoolToUint32: map[bool]uint32{
   597  				true:  42,
   598  				false: 101,
   599  			},
   600  		},
   601  		want: `int32_to_str: {
   602    key: -101
   603    value: "-101"
   604  }
   605  int32_to_str: {
   606    key: 0
   607    value: "zero"
   608  }
   609  int32_to_str: {
   610    key: 255
   611    value: "0xff"
   612  }
   613  bool_to_uint32: {
   614    key: false
   615    value: 101
   616  }
   617  bool_to_uint32: {
   618    key: true
   619    value: 42
   620  }
   621  `,
   622  	}, {
   623  		desc: "map fields 2",
   624  		input: &pb3.Maps{
   625  			Uint64ToEnum: map[uint64]pb3.Enum{
   626  				1:  pb3.Enum_ONE,
   627  				2:  pb3.Enum_TWO,
   628  				10: pb3.Enum_TEN,
   629  				47: 47,
   630  			},
   631  		},
   632  		want: `uint64_to_enum: {
   633    key: 1
   634    value: ONE
   635  }
   636  uint64_to_enum: {
   637    key: 2
   638    value: TWO
   639  }
   640  uint64_to_enum: {
   641    key: 10
   642    value: TEN
   643  }
   644  uint64_to_enum: {
   645    key: 47
   646    value: 47
   647  }
   648  `,
   649  	}, {
   650  		desc: "map fields 3",
   651  		input: &pb3.Maps{
   652  			StrToNested: map[string]*pb3.Nested{
   653  				"nested": &pb3.Nested{
   654  					SString: "nested in a map",
   655  				},
   656  			},
   657  		},
   658  		want: `str_to_nested: {
   659    key: "nested"
   660    value: {
   661      s_string: "nested in a map"
   662    }
   663  }
   664  `,
   665  	}, {
   666  		desc: "map fields 4",
   667  		input: &pb3.Maps{
   668  			StrToOneofs: map[string]*pb3.Oneofs{
   669  				"string": &pb3.Oneofs{
   670  					Union: &pb3.Oneofs_OneofString{
   671  						OneofString: "hello",
   672  					},
   673  				},
   674  				"nested": &pb3.Oneofs{
   675  					Union: &pb3.Oneofs_OneofNested{
   676  						OneofNested: &pb3.Nested{
   677  							SString: "nested oneof in map field value",
   678  						},
   679  					},
   680  				},
   681  			},
   682  		},
   683  		want: `str_to_oneofs: {
   684    key: "nested"
   685    value: {
   686      oneof_nested: {
   687        s_string: "nested oneof in map field value"
   688      }
   689    }
   690  }
   691  str_to_oneofs: {
   692    key: "string"
   693    value: {
   694      oneof_string: "hello"
   695    }
   696  }
   697  `,
   698  	}, {
   699  		desc: "proto2 map field value contains invalid UTF-8",
   700  		input: &pb2.Maps{
   701  			Int32ToStr: map[int32]string{
   702  				101: "abc\xff",
   703  			},
   704  		},
   705  		want: `int32_to_str: {
   706    key: 101
   707    value: "abc\xff"
   708  }
   709  `,
   710  	}, {
   711  		desc: "proto2 map field key contains invalid UTF-8",
   712  		input: &pb2.Maps{
   713  			StrToNested: map[string]*pb2.Nested{
   714  				"abc\xff": {},
   715  			},
   716  		},
   717  		want: `str_to_nested: {
   718    key: "abc\xff"
   719    value: {}
   720  }
   721  `,
   722  	}, {
   723  		desc: "proto3 map field value contains invalid UTF-8",
   724  		input: &pb3.Maps{
   725  			Int32ToStr: map[int32]string{
   726  				101: "abc\xff",
   727  			},
   728  		},
   729  		wantErr: true,
   730  	}, {
   731  		desc: "proto3 map field key contains invalid UTF-8",
   732  		input: &pb3.Maps{
   733  			StrToNested: map[string]*pb3.Nested{
   734  				"abc\xff": {},
   735  			},
   736  		},
   737  		wantErr: true,
   738  	}, {
   739  		desc: "map field contains nil value",
   740  		input: &pb3.Maps{
   741  			StrToNested: map[string]*pb3.Nested{
   742  				"nil": nil,
   743  			},
   744  		},
   745  		want: `str_to_nested: {
   746    key: "nil"
   747    value: {}
   748  }
   749  `,
   750  	}, {
   751  		desc:    "required fields not set",
   752  		input:   &pb2.Requireds{},
   753  		want:    "",
   754  		wantErr: true,
   755  	}, {
   756  		desc: "required fields partially set",
   757  		input: &pb2.Requireds{
   758  			ReqBool:     proto.Bool(false),
   759  			ReqSfixed64: proto.Int64(0xbeefcafe),
   760  			ReqDouble:   proto.Float64(math.NaN()),
   761  			ReqString:   proto.String("hello"),
   762  			ReqEnum:     pb2.Enum_ONE.Enum(),
   763  		},
   764  		want: `req_bool: false
   765  req_sfixed64: 3203386110
   766  req_double: nan
   767  req_string: "hello"
   768  req_enum: ONE
   769  `,
   770  		wantErr: true,
   771  	}, {
   772  		desc: "required fields not set with AllowPartial",
   773  		mo:   prototext.MarshalOptions{AllowPartial: true},
   774  		input: &pb2.Requireds{
   775  			ReqBool:     proto.Bool(false),
   776  			ReqSfixed64: proto.Int64(0xbeefcafe),
   777  			ReqDouble:   proto.Float64(math.NaN()),
   778  			ReqString:   proto.String("hello"),
   779  			ReqEnum:     pb2.Enum_ONE.Enum(),
   780  		},
   781  		want: `req_bool: false
   782  req_sfixed64: 3203386110
   783  req_double: nan
   784  req_string: "hello"
   785  req_enum: ONE
   786  `,
   787  	}, {
   788  		desc: "required fields all set",
   789  		input: &pb2.Requireds{
   790  			ReqBool:     proto.Bool(false),
   791  			ReqSfixed64: proto.Int64(0),
   792  			ReqDouble:   proto.Float64(1.23),
   793  			ReqString:   proto.String(""),
   794  			ReqEnum:     pb2.Enum_ONE.Enum(),
   795  			ReqNested:   &pb2.Nested{},
   796  		},
   797  		want: `req_bool: false
   798  req_sfixed64: 0
   799  req_double: 1.23
   800  req_string: ""
   801  req_enum: ONE
   802  req_nested: {}
   803  `,
   804  	}, {
   805  		desc: "indirect required field",
   806  		input: &pb2.IndirectRequired{
   807  			OptNested: &pb2.NestedWithRequired{},
   808  		},
   809  		want:    "opt_nested: {}\n",
   810  		wantErr: true,
   811  	}, {
   812  		desc: "indirect required field with AllowPartial",
   813  		mo:   prototext.MarshalOptions{AllowPartial: true},
   814  		input: &pb2.IndirectRequired{
   815  			OptNested: &pb2.NestedWithRequired{},
   816  		},
   817  		want: "opt_nested: {}\n",
   818  	}, {
   819  		desc: "indirect required field in empty repeated",
   820  		input: &pb2.IndirectRequired{
   821  			RptNested: []*pb2.NestedWithRequired{},
   822  		},
   823  		want: "",
   824  	}, {
   825  		desc: "indirect required field in repeated",
   826  		input: &pb2.IndirectRequired{
   827  			RptNested: []*pb2.NestedWithRequired{
   828  				&pb2.NestedWithRequired{},
   829  			},
   830  		},
   831  		want:    "rpt_nested: {}\n",
   832  		wantErr: true,
   833  	}, {
   834  		desc: "indirect required field in repeated with AllowPartial",
   835  		mo:   prototext.MarshalOptions{AllowPartial: true},
   836  		input: &pb2.IndirectRequired{
   837  			RptNested: []*pb2.NestedWithRequired{
   838  				&pb2.NestedWithRequired{},
   839  			},
   840  		},
   841  		want: "rpt_nested: {}\n",
   842  	}, {
   843  		desc: "indirect required field in empty map",
   844  		input: &pb2.IndirectRequired{
   845  			StrToNested: map[string]*pb2.NestedWithRequired{},
   846  		},
   847  		want: "",
   848  	}, {
   849  		desc: "indirect required field in map",
   850  		input: &pb2.IndirectRequired{
   851  			StrToNested: map[string]*pb2.NestedWithRequired{
   852  				"fail": &pb2.NestedWithRequired{},
   853  			},
   854  		},
   855  		want: `str_to_nested: {
   856    key: "fail"
   857    value: {}
   858  }
   859  `,
   860  		wantErr: true,
   861  	}, {
   862  		desc: "indirect required field in map with AllowPartial",
   863  		mo:   prototext.MarshalOptions{AllowPartial: true},
   864  		input: &pb2.IndirectRequired{
   865  			StrToNested: map[string]*pb2.NestedWithRequired{
   866  				"fail": &pb2.NestedWithRequired{},
   867  			},
   868  		},
   869  		want: `str_to_nested: {
   870    key: "fail"
   871    value: {}
   872  }
   873  `,
   874  	}, {
   875  		desc: "indirect required field in oneof",
   876  		input: &pb2.IndirectRequired{
   877  			Union: &pb2.IndirectRequired_OneofNested{
   878  				OneofNested: &pb2.NestedWithRequired{},
   879  			},
   880  		},
   881  		want:    "oneof_nested: {}\n",
   882  		wantErr: true,
   883  	}, {
   884  		desc: "indirect required field in oneof with AllowPartial",
   885  		mo:   prototext.MarshalOptions{AllowPartial: true},
   886  		input: &pb2.IndirectRequired{
   887  			Union: &pb2.IndirectRequired_OneofNested{
   888  				OneofNested: &pb2.NestedWithRequired{},
   889  			},
   890  		},
   891  		want: "oneof_nested: {}\n",
   892  	}, {
   893  		desc: "unknown fields not printed",
   894  		input: func() proto.Message {
   895  			m := &pb2.Scalars{
   896  				OptString: proto.String("this message contains unknown fields"),
   897  			}
   898  			m.ProtoReflect().SetUnknown(protopack.Message{
   899  				protopack.Tag{101, protopack.VarintType}, protopack.Bool(true),
   900  				protopack.Tag{102, protopack.VarintType}, protopack.Varint(0xff),
   901  				protopack.Tag{103, protopack.Fixed32Type}, protopack.Uint32(47),
   902  				protopack.Tag{104, protopack.Fixed64Type}, protopack.Int64(0xdeadbeef),
   903  			}.Marshal())
   904  			return m
   905  		}(),
   906  		want: `opt_string: "this message contains unknown fields"
   907  `,
   908  	}, {
   909  		desc: "unknown varint and fixed types",
   910  		mo:   prototext.MarshalOptions{EmitUnknown: true},
   911  		input: func() proto.Message {
   912  			m := &pb2.Scalars{
   913  				OptString: proto.String("this message contains unknown fields"),
   914  			}
   915  			m.ProtoReflect().SetUnknown(protopack.Message{
   916  				protopack.Tag{101, protopack.VarintType}, protopack.Bool(true),
   917  				protopack.Tag{102, protopack.VarintType}, protopack.Varint(0xff),
   918  				protopack.Tag{103, protopack.Fixed32Type}, protopack.Uint32(0x47),
   919  				protopack.Tag{104, protopack.Fixed64Type}, protopack.Int64(0xdeadbeef),
   920  			}.Marshal())
   921  			return m
   922  		}(),
   923  		want: `opt_string: "this message contains unknown fields"
   924  101: 1
   925  102: 255
   926  103: 0x47
   927  104: 0xdeadbeef
   928  `,
   929  	}, {
   930  		desc: "unknown length-delimited",
   931  		mo:   prototext.MarshalOptions{EmitUnknown: true},
   932  		input: func() proto.Message {
   933  			m := new(pb2.Scalars)
   934  			m.ProtoReflect().SetUnknown(protopack.Message{
   935  				protopack.Tag{101, protopack.BytesType}, protopack.LengthPrefix{protopack.Bool(true), protopack.Bool(false)},
   936  				protopack.Tag{102, protopack.BytesType}, protopack.String("hello world"),
   937  				protopack.Tag{103, protopack.BytesType}, protopack.Bytes("\xe4\xb8\x96\xe7\x95\x8c"),
   938  			}.Marshal())
   939  			return m
   940  		}(),
   941  		want: `101: "\x01\x00"
   942  102: "hello world"
   943  103: "世界"
   944  `,
   945  	}, {
   946  		desc: "unknown group type",
   947  		mo:   prototext.MarshalOptions{EmitUnknown: true},
   948  		input: func() proto.Message {
   949  			m := new(pb2.Scalars)
   950  			m.ProtoReflect().SetUnknown(protopack.Message{
   951  				protopack.Tag{101, protopack.StartGroupType}, protopack.Tag{101, protopack.EndGroupType},
   952  				protopack.Tag{102, protopack.StartGroupType},
   953  				protopack.Tag{101, protopack.VarintType}, protopack.Bool(false),
   954  				protopack.Tag{102, protopack.BytesType}, protopack.String("inside a group"),
   955  				protopack.Tag{102, protopack.EndGroupType},
   956  			}.Marshal())
   957  			return m
   958  		}(),
   959  		want: `101: {}
   960  102: {
   961    101: 0
   962    102: "inside a group"
   963  }
   964  `,
   965  	}, {
   966  		desc: "unknown unpack repeated field",
   967  		mo:   prototext.MarshalOptions{EmitUnknown: true},
   968  		input: func() proto.Message {
   969  			m := new(pb2.Scalars)
   970  			m.ProtoReflect().SetUnknown(protopack.Message{
   971  				protopack.Tag{101, protopack.BytesType}, protopack.LengthPrefix{protopack.Bool(true), protopack.Bool(false), protopack.Bool(true)},
   972  				protopack.Tag{102, protopack.BytesType}, protopack.String("hello"),
   973  				protopack.Tag{101, protopack.VarintType}, protopack.Bool(true),
   974  				protopack.Tag{102, protopack.BytesType}, protopack.String("世界"),
   975  			}.Marshal())
   976  			return m
   977  		}(),
   978  		want: `101: "\x01\x00\x01"
   979  102: "hello"
   980  101: 1
   981  102: "世界"
   982  `,
   983  	}, {
   984  		desc: "extensions of non-repeated fields",
   985  		input: func() proto.Message {
   986  			m := &pb2.Extensions{
   987  				OptString: proto.String("non-extension field"),
   988  				OptBool:   proto.Bool(true),
   989  				OptInt32:  proto.Int32(42),
   990  			}
   991  			proto.SetExtension(m, pb2.E_OptExtBool, true)
   992  			proto.SetExtension(m, pb2.E_OptExtString, "extension field")
   993  			proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN)
   994  			proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{
   995  				OptString: proto.String("nested in an extension"),
   996  				OptNested: &pb2.Nested{
   997  					OptString: proto.String("another nested in an extension"),
   998  				},
   999  			})
  1000  			return m
  1001  		}(),
  1002  		want: `opt_string: "non-extension field"
  1003  opt_bool: true
  1004  opt_int32: 42
  1005  [pb2.opt_ext_bool]: true
  1006  [pb2.opt_ext_enum]: TEN
  1007  [pb2.opt_ext_nested]: {
  1008    opt_string: "nested in an extension"
  1009    opt_nested: {
  1010      opt_string: "another nested in an extension"
  1011    }
  1012  }
  1013  [pb2.opt_ext_string]: "extension field"
  1014  `,
  1015  	}, {
  1016  		desc: "proto2 extension field contains invalid UTF-8",
  1017  		input: func() proto.Message {
  1018  			m := &pb2.Extensions{}
  1019  			proto.SetExtension(m, pb2.E_OptExtString, "abc\xff")
  1020  			return m
  1021  		}(),
  1022  		want: `[pb2.opt_ext_string]: "abc\xff"
  1023  `,
  1024  	}, {
  1025  		desc: "extension partial returns error",
  1026  		input: func() proto.Message {
  1027  			m := &pb2.Extensions{}
  1028  			proto.SetExtension(m, pb2.E_OptExtPartial, &pb2.PartialRequired{
  1029  				OptString: proto.String("partial1"),
  1030  			})
  1031  			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtPartial, &pb2.PartialRequired{
  1032  				OptString: proto.String("partial2"),
  1033  			})
  1034  			return m
  1035  		}(),
  1036  		want: `[pb2.ExtensionsContainer.opt_ext_partial]: {
  1037    opt_string: "partial2"
  1038  }
  1039  [pb2.opt_ext_partial]: {
  1040    opt_string: "partial1"
  1041  }
  1042  `,
  1043  		wantErr: true,
  1044  	}, {
  1045  		desc: "extension partial with AllowPartial",
  1046  		mo:   prototext.MarshalOptions{AllowPartial: true},
  1047  		input: func() proto.Message {
  1048  			m := &pb2.Extensions{}
  1049  			proto.SetExtension(m, pb2.E_OptExtPartial, &pb2.PartialRequired{
  1050  				OptString: proto.String("partial1"),
  1051  			})
  1052  			return m
  1053  		}(),
  1054  		want: `[pb2.opt_ext_partial]: {
  1055    opt_string: "partial1"
  1056  }
  1057  `,
  1058  	}, {
  1059  		desc: "extensions of repeated fields",
  1060  		input: func() proto.Message {
  1061  			m := &pb2.Extensions{}
  1062  			proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
  1063  			proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47})
  1064  			proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{
  1065  				&pb2.Nested{OptString: proto.String("one")},
  1066  				&pb2.Nested{OptString: proto.String("two")},
  1067  				&pb2.Nested{OptString: proto.String("three")},
  1068  			})
  1069  			return m
  1070  		}(),
  1071  		want: `[pb2.rpt_ext_enum]: TEN
  1072  [pb2.rpt_ext_enum]: 101
  1073  [pb2.rpt_ext_enum]: ONE
  1074  [pb2.rpt_ext_fixed32]: 42
  1075  [pb2.rpt_ext_fixed32]: 47
  1076  [pb2.rpt_ext_nested]: {
  1077    opt_string: "one"
  1078  }
  1079  [pb2.rpt_ext_nested]: {
  1080    opt_string: "two"
  1081  }
  1082  [pb2.rpt_ext_nested]: {
  1083    opt_string: "three"
  1084  }
  1085  `,
  1086  	}, {
  1087  		desc: "extensions of non-repeated fields in another message",
  1088  		input: func() proto.Message {
  1089  			m := &pb2.Extensions{}
  1090  			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true)
  1091  			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field")
  1092  			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN)
  1093  			proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{
  1094  				OptString: proto.String("nested in an extension"),
  1095  				OptNested: &pb2.Nested{
  1096  					OptString: proto.String("another nested in an extension"),
  1097  				},
  1098  			})
  1099  			return m
  1100  		}(),
  1101  		want: `[pb2.ExtensionsContainer.opt_ext_bool]: true
  1102  [pb2.ExtensionsContainer.opt_ext_enum]: TEN
  1103  [pb2.ExtensionsContainer.opt_ext_nested]: {
  1104    opt_string: "nested in an extension"
  1105    opt_nested: {
  1106      opt_string: "another nested in an extension"
  1107    }
  1108  }
  1109  [pb2.ExtensionsContainer.opt_ext_string]: "extension field"
  1110  `,
  1111  	}, {
  1112  		desc: "extensions of repeated fields in another message",
  1113  		input: func() proto.Message {
  1114  			m := &pb2.Extensions{
  1115  				OptString: proto.String("non-extension field"),
  1116  				OptBool:   proto.Bool(true),
  1117  				OptInt32:  proto.Int32(42),
  1118  			}
  1119  			proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE})
  1120  			proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"})
  1121  			proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{
  1122  				&pb2.Nested{OptString: proto.String("one")},
  1123  				&pb2.Nested{OptString: proto.String("two")},
  1124  				&pb2.Nested{OptString: proto.String("three")},
  1125  			})
  1126  			return m
  1127  		}(),
  1128  		want: `opt_string: "non-extension field"
  1129  opt_bool: true
  1130  opt_int32: 42
  1131  [pb2.ExtensionsContainer.rpt_ext_enum]: TEN
  1132  [pb2.ExtensionsContainer.rpt_ext_enum]: 101
  1133  [pb2.ExtensionsContainer.rpt_ext_enum]: ONE
  1134  [pb2.ExtensionsContainer.rpt_ext_nested]: {
  1135    opt_string: "one"
  1136  }
  1137  [pb2.ExtensionsContainer.rpt_ext_nested]: {
  1138    opt_string: "two"
  1139  }
  1140  [pb2.ExtensionsContainer.rpt_ext_nested]: {
  1141    opt_string: "three"
  1142  }
  1143  [pb2.ExtensionsContainer.rpt_ext_string]: "hello"
  1144  [pb2.ExtensionsContainer.rpt_ext_string]: "world"
  1145  `,
  1146  	}, {
  1147  		desc: "MessageSet",
  1148  		input: func() proto.Message {
  1149  			m := &pb2.MessageSet{}
  1150  			proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{
  1151  				OptString: proto.String("a messageset extension"),
  1152  			})
  1153  			proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{
  1154  				OptString: proto.String("not a messageset extension"),
  1155  			})
  1156  			proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{
  1157  				OptString: proto.String("just a regular extension"),
  1158  			})
  1159  			return m
  1160  		}(),
  1161  		want: `[pb2.MessageSetExtension.ext_nested]: {
  1162    opt_string: "just a regular extension"
  1163  }
  1164  [pb2.MessageSetExtension]: {
  1165    opt_string: "a messageset extension"
  1166  }
  1167  [pb2.MessageSetExtension.not_message_set_extension]: {
  1168    opt_string: "not a messageset extension"
  1169  }
  1170  `,
  1171  		skip: !flags.ProtoLegacy,
  1172  	}, {
  1173  		desc: "not real MessageSet 1",
  1174  		input: func() proto.Message {
  1175  			m := &pb2.FakeMessageSet{}
  1176  			proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{
  1177  				OptString: proto.String("not a messageset extension"),
  1178  			})
  1179  			return m
  1180  		}(),
  1181  		want: `[pb2.FakeMessageSetExtension.message_set_extension]: {
  1182    opt_string: "not a messageset extension"
  1183  }
  1184  `,
  1185  		skip: !flags.ProtoLegacy,
  1186  	}, {
  1187  		desc: "not real MessageSet 2",
  1188  		input: func() proto.Message {
  1189  			m := &pb2.MessageSet{}
  1190  			proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{
  1191  				OptString: proto.String("another not a messageset extension"),
  1192  			})
  1193  			return m
  1194  		}(),
  1195  		want: `[pb2.message_set_extension]: {
  1196    opt_string: "another not a messageset extension"
  1197  }
  1198  `,
  1199  		skip: !flags.ProtoLegacy,
  1200  	}, {
  1201  		desc: "Any not expanded",
  1202  		mo: prototext.MarshalOptions{
  1203  			Resolver: new(protoregistry.Types),
  1204  		},
  1205  		input: func() proto.Message {
  1206  			m := &pb2.Nested{
  1207  				OptString: proto.String("embedded inside Any"),
  1208  				OptNested: &pb2.Nested{
  1209  					OptString: proto.String("inception"),
  1210  				},
  1211  			}
  1212  			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
  1213  			if err != nil {
  1214  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1215  			}
  1216  			return &anypb.Any{
  1217  				TypeUrl: "pb2.Nested",
  1218  				Value:   b,
  1219  			}
  1220  		}(),
  1221  		want: `type_url: "pb2.Nested"
  1222  value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
  1223  `,
  1224  	}, {
  1225  		desc: "Any expanded",
  1226  		input: func() proto.Message {
  1227  			m := &pb2.Nested{
  1228  				OptString: proto.String("embedded inside Any"),
  1229  				OptNested: &pb2.Nested{
  1230  					OptString: proto.String("inception"),
  1231  				},
  1232  			}
  1233  			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
  1234  			if err != nil {
  1235  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1236  			}
  1237  			return &anypb.Any{
  1238  				TypeUrl: "foo/pb2.Nested",
  1239  				Value:   b,
  1240  			}
  1241  		}(),
  1242  		want: `[foo/pb2.Nested]: {
  1243    opt_string: "embedded inside Any"
  1244    opt_nested: {
  1245      opt_string: "inception"
  1246    }
  1247  }
  1248  `,
  1249  	}, {
  1250  		desc: "Any expanded with missing required",
  1251  		input: func() proto.Message {
  1252  			m := &pb2.PartialRequired{
  1253  				OptString: proto.String("embedded inside Any"),
  1254  			}
  1255  			b, err := proto.MarshalOptions{
  1256  				AllowPartial:  true,
  1257  				Deterministic: true,
  1258  			}.Marshal(m)
  1259  			if err != nil {
  1260  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1261  			}
  1262  			return &anypb.Any{
  1263  				TypeUrl: string(m.ProtoReflect().Descriptor().FullName()),
  1264  				Value:   b,
  1265  			}
  1266  		}(),
  1267  		want: `[pb2.PartialRequired]: {
  1268    opt_string: "embedded inside Any"
  1269  }
  1270  `,
  1271  	}, {
  1272  		desc: "Any with invalid value",
  1273  		input: &anypb.Any{
  1274  			TypeUrl: "foo/pb2.Nested",
  1275  			Value:   []byte("\x80"),
  1276  		},
  1277  		want: `type_url: "foo/pb2.Nested"
  1278  value: "\x80"
  1279  `,
  1280  	}, {
  1281  		desc: "Any expanded in another message",
  1282  		input: func() *pb2.KnownTypes {
  1283  			m1 := &pb2.Nested{
  1284  				OptString: proto.String("message inside Any of another Any field"),
  1285  			}
  1286  			b1, err := proto.MarshalOptions{Deterministic: true}.Marshal(m1)
  1287  			if err != nil {
  1288  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1289  			}
  1290  			m2 := &anypb.Any{
  1291  				TypeUrl: "pb2.Nested",
  1292  				Value:   b1,
  1293  			}
  1294  			b2, err := proto.MarshalOptions{Deterministic: true}.Marshal(m2)
  1295  			if err != nil {
  1296  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1297  			}
  1298  			return &pb2.KnownTypes{
  1299  				OptAny: &anypb.Any{
  1300  					TypeUrl: "google.protobuf.Any",
  1301  					Value:   b2,
  1302  				},
  1303  			}
  1304  		}(),
  1305  		want: `opt_any: {
  1306    [google.protobuf.Any]: {
  1307      [pb2.Nested]: {
  1308        opt_string: "message inside Any of another Any field"
  1309      }
  1310    }
  1311  }
  1312  `,
  1313  	}, {
  1314  		desc: "Any expanded with invalid UTF-8 in proto2",
  1315  		input: func() *pb2.KnownTypes {
  1316  			m := &pb2.Nested{
  1317  				OptString: proto.String("invalid UTF-8 abc\xff"),
  1318  			}
  1319  			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
  1320  			if err != nil {
  1321  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1322  			}
  1323  			return &pb2.KnownTypes{
  1324  				OptAny: &anypb.Any{
  1325  					TypeUrl: "pb2.Nested",
  1326  					Value:   b,
  1327  				},
  1328  			}
  1329  		}(),
  1330  		want: `opt_any: {
  1331    [pb2.Nested]: {
  1332      opt_string: "invalid UTF-8 abc\xff"
  1333    }
  1334  }
  1335  `,
  1336  	}, {
  1337  		desc: "Any not expanded due to invalid data",
  1338  		mo:   prototext.MarshalOptions{EmitASCII: true},
  1339  		input: func() *pb2.KnownTypes {
  1340  			return &pb2.KnownTypes{
  1341  				OptAny: &anypb.Any{
  1342  					TypeUrl: "pb3.Scalar",
  1343  					Value:   []byte("\xde\xad\xbe\xef"),
  1344  				},
  1345  			}
  1346  		}(),
  1347  		want: `opt_any: {
  1348    type_url: "pb3.Scalar"
  1349    value: "\u07ad\xbe\xef"
  1350  }
  1351  `,
  1352  	}, {
  1353  		desc: "Any inside Any expanded",
  1354  		input: func() *pb2.KnownTypes {
  1355  			m1 := &pb2.Nested{
  1356  				OptString: proto.String("invalid UTF-8 abc\xff"),
  1357  			}
  1358  			b1, err := proto.MarshalOptions{Deterministic: true}.Marshal(m1)
  1359  			if err != nil {
  1360  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1361  			}
  1362  			m2 := &anypb.Any{
  1363  				TypeUrl: "pb2.Nested",
  1364  				Value:   b1,
  1365  			}
  1366  			b2, err := proto.MarshalOptions{Deterministic: true}.Marshal(m2)
  1367  			if err != nil {
  1368  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1369  			}
  1370  			return &pb2.KnownTypes{
  1371  				OptAny: &anypb.Any{
  1372  					TypeUrl: "google.protobuf.Any",
  1373  					Value:   b2,
  1374  				},
  1375  			}
  1376  		}(),
  1377  		want: `opt_any: {
  1378    [google.protobuf.Any]: {
  1379      [pb2.Nested]: {
  1380        opt_string: "invalid UTF-8 abc\xff"
  1381      }
  1382    }
  1383  }
  1384  `,
  1385  	}, {
  1386  		desc: "Any inside Any not expanded due to invalid data",
  1387  		mo:   prototext.MarshalOptions{EmitASCII: true},
  1388  		input: func() *pb2.KnownTypes {
  1389  			m := &anypb.Any{
  1390  				TypeUrl: "pb2.Nested",
  1391  				Value:   []byte("\xde\xad\xbe\xef"),
  1392  			}
  1393  			b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
  1394  			if err != nil {
  1395  				t.Fatalf("error in binary marshaling message for Any.value: %v", err)
  1396  			}
  1397  			return &pb2.KnownTypes{
  1398  				OptAny: &anypb.Any{
  1399  					TypeUrl: "google.protobuf.Any",
  1400  					Value:   b,
  1401  				},
  1402  			}
  1403  		}(),
  1404  		want: `opt_any: {
  1405    [google.protobuf.Any]: {
  1406      type_url: "pb2.Nested"
  1407      value: "\u07ad\xbe\xef"
  1408    }
  1409  }
  1410  `,
  1411  	}}
  1412  
  1413  	for _, tt := range tests {
  1414  		tt := tt
  1415  		if tt.skip {
  1416  			continue
  1417  		}
  1418  		t.Run(tt.desc, func(t *testing.T) {
  1419  			// Use 2-space indentation on all MarshalOptions.
  1420  			tt.mo.Indent = "  "
  1421  			b, err := tt.mo.Marshal(tt.input)
  1422  			if err != nil && !tt.wantErr {
  1423  				t.Errorf("Marshal() returned error: %v\n", err)
  1424  			}
  1425  			if err == nil && tt.wantErr {
  1426  				t.Error("Marshal() got nil error, want error\n")
  1427  			}
  1428  			got := string(b)
  1429  			if tt.want != "" && got != tt.want {
  1430  				t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
  1431  				if diff := cmp.Diff(tt.want, got); diff != "" {
  1432  					t.Errorf("Marshal() diff -want +got\n%v\n", diff)
  1433  				}
  1434  			}
  1435  		})
  1436  	}
  1437  }