...

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

View as plain text