...

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

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

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

View as plain text