|  | // Copyright 2018 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package prototext_test | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "math" | 
|  | "strings" | 
|  | "testing" | 
|  |  | 
|  | "google.golang.org/protobuf/encoding/prototext" | 
|  | "google.golang.org/protobuf/internal/flags" | 
|  | "google.golang.org/protobuf/internal/protobuild" | 
|  | "google.golang.org/protobuf/proto" | 
|  | "google.golang.org/protobuf/reflect/protoregistry" | 
|  |  | 
|  | pb2 "google.golang.org/protobuf/internal/testprotos/textpb2" | 
|  | pb3 "google.golang.org/protobuf/internal/testprotos/textpb3" | 
|  | pbeditions "google.golang.org/protobuf/internal/testprotos/textpbeditions" | 
|  | "google.golang.org/protobuf/types/known/anypb" | 
|  | ) | 
|  |  | 
|  | func TestUnmarshal(t *testing.T) { | 
|  | type test struct { | 
|  | desc         string | 
|  | umo          prototext.UnmarshalOptions | 
|  | inputMessage proto.Message | 
|  | inputText    string | 
|  | wantMessage  proto.Message | 
|  | wantErr      string // Expected error substring. | 
|  | skip         bool | 
|  | } | 
|  | tests := []test{{ | 
|  | desc:         "case sensitive", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    `S_BOOL: true`, | 
|  | wantErr:      "unknown field: S_BOOL", | 
|  | }, { | 
|  | desc:         "proto2 string with invalid UTF-8", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `opt_string: "abc\xff"`, | 
|  | wantMessage: &pb2.Scalars{ | 
|  | OptString: proto.String("abc\xff"), | 
|  | }, | 
|  | }, { | 
|  | desc:         "protoeditions unvalidated string with invalid UTF-8", | 
|  | inputMessage: &pbeditions.Scalars{}, | 
|  | inputText:    `opt_string: "abc\xff"`, | 
|  | wantMessage: &pbeditions.Scalars{ | 
|  | OptString: proto.String("abc\xff"), | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 string with invalid UTF-8", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    `s_string: "abc\xff"`, | 
|  | wantErr:      "(line 1:11): contains invalid UTF-8", | 
|  | }, { | 
|  | desc:         "proto2 message contains unknown field", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    "unknown_field: 123", | 
|  | wantErr:      "unknown field", | 
|  | }, { | 
|  | desc:         "proto3 message contains unknown field", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "unknown_field: 456", | 
|  | wantErr:      "unknown field", | 
|  | }, { | 
|  | desc:         "proto2 message contains reserved field name", | 
|  | inputMessage: &pb2.ReservedFieldNames{}, | 
|  | inputText:    "reserved_field: 123 reserved_field { nested: 123 } opt_int32: 456", | 
|  | wantMessage:  &pb2.ReservedFieldNames{OptInt32: proto.Int32(456)}, | 
|  | }, { | 
|  | desc:         "proto3 message contains reserved field name", | 
|  | inputMessage: &pb3.ReservedFieldNames{}, | 
|  | inputText:    "reserved_field: 123 reserved_field { nested: 123 } opt_int32: 456", | 
|  | wantMessage:  &pb3.ReservedFieldNames{OptInt32: 456}, | 
|  | }, { | 
|  | desc:         "proto2 message contains discarded unknown field", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `unknown_field:123 1000:"hello"`, | 
|  | }, { | 
|  | desc:         "protoeditions message contains discarded unknown field", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pbeditions.Scalars{}, | 
|  | inputText:    `unknown_field:123 1000:"hello"`, | 
|  | }, { | 
|  | desc:         "proto3 message contains discarded unknown field", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    `unknown_field:456 1000:"goodbye"`, | 
|  | }, { | 
|  | desc:         "proto2 message cannot parse field number", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `13:"hello"`, | 
|  | wantErr:      "cannot specify field by number", | 
|  | }, { | 
|  | desc:         "unknown list field", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `unknown_field: { strings: [ "" ] }`, | 
|  | }, { | 
|  | desc:         "unknown list of list field", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `unknown_field: { strings: [ [ ] ] }`, | 
|  | wantErr:      `(line 1:29): invalid scalar value: [`, | 
|  | }, { | 
|  | desc:         "unknown list of message field", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `unknown_field: [ { a: "b" }, { c: "d" } ]`, | 
|  | }, { | 
|  | desc:         "proto3 message cannot parse field number", | 
|  | umo:          prototext.UnmarshalOptions{DiscardUnknown: true}, | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    `13:"goodbye"`, | 
|  | wantErr:      "cannot specify field by number", | 
|  | }, { | 
|  | desc:         "proto2 numeric key field", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    "1: true", | 
|  | wantErr:      "cannot specify field by number", | 
|  | }, { | 
|  | desc:         "proto3 numeric key field", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "1: true", | 
|  | wantErr:      "cannot specify field by number", | 
|  | }, { | 
|  | desc:         "invalid bool value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_bool: 123", | 
|  | wantErr:      "invalid value for bool", | 
|  | }, { | 
|  | desc:         "invalid int32 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_int32: not_a_num", | 
|  | wantErr:      "invalid value for int32", | 
|  | }, { | 
|  | desc:         "invalid int64 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_int64: 'not a num either'", | 
|  | wantErr:      "invalid value for int64", | 
|  | }, { | 
|  | desc:         "invalid uint32 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_fixed32: -42", | 
|  | wantErr:      "invalid value for fixed32", | 
|  | }, { | 
|  | desc:         "invalid uint64 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_uint64: -47", | 
|  | wantErr:      "invalid value for uint64", | 
|  | }, { | 
|  | desc:         "invalid sint32 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_sint32: '42'", | 
|  | wantErr:      "invalid value for sint32", | 
|  | }, { | 
|  | desc:         "invalid sint64 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_sint64: '-47'", | 
|  | wantErr:      "invalid value for sint64", | 
|  | }, { | 
|  | desc:         "invalid fixed32 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_fixed32: -42", | 
|  | wantErr:      "invalid value for fixed32", | 
|  | }, { | 
|  | desc:         "invalid fixed64 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_fixed64: -42", | 
|  | wantErr:      "invalid value for fixed64", | 
|  | }, { | 
|  | desc:         "invalid sfixed32 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_sfixed32: 'not valid'", | 
|  | wantErr:      "invalid value for sfixed32", | 
|  | }, { | 
|  | desc:         "invalid sfixed64 value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_sfixed64: bad", | 
|  | wantErr:      "invalid value for sfixed64", | 
|  | }, { | 
|  | desc:         "incomplete number value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    `s_int32: - `, | 
|  | wantErr:      "(line 1:10): invalid scalar value: -", | 
|  | }, { | 
|  | desc:         "conformance: FloatFieldMaxValue", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `opt_float: 3.4028235e+38`, | 
|  | wantMessage: &pb2.Scalars{ | 
|  | OptFloat: proto.Float32(3.40282347e+38), | 
|  | }, | 
|  | }, { | 
|  | desc:         "conformance: FloatFieldLargerThanUint64", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `opt_float: 18446744073709551616`, | 
|  | wantMessage: &pb2.Scalars{ | 
|  | OptFloat: proto.Float32(1.84467441e+19), | 
|  | }, | 
|  | }, { | 
|  | desc:         "conformance: FloatFieldTooLarge", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    `opt_float: 3.4028235e+39`, | 
|  | wantMessage: &pb2.Scalars{ | 
|  | OptFloat: proto.Float32(float32(math.Inf(1))), | 
|  | }, | 
|  | }, { | 
|  | desc:         "invalid string value", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_string: invalid_string", | 
|  | wantErr:      "invalid value for string type", | 
|  | }, { | 
|  | desc:         "proto2 bytes set to empty string", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText:    "opt_bytes: ''", | 
|  | wantMessage: &pb2.Scalars{ | 
|  | OptBytes: []byte(""), | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 bytes set to empty string", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText:    "s_bytes: ''", | 
|  | wantMessage:  &pb3.Scalars{}, | 
|  | }, { | 
|  | desc:         "proto2 duplicate singular field", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText: ` | 
|  | opt_bool: true | 
|  | opt_bool: false | 
|  | `, | 
|  | wantErr: `(line 3:1): non-repeated field "opt_bool" is repeated`, | 
|  | }, { | 
|  | desc:         "proto2 more duplicate singular field", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText: ` | 
|  | opt_bool: true | 
|  | opt_string: "hello" | 
|  | opt_bool: false | 
|  | `, | 
|  | wantErr: `(line 4:1): non-repeated field "opt_bool" is repeated`, | 
|  | }, { | 
|  | desc:         "proto2 invalid singular field", | 
|  | inputMessage: &pb2.Scalars{}, | 
|  | inputText: ` | 
|  | opt_bool: [true, false] | 
|  | `, | 
|  | wantErr: "(line 2:11): unexpected token: [", | 
|  | }, { | 
|  | desc:         "proto3 duplicate singular field", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText: ` | 
|  | s_bool: false | 
|  | s_bool: true | 
|  | `, | 
|  | wantErr: `non-repeated field "s_bool" is repeated`, | 
|  | }, { | 
|  | desc:         "proto3 more duplicate singular field", | 
|  | inputMessage: &pb3.Scalars{}, | 
|  | inputText: ` | 
|  | s_bool: false | 
|  | s_string: "" | 
|  | s_bool: true | 
|  | `, | 
|  | wantErr: `non-repeated field "s_bool" is repeated`, | 
|  | }, { | 
|  | desc:         "protoeditions open enum", | 
|  | inputMessage: &pbeditions.Enums{}, | 
|  | inputText: ` | 
|  | implicit_enum: EINS | 
|  | implicit_nested_enum: ZEHN | 
|  | `, | 
|  | wantMessage: &pbeditions.Enums{ | 
|  | ImplicitEnum:       pbeditions.OpenEnum_EINS, | 
|  | ImplicitNestedEnum: pbeditions.Enums_ZEHN, | 
|  | }, | 
|  | }, { | 
|  | desc:         "protoeditions enum numeric value", | 
|  | inputMessage: &pbeditions.Enums{}, | 
|  | inputText: ` | 
|  | implicit_enum: 1 | 
|  | implicit_nested_enum: 10 | 
|  | `, | 
|  | wantMessage: &pbeditions.Enums{ | 
|  | ImplicitEnum:       pbeditions.OpenEnum_EINS, | 
|  | ImplicitNestedEnum: pbeditions.Enums_ZEHN, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto2 enum set to unnamed numeric values", | 
|  | inputMessage: &pb2.Enums{}, | 
|  | inputText: ` | 
|  | opt_enum: 101 | 
|  | opt_nested_enum: -101 | 
|  | `, | 
|  | wantMessage: &pb2.Enums{ | 
|  | OptEnum:       pb2.Enum(101).Enum(), | 
|  | OptNestedEnum: pb2.Enums_NestedEnum(-101).Enum(), | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto2 enum set to invalid named", | 
|  | inputMessage: &pb2.Enums{}, | 
|  | inputText: ` | 
|  | opt_enum: UNNAMED | 
|  | opt_nested_enum: UNNAMED_TOO | 
|  | `, | 
|  | wantErr: "invalid value for enum type: UNNAMED", | 
|  | }, { | 
|  | desc:         "proto3 enum name value", | 
|  | inputMessage: &pb3.Enums{}, | 
|  | inputText: ` | 
|  | s_enum: ONE | 
|  | s_nested_enum: DIEZ | 
|  | `, | 
|  | wantMessage: &pb3.Enums{ | 
|  | SEnum:       pb3.Enum_ONE, | 
|  | SNestedEnum: pb3.Enums_DIEZ, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 enum numeric value", | 
|  | inputMessage: &pb3.Enums{}, | 
|  | inputText: ` | 
|  | s_enum: 2 | 
|  | s_nested_enum: 2 | 
|  | `, | 
|  | wantMessage: &pb3.Enums{ | 
|  | SEnum:       pb3.Enum_TWO, | 
|  | SNestedEnum: pb3.Enums_DOS, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 enum unnamed numeric value", | 
|  | inputMessage: &pb3.Enums{}, | 
|  | inputText: ` | 
|  | s_enum: 0x7fffffff | 
|  | s_nested_enum: -0x80000000 | 
|  | `, | 
|  | wantMessage: &pb3.Enums{ | 
|  | SEnum:       0x7fffffff, | 
|  | SNestedEnum: -0x80000000, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto2 nested empty messages", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText: ` | 
|  | opt_nested: {} | 
|  | OptGroup: {} | 
|  | `, | 
|  | wantMessage: &pb2.Nests{ | 
|  | OptNested: &pb2.Nested{}, | 
|  | Optgroup:  &pb2.Nests_OptGroup{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "protoeditions nested empty messages", | 
|  | inputMessage: &pbeditions.Nests{}, | 
|  | inputText: ` | 
|  | opt_nested: {} | 
|  | OptGroup: {} | 
|  | delimited_field: {} | 
|  | `, | 
|  | wantMessage: &pbeditions.Nests{ | 
|  | OptNested:      &pbeditions.Nested{}, | 
|  | Optgroup:       &pbeditions.Nests_OptGroup{}, | 
|  | DelimitedField: &pbeditions.Nests_OptGroup{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "message fields with no field separator", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText: ` | 
|  | opt_nested {} | 
|  | OptGroup {} | 
|  | `, | 
|  | wantMessage: &pb2.Nests{ | 
|  | OptNested: &pb2.Nested{}, | 
|  | Optgroup:  &pb2.Nests_OptGroup{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "message fields with no field separator", | 
|  | inputMessage: &pbeditions.Nests{}, | 
|  | inputText: ` | 
|  | opt_nested {} | 
|  | OptGroup {} | 
|  | delimited_field {} | 
|  | `, | 
|  | wantMessage: &pbeditions.Nests{ | 
|  | OptNested:      &pbeditions.Nested{}, | 
|  | Optgroup:       &pbeditions.Nests_OptGroup{}, | 
|  | DelimitedField: &pbeditions.Nests_OptGroup{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "group field name", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText:    `optgroup: {}`, | 
|  | wantMessage: &pb2.Nests{ | 
|  | Optgroup: &pb2.Nests_OptGroup{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "delimited encoded group-like message field name", | 
|  | inputMessage: &pbeditions.Nests{}, | 
|  | inputText:    `optgroup {}`, | 
|  | wantMessage: &pbeditions.Nests{ | 
|  | Optgroup: &pbeditions.Nests_OptGroup{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "delimited encoded message field name", | 
|  | inputMessage: &pbeditions.Nests{}, | 
|  | inputText:    `Delimited_Field: {}`, | 
|  | wantErr:      "unknown field: Delimited_Field", | 
|  | }, { | 
|  | desc:         "proto2 nested messages", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText: ` | 
|  | opt_nested: { | 
|  | opt_string: "nested message" | 
|  | opt_nested: { | 
|  | opt_string: "another nested message" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb2.Nests{ | 
|  | OptNested: &pb2.Nested{ | 
|  | OptString: proto.String("nested message"), | 
|  | OptNested: &pb2.Nested{ | 
|  | OptString: proto.String("another nested message"), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "protoeditions delimited encoded nested messages", | 
|  | inputMessage: &pbeditions.Nests{}, | 
|  | inputText: ` | 
|  | opt_nested: { | 
|  | opt_string: "nested message" | 
|  | opt_nested: { | 
|  | opt_string: "another nested message" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pbeditions.Nests{ | 
|  | OptNested: &pbeditions.Nested{ | 
|  | OptString: proto.String("nested message"), | 
|  | OptNested: &pbeditions.Nested{ | 
|  | OptString: proto.String("another nested message"), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 nested empty message", | 
|  | inputMessage: &pb3.Nests{}, | 
|  | inputText:    "s_nested: {}", | 
|  | wantMessage: &pb3.Nests{ | 
|  | SNested: &pb3.Nested{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 nested message", | 
|  | inputMessage: &pb3.Nests{}, | 
|  | inputText: ` | 
|  | s_nested: { | 
|  | s_string: "nested message" | 
|  | s_nested: { | 
|  | s_string: "another nested message" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Nests{ | 
|  | SNested: &pb3.Nested{ | 
|  | SString: "nested message", | 
|  | SNested: &pb3.Nested{ | 
|  | SString: "another nested message", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 nested message contains invalid UTF-8", | 
|  | inputMessage: &pb3.Nests{}, | 
|  | inputText: `s_nested: { | 
|  | s_string: "abc\xff" | 
|  | } | 
|  | `, | 
|  | wantErr: "contains invalid UTF-8", | 
|  | }, { | 
|  | desc:         "oneof set to empty string", | 
|  | inputMessage: &pb3.Oneofs{}, | 
|  | inputText:    "oneof_string: ''", | 
|  | wantMessage: &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofString{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "oneof set to string", | 
|  | inputMessage: &pb3.Oneofs{}, | 
|  | inputText:    "oneof_string: 'hello'", | 
|  | wantMessage: &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofString{ | 
|  | OneofString: "hello", | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "oneof set to enum", | 
|  | inputMessage: &pb3.Oneofs{}, | 
|  | inputText:    "oneof_enum: TEN", | 
|  | wantMessage: &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofEnum{ | 
|  | OneofEnum: pb3.Enum_TEN, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "oneof set to empty message", | 
|  | inputMessage: &pb3.Oneofs{}, | 
|  | inputText:    "oneof_nested: {}", | 
|  | wantMessage: &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofNested{ | 
|  | OneofNested: &pb3.Nested{}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "oneof set to message", | 
|  | inputMessage: &pb3.Oneofs{}, | 
|  | inputText: ` | 
|  | oneof_nested: { | 
|  | s_string: "nested message" | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofNested{ | 
|  | OneofNested: &pb3.Nested{ | 
|  | SString: "nested message", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "oneof set to more than one field", | 
|  | inputMessage: &pb3.Oneofs{}, | 
|  | inputText: ` | 
|  | oneof_enum: ZERO | 
|  | oneof_string: "hello" | 
|  | `, | 
|  | wantErr: `error parsing "oneof_string", oneof pb3.Oneofs.union is already set`, | 
|  | }, { | 
|  | desc:         "repeated scalar using same field name", | 
|  | inputMessage: &pb2.Repeats{}, | 
|  | inputText: ` | 
|  | rpt_string: "a" | 
|  | rpt_string: "b" | 
|  | rpt_int32: 0xff | 
|  | rpt_float: 1.23 | 
|  | rpt_bytes: "bytes" | 
|  | `, | 
|  | wantMessage: &pb2.Repeats{ | 
|  | RptString: []string{"a", "b"}, | 
|  | RptInt32:  []int32{0xff}, | 
|  | RptFloat:  []float32{1.23}, | 
|  | RptBytes:  [][]byte{[]byte("bytes")}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated using mix of [] and repeated field name", | 
|  | inputMessage: &pb2.Repeats{}, | 
|  | inputText: ` | 
|  | rpt_string: "a" | 
|  | rpt_bool: true | 
|  | rpt_string: ["x", "y"] | 
|  | rpt_bool: [ false, true ] | 
|  | rpt_string: "b" | 
|  | `, | 
|  | wantMessage: &pb2.Repeats{ | 
|  | RptString: []string{"a", "x", "y", "b"}, | 
|  | RptBool:   []bool{true, false, true}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated proto2 contains invalid UTF-8", | 
|  | inputMessage: &pb2.Repeats{}, | 
|  | inputText:    `rpt_string: "abc\xff"`, | 
|  | wantMessage: &pb2.Repeats{ | 
|  | RptString: []string{"abc\xff"}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated proto3 contains invalid UTF-8", | 
|  | inputMessage: &pb3.Repeats{}, | 
|  | inputText:    `rpt_string: "abc\xff"`, | 
|  | wantErr:      "contains invalid UTF-8", | 
|  | }, { | 
|  | desc:         "repeated enums", | 
|  | inputMessage: &pb2.Enums{}, | 
|  | inputText: ` | 
|  | rpt_enum: TEN | 
|  | rpt_enum: 1 | 
|  | rpt_nested_enum: [DOS, 2] | 
|  | rpt_enum: 42 | 
|  | rpt_nested_enum: -47 | 
|  | `, | 
|  | wantMessage: &pb2.Enums{ | 
|  | RptEnum:       []pb2.Enum{pb2.Enum_TEN, pb2.Enum_ONE, 42}, | 
|  | RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_DOS, pb2.Enums_DOS, -47}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated nested messages", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText: ` | 
|  | rpt_nested: { | 
|  | opt_string: "repeat nested one" | 
|  | } | 
|  | rpt_nested: { | 
|  | opt_string: "repeat nested two" | 
|  | opt_nested: { | 
|  | opt_string: "inside repeat nested two" | 
|  | } | 
|  | } | 
|  | rpt_nested: {} | 
|  | `, | 
|  | wantMessage: &pb2.Nests{ | 
|  | RptNested: []*pb2.Nested{ | 
|  | { | 
|  | OptString: proto.String("repeat nested one"), | 
|  | }, | 
|  | { | 
|  | OptString: proto.String("repeat nested two"), | 
|  | OptNested: &pb2.Nested{ | 
|  | OptString: proto.String("inside repeat nested two"), | 
|  | }, | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated group fields", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText: ` | 
|  | RptGroup: { | 
|  | rpt_string: "hello" | 
|  | rpt_string: "world" | 
|  | } | 
|  | RptGroup: {} | 
|  | `, | 
|  | wantMessage: &pb2.Nests{ | 
|  | Rptgroup: []*pb2.Nests_RptGroup{ | 
|  | { | 
|  | RptString: []string{"hello", "world"}, | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated delimited encoded message fields", | 
|  | inputMessage: &pbeditions.Nests{}, | 
|  | inputText: ` | 
|  | RptGroup: { | 
|  | rpt_string: "hello" | 
|  | rpt_string: "world" | 
|  | } | 
|  | RptGroup: {} | 
|  | `, | 
|  | wantMessage: &pbeditions.Nests{ | 
|  | Rptgroup: []*pbeditions.Nests_RptGroup{ | 
|  | { | 
|  | RptString: []string{"hello", "world"}, | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "repeated message fields without field separator", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText: ` | 
|  | rpt_nested { | 
|  | opt_string: "repeat nested one" | 
|  | } | 
|  | rpt_nested: [ | 
|  | { | 
|  | opt_string: "repeat nested two" | 
|  | }, | 
|  | {} | 
|  | ] | 
|  | `, | 
|  | wantMessage: &pb2.Nests{ | 
|  | RptNested: []*pb2.Nested{ | 
|  | { | 
|  | OptString: proto.String("repeat nested one"), | 
|  | }, | 
|  | { | 
|  | OptString: proto.String("repeat nested two"), | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "bools", | 
|  | inputMessage: &pb2.Repeats{}, | 
|  | inputText: ` | 
|  | rpt_bool: [ True, true, t, 1, False, false, f, 0 ] | 
|  | `, | 
|  | wantMessage: &pb2.Repeats{ | 
|  | RptBool: []bool{true, true, true, true, false, false, false, false}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "special floats and doubles", | 
|  | inputMessage: &pb2.Repeats{}, | 
|  | inputText: ` | 
|  | rpt_float: [ inf, Inf, infinity, InFiniTy, -inf, -inF, -infinitY, -InfinitY, nan, NaN, Nan ], | 
|  | rpt_double: [ inf, Inf, infinity, InFiniTy, -inf, -inF, -infinitY, -InfinitY, nan, NaN, Nan ], | 
|  | `, | 
|  | wantMessage: &pb2.Repeats{ | 
|  | RptFloat: []float32{ | 
|  | float32(math.Inf(1)), | 
|  | float32(math.Inf(1)), | 
|  | float32(math.Inf(1)), | 
|  | float32(math.Inf(1)), | 
|  | float32(math.Inf(-1)), | 
|  | float32(math.Inf(-1)), | 
|  | float32(math.Inf(-1)), | 
|  | float32(math.Inf(-1)), | 
|  | float32(math.NaN()), | 
|  | float32(math.NaN()), | 
|  | float32(math.NaN()), | 
|  | }, | 
|  | RptDouble: []float64{ | 
|  | math.Inf(1), | 
|  | math.Inf(1), | 
|  | math.Inf(1), | 
|  | math.Inf(1), | 
|  | math.Inf(-1), | 
|  | math.Inf(-1), | 
|  | math.Inf(-1), | 
|  | math.Inf(-1), | 
|  | math.NaN(), | 
|  | math.NaN(), | 
|  | math.NaN(), | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map fields 1", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: -101 | 
|  | value: "-101" | 
|  | } | 
|  | int32_to_str { | 
|  | key: 0 | 
|  | value: "zero" | 
|  | } | 
|  | bool_to_uint32: { | 
|  | key: false | 
|  | value: 101 | 
|  | } | 
|  | int32_to_str: { | 
|  | key: 255 | 
|  | value: "0xff" | 
|  | } | 
|  | bool_to_uint32 { | 
|  | key: true | 
|  | value: 42 | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | -101: "-101", | 
|  | 0xff: "0xff", | 
|  | 0:    "zero", | 
|  | }, | 
|  | BoolToUint32: map[bool]uint32{ | 
|  | true:  42, | 
|  | false: 101, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map fields 2", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | uint64_to_enum: { | 
|  | key: 1 | 
|  | value: ONE | 
|  | } | 
|  | uint64_to_enum: { | 
|  | key: 2 | 
|  | value: 2 | 
|  | } | 
|  | uint64_to_enum: { | 
|  | key: 10 | 
|  | value: 101 | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Uint64ToEnum: map[uint64]pb3.Enum{ | 
|  | 1:  pb3.Enum_ONE, | 
|  | 2:  pb3.Enum_TWO, | 
|  | 10: 101, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map fields 3", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | str_to_nested: { | 
|  | key: "nested_one" | 
|  | value { | 
|  | s_string: "nested in a map" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | StrToNested: map[string]*pb3.Nested{ | 
|  | "nested_one": &pb3.Nested{ | 
|  | SString: "nested in a map", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map fields 4", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | str_to_oneofs: { | 
|  | key: "nested" | 
|  | value: { | 
|  | oneof_nested: { | 
|  | s_string: "nested oneof in map field value" | 
|  | } | 
|  | } | 
|  | } | 
|  | str_to_oneofs: { | 
|  | key: "string" | 
|  | value: { | 
|  | oneof_string: "hello" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | StrToOneofs: map[string]*pb3.Oneofs{ | 
|  | "string": &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofString{ | 
|  | OneofString: "hello", | 
|  | }, | 
|  | }, | 
|  | "nested": &pb3.Oneofs{ | 
|  | Union: &pb3.Oneofs_OneofNested{ | 
|  | OneofNested: &pb3.Nested{ | 
|  | SString: "nested oneof in map field value", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map contains duplicate keys", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 0 | 
|  | value: "cero" | 
|  | } | 
|  | int32_to_str: { | 
|  | key: 0 | 
|  | value: "zero" | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | 0: "zero", | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map contains duplicate key fields", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 0 | 
|  | key: 1 | 
|  | value: "cero" | 
|  | } | 
|  | `, | 
|  | wantErr: `map entry "key" cannot be repeated`, | 
|  | }, { | 
|  | desc:         "map contains duplicate value fields", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 1 | 
|  | value: "cero" | 
|  | value: "uno" | 
|  | } | 
|  | `, | 
|  | wantErr: `map entry "value" cannot be repeated`, | 
|  | }, { | 
|  | desc:         "map contains missing key", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | value: "zero" | 
|  | } | 
|  | bool_to_uint32: { | 
|  | value: 47 | 
|  | } | 
|  | str_to_nested: { | 
|  | value: {} | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | 0: "zero", | 
|  | }, | 
|  | BoolToUint32: map[bool]uint32{ | 
|  | false: 47, | 
|  | }, | 
|  | StrToNested: map[string]*pb3.Nested{ | 
|  | "": {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map contains missing value", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 100 | 
|  | } | 
|  | bool_to_uint32: { | 
|  | key: true | 
|  | } | 
|  | uint64_to_enum: { | 
|  | key: 101 | 
|  | } | 
|  | str_to_nested: { | 
|  | key: "hello" | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | 100: "", | 
|  | }, | 
|  | BoolToUint32: map[bool]uint32{ | 
|  | true: 0, | 
|  | }, | 
|  | Uint64ToEnum: map[uint64]pb3.Enum{ | 
|  | 101: pb3.Enum_ZERO, | 
|  | }, | 
|  | StrToNested: map[string]*pb3.Nested{ | 
|  | "hello": {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map contains missing key and value", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: {} | 
|  | bool_to_uint32: {} | 
|  | uint64_to_enum: {} | 
|  | str_to_nested: {} | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | 0: "", | 
|  | }, | 
|  | BoolToUint32: map[bool]uint32{ | 
|  | false: 0, | 
|  | }, | 
|  | Uint64ToEnum: map[uint64]pb3.Enum{ | 
|  | 0: pb3.Enum_ZERO, | 
|  | }, | 
|  | StrToNested: map[string]*pb3.Nested{ | 
|  | "": {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "map contains overriding entries", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 0 | 
|  | } | 
|  | int32_to_str: { | 
|  | value: "empty" | 
|  | } | 
|  | int32_to_str: {} | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | 0: "", | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto2 map field value contains invalid UTF-8", | 
|  | inputMessage: &pb2.Maps{}, | 
|  | inputText: `int32_to_str: { | 
|  | key: 101 | 
|  | value: "abc\xff" | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb2.Maps{ | 
|  | Int32ToStr: map[int32]string{101: "abc\xff"}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto2 map field key contains invalid UTF-8", | 
|  | inputMessage: &pb2.Maps{}, | 
|  | inputText: `str_to_nested: { | 
|  | key: "abc\xff" | 
|  | value: {} | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb2.Maps{ | 
|  | StrToNested: map[string]*pb2.Nested{"abc\xff": {}}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "proto3 map field value contains invalid UTF-8", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: `int32_to_str: { | 
|  | key: 101 | 
|  | value: "abc\xff" | 
|  | } | 
|  | `, | 
|  | wantErr: "contains invalid UTF-8", | 
|  | }, { | 
|  | desc:         "proto3 map field key contains invalid UTF-8", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: `str_to_nested: { | 
|  | key: "abc\xff" | 
|  | value: {} | 
|  | } | 
|  | `, | 
|  | wantErr: "contains invalid UTF-8", | 
|  | }, { | 
|  | desc:         "map contains unknown field", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 0 | 
|  | value: "cero" | 
|  | unknown: "bad" | 
|  | } | 
|  | `, | 
|  | wantErr: `(line 5:3): unknown map entry field "unknown"`, | 
|  | }, { | 
|  | desc:         "map contains extension-like key field", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | [key]: 10 | 
|  | value: "ten" | 
|  | } | 
|  | `, | 
|  | wantErr: `unknown map entry field "[key]"`, | 
|  | }, { | 
|  | desc:         "map contains invalid key", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: "invalid" | 
|  | value: "cero" | 
|  | } | 
|  | `, | 
|  | wantErr: "(line 3:8): invalid value for int32 type", | 
|  | }, { | 
|  | desc:         "map contains invalid value", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 100 | 
|  | value: 101 | 
|  | } | 
|  | `, | 
|  | wantErr: "(line 4:10): invalid value for string type", | 
|  | }, { | 
|  | desc:         "map contains invalid message value", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | str_to_nested: { | 
|  | key: "one" | 
|  | value: 1 | 
|  | } | 
|  | `, | 
|  | wantErr: "syntax error (line 4:10): unexpected token: 1", | 
|  | }, { | 
|  | desc:         "map using mix of [] and repeated", | 
|  | inputMessage: &pb3.Maps{}, | 
|  | inputText: ` | 
|  | int32_to_str: { | 
|  | key: 1 | 
|  | value: "one" | 
|  | } | 
|  | int32_to_str: [ | 
|  | { | 
|  | key: 2 | 
|  | value: "not this" | 
|  | }, | 
|  | { | 
|  | }, | 
|  | { | 
|  | key: 3 | 
|  | value: "three" | 
|  | } | 
|  | ] | 
|  | int32_to_str: { | 
|  | key: 2 | 
|  | value: "two" | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb3.Maps{ | 
|  | Int32ToStr: map[int32]string{ | 
|  | 0: "", | 
|  | 1: "one", | 
|  | 2: "two", | 
|  | 3: "three", | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "required fields not set", | 
|  | inputMessage: &pb2.Requireds{}, | 
|  | wantErr:      "required field", | 
|  | }, { | 
|  | desc:         "required field set", | 
|  | inputMessage: &pb2.PartialRequired{}, | 
|  | inputText:    "req_string: 'this is required'", | 
|  | wantMessage: &pb2.PartialRequired{ | 
|  | ReqString: proto.String("this is required"), | 
|  | }, | 
|  | }, { | 
|  | desc:         "required fields partially set", | 
|  | inputMessage: &pb2.Requireds{}, | 
|  | inputText: ` | 
|  | req_bool: false | 
|  | req_sfixed64: 3203386110 | 
|  | req_string: "hello" | 
|  | req_enum: ONE | 
|  | `, | 
|  | wantMessage: &pb2.Requireds{ | 
|  | ReqBool:     proto.Bool(false), | 
|  | ReqSfixed64: proto.Int64(0xbeefcafe), | 
|  | ReqString:   proto.String("hello"), | 
|  | ReqEnum:     pb2.Enum_ONE.Enum(), | 
|  | }, | 
|  | wantErr: "required field", | 
|  | }, { | 
|  | desc:         "required fields partially set with AllowPartial", | 
|  | umo:          prototext.UnmarshalOptions{AllowPartial: true}, | 
|  | inputMessage: &pb2.Requireds{}, | 
|  | inputText: ` | 
|  | req_bool: false | 
|  | req_sfixed64: 3203386110 | 
|  | req_string: "hello" | 
|  | req_enum: ONE | 
|  | `, | 
|  | wantMessage: &pb2.Requireds{ | 
|  | ReqBool:     proto.Bool(false), | 
|  | ReqSfixed64: proto.Int64(0xbeefcafe), | 
|  | ReqString:   proto.String("hello"), | 
|  | ReqEnum:     pb2.Enum_ONE.Enum(), | 
|  | }, | 
|  | }, { | 
|  | desc:         "required fields all set", | 
|  | inputMessage: &pb2.Requireds{}, | 
|  | inputText: ` | 
|  | req_bool: false | 
|  | req_sfixed64: 0 | 
|  | req_double: 0 | 
|  | req_string: "" | 
|  | req_enum: ONE | 
|  | req_nested: {} | 
|  | `, | 
|  | wantMessage: &pb2.Requireds{ | 
|  | ReqBool:     proto.Bool(false), | 
|  | ReqSfixed64: proto.Int64(0), | 
|  | ReqDouble:   proto.Float64(0), | 
|  | ReqString:   proto.String(""), | 
|  | ReqEnum:     pb2.Enum_ONE.Enum(), | 
|  | ReqNested:   &pb2.Nested{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "indirect required field", | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText:    "opt_nested: {}", | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | OptNested: &pb2.NestedWithRequired{}, | 
|  | }, | 
|  | wantErr: "required field", | 
|  | }, { | 
|  | desc:         "indirect required field with AllowPartial", | 
|  | umo:          prototext.UnmarshalOptions{AllowPartial: true}, | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText:    "opt_nested: {}", | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | OptNested: &pb2.NestedWithRequired{}, | 
|  | }, | 
|  | }, { | 
|  | desc:         "indirect required field in repeated", | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText: ` | 
|  | rpt_nested: { | 
|  | req_string: "one" | 
|  | } | 
|  | rpt_nested: {} | 
|  | `, | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | RptNested: []*pb2.NestedWithRequired{ | 
|  | { | 
|  | ReqString: proto.String("one"), | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | }, | 
|  | wantErr: "required field", | 
|  | }, { | 
|  | desc:         "indirect required field in repeated with AllowPartial", | 
|  | umo:          prototext.UnmarshalOptions{AllowPartial: true}, | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText: ` | 
|  | rpt_nested: { | 
|  | req_string: "one" | 
|  | } | 
|  | rpt_nested: {} | 
|  | `, | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | RptNested: []*pb2.NestedWithRequired{ | 
|  | { | 
|  | ReqString: proto.String("one"), | 
|  | }, | 
|  | {}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "indirect required field in map", | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText: ` | 
|  | str_to_nested: { | 
|  | key: "missing" | 
|  | } | 
|  | str_to_nested: { | 
|  | key: "contains" | 
|  | value: { | 
|  | req_string: "here" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | StrToNested: map[string]*pb2.NestedWithRequired{ | 
|  | "missing": &pb2.NestedWithRequired{}, | 
|  | "contains": &pb2.NestedWithRequired{ | 
|  | ReqString: proto.String("here"), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | wantErr: "required field", | 
|  | }, { | 
|  | desc:         "indirect required field in map with AllowPartial", | 
|  | umo:          prototext.UnmarshalOptions{AllowPartial: true}, | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText: ` | 
|  | str_to_nested: { | 
|  | key: "missing" | 
|  | } | 
|  | str_to_nested: { | 
|  | key: "contains" | 
|  | value: { | 
|  | req_string: "here" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | StrToNested: map[string]*pb2.NestedWithRequired{ | 
|  | "missing": &pb2.NestedWithRequired{}, | 
|  | "contains": &pb2.NestedWithRequired{ | 
|  | ReqString: proto.String("here"), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "indirect required field in oneof", | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText: `oneof_nested: {} | 
|  | `, | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | Union: &pb2.IndirectRequired_OneofNested{ | 
|  | OneofNested: &pb2.NestedWithRequired{}, | 
|  | }, | 
|  | }, | 
|  | wantErr: "required field", | 
|  | }, { | 
|  | desc:         "indirect required field in oneof with AllowPartial", | 
|  | umo:          prototext.UnmarshalOptions{AllowPartial: true}, | 
|  | inputMessage: &pb2.IndirectRequired{}, | 
|  | inputText: `oneof_nested: {} | 
|  | `, | 
|  | wantMessage: &pb2.IndirectRequired{ | 
|  | Union: &pb2.IndirectRequired_OneofNested{ | 
|  | OneofNested: &pb2.NestedWithRequired{}, | 
|  | }, | 
|  | }, | 
|  | }, { | 
|  | desc:         "ignore reserved field", | 
|  | inputMessage: &pb2.Nests{}, | 
|  | inputText:    "reserved_field: 'ignore this'", | 
|  | wantMessage:  &pb2.Nests{}, | 
|  | }, { | 
|  | desc:         "extensions of non-repeated fields", | 
|  | inputMessage: &pb2.Extensions{}, | 
|  | inputText: `opt_string: "non-extension field" | 
|  | [pb2.opt_ext_bool]: true | 
|  | opt_bool: true | 
|  | [pb2.opt_ext_nested]: { | 
|  | opt_string: "nested in an extension" | 
|  | opt_nested: { | 
|  | opt_string: "another nested in an extension" | 
|  | } | 
|  | } | 
|  | [pb2.opt_ext_string]: "extension field" | 
|  | opt_int32: 42 | 
|  | [pb2.opt_ext_enum]: TEN | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.Extensions{ | 
|  | OptString: proto.String("non-extension field"), | 
|  | OptBool:   proto.Bool(true), | 
|  | OptInt32:  proto.Int32(42), | 
|  | } | 
|  | proto.SetExtension(m, pb2.E_OptExtBool, true) | 
|  | proto.SetExtension(m, pb2.E_OptExtString, "extension field") | 
|  | proto.SetExtension(m, pb2.E_OptExtEnum, pb2.Enum_TEN) | 
|  | proto.SetExtension(m, pb2.E_OptExtNested, &pb2.Nested{ | 
|  | OptString: proto.String("nested in an extension"), | 
|  | OptNested: &pb2.Nested{ | 
|  | OptString: proto.String("another nested in an extension"), | 
|  | }, | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | }, { | 
|  | desc:         "extension field contains invalid UTF-8", | 
|  | inputMessage: &pb2.Extensions{}, | 
|  | inputText:    `[pb2.opt_ext_string]: "abc\xff"`, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.Extensions{} | 
|  | proto.SetExtension(m, pb2.E_OptExtString, "abc\xff") | 
|  | return m | 
|  | }(), | 
|  | }, { | 
|  | desc:         "extensions of repeated fields", | 
|  | inputMessage: &pb2.Extensions{}, | 
|  | inputText: `[pb2.rpt_ext_enum]: TEN | 
|  | [pb2.rpt_ext_enum]: 101 | 
|  | [pb2.rpt_ext_fixed32]: 42 | 
|  | [pb2.rpt_ext_enum]: ONE | 
|  | [pb2.rpt_ext_nested]: { | 
|  | opt_string: "one" | 
|  | } | 
|  | [pb2.rpt_ext_nested]: { | 
|  | opt_string: "two" | 
|  | } | 
|  | [pb2.rpt_ext_fixed32]: 47 | 
|  | [pb2.rpt_ext_nested]: { | 
|  | opt_string: "three" | 
|  | } | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.Extensions{} | 
|  | proto.SetExtension(m, pb2.E_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE}) | 
|  | proto.SetExtension(m, pb2.E_RptExtFixed32, []uint32{42, 47}) | 
|  | proto.SetExtension(m, pb2.E_RptExtNested, []*pb2.Nested{ | 
|  | &pb2.Nested{OptString: proto.String("one")}, | 
|  | &pb2.Nested{OptString: proto.String("two")}, | 
|  | &pb2.Nested{OptString: proto.String("three")}, | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | }, { | 
|  | desc:         "extensions of non-repeated fields in another message", | 
|  | inputMessage: &pb2.Extensions{}, | 
|  | inputText: `[pb2.ExtensionsContainer.opt_ext_bool]: true | 
|  | [pb2.ExtensionsContainer.opt_ext_enum]: TEN | 
|  | [pb2.ExtensionsContainer.opt_ext_nested]: { | 
|  | opt_string: "nested in an extension" | 
|  | opt_nested: { | 
|  | opt_string: "another nested in an extension" | 
|  | } | 
|  | } | 
|  | [pb2.ExtensionsContainer.opt_ext_string]: "extension field" | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.Extensions{} | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtBool, true) | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtString, "extension field") | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtEnum, pb2.Enum_TEN) | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_OptExtNested, &pb2.Nested{ | 
|  | OptString: proto.String("nested in an extension"), | 
|  | OptNested: &pb2.Nested{ | 
|  | OptString: proto.String("another nested in an extension"), | 
|  | }, | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | }, { | 
|  | desc:         "extensions of repeated fields in another message", | 
|  | inputMessage: &pb2.Extensions{}, | 
|  | inputText: `opt_string: "non-extension field" | 
|  | opt_bool: true | 
|  | opt_int32: 42 | 
|  | [pb2.ExtensionsContainer.rpt_ext_nested]: { | 
|  | opt_string: "one" | 
|  | } | 
|  | [pb2.ExtensionsContainer.rpt_ext_enum]: TEN | 
|  | [pb2.ExtensionsContainer.rpt_ext_nested]: { | 
|  | opt_string: "two" | 
|  | } | 
|  | [pb2.ExtensionsContainer.rpt_ext_enum]: 101 | 
|  | [pb2.ExtensionsContainer.rpt_ext_string]: "hello" | 
|  | [pb2.ExtensionsContainer.rpt_ext_enum]: ONE | 
|  | [pb2.ExtensionsContainer.rpt_ext_nested]: { | 
|  | opt_string: "three" | 
|  | } | 
|  | [pb2.ExtensionsContainer.rpt_ext_string]: "world" | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.Extensions{ | 
|  | OptString: proto.String("non-extension field"), | 
|  | OptBool:   proto.Bool(true), | 
|  | OptInt32:  proto.Int32(42), | 
|  | } | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtEnum, []pb2.Enum{pb2.Enum_TEN, 101, pb2.Enum_ONE}) | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtString, []string{"hello", "world"}) | 
|  | proto.SetExtension(m, pb2.E_ExtensionsContainer_RptExtNested, []*pb2.Nested{ | 
|  | &pb2.Nested{OptString: proto.String("one")}, | 
|  | &pb2.Nested{OptString: proto.String("two")}, | 
|  | &pb2.Nested{OptString: proto.String("three")}, | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | }, { | 
|  | desc:         "invalid extension field name", | 
|  | inputMessage: &pb2.Extensions{}, | 
|  | inputText:    "[pb2.invalid_message_field]: true", | 
|  | wantErr:      "unknown field", | 
|  | }, { | 
|  | desc:         "MessageSet", | 
|  | inputMessage: &pb2.MessageSet{}, | 
|  | inputText: ` | 
|  | [pb2.MessageSetExtension]: { | 
|  | opt_string: "a messageset extension" | 
|  | } | 
|  | [pb2.MessageSetExtension.ext_nested]: { | 
|  | opt_string: "just a regular extension" | 
|  | } | 
|  | [pb2.MessageSetExtension.not_message_set_extension]: { | 
|  | opt_string: "not a messageset extension" | 
|  | } | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.MessageSet{} | 
|  | proto.SetExtension(m, pb2.E_MessageSetExtension_MessageSetExtension, &pb2.MessageSetExtension{ | 
|  | OptString: proto.String("a messageset extension"), | 
|  | }) | 
|  | proto.SetExtension(m, pb2.E_MessageSetExtension_NotMessageSetExtension, &pb2.MessageSetExtension{ | 
|  | OptString: proto.String("not a messageset extension"), | 
|  | }) | 
|  | proto.SetExtension(m, pb2.E_MessageSetExtension_ExtNested, &pb2.Nested{ | 
|  | OptString: proto.String("just a regular extension"), | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | skip: !flags.ProtoLegacy, | 
|  | }, { | 
|  | desc:         "not real MessageSet 1", | 
|  | inputMessage: &pb2.FakeMessageSet{}, | 
|  | inputText: ` | 
|  | [pb2.FakeMessageSetExtension.message_set_extension]: { | 
|  | opt_string: "not a messageset extension" | 
|  | } | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.FakeMessageSet{} | 
|  | proto.SetExtension(m, pb2.E_FakeMessageSetExtension_MessageSetExtension, &pb2.FakeMessageSetExtension{ | 
|  | OptString: proto.String("not a messageset extension"), | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | skip: !flags.ProtoLegacy, | 
|  | }, { | 
|  | desc:         "not real MessageSet 2", | 
|  | inputMessage: &pb2.FakeMessageSet{}, | 
|  | inputText: ` | 
|  | [pb2.FakeMessageSetExtension]: { | 
|  | opt_string: "not a messageset extension" | 
|  | } | 
|  | `, | 
|  | wantErr: `unable to resolve [[pb2.FakeMessageSetExtension]]: found wrong type`, | 
|  | skip:    !flags.ProtoLegacy, | 
|  | }, { | 
|  | desc:         "not real MessageSet 3", | 
|  | inputMessage: &pb2.MessageSet{}, | 
|  | inputText: ` | 
|  | [pb2.message_set_extension]: { | 
|  | opt_string: "another not a messageset extension" | 
|  | }`, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.MessageSet{} | 
|  | proto.SetExtension(m, pb2.E_MessageSetExtension, &pb2.FakeMessageSetExtension{ | 
|  | OptString: proto.String("another not a messageset extension"), | 
|  | }) | 
|  | return m | 
|  | }(), | 
|  | skip: !flags.ProtoLegacy, | 
|  | }, { | 
|  | desc:         "Any not expanded", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText: ` | 
|  | type_url: "pb2.Nested" | 
|  | value: "some bytes" | 
|  | `, | 
|  | wantMessage: &anypb.Any{ | 
|  | TypeUrl: "pb2.Nested", | 
|  | Value:   []byte("some bytes"), | 
|  | }, | 
|  | }, { | 
|  | desc:         "Any not expanded missing value", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText:    `type_url: "pb2.Nested"`, | 
|  | wantMessage: &anypb.Any{ | 
|  | TypeUrl: "pb2.Nested", | 
|  | }, | 
|  | }, { | 
|  | desc:         "Any not expanded missing type_url", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText:    `value: "some bytes"`, | 
|  | wantMessage: &anypb.Any{ | 
|  | Value: []byte("some bytes"), | 
|  | }, | 
|  | }, { | 
|  | desc:         "Any expanded", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText: ` | 
|  | [foobar/pb2.Nested]: { | 
|  | opt_string: "embedded inside Any" | 
|  | opt_nested: { | 
|  | opt_string: "inception" | 
|  | } | 
|  | } | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.Nested{ | 
|  | OptString: proto.String("embedded inside Any"), | 
|  | OptNested: &pb2.Nested{ | 
|  | OptString: proto.String("inception"), | 
|  | }, | 
|  | } | 
|  | b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) | 
|  | if err != nil { | 
|  | t.Fatalf("error in binary marshaling message for Any.value: %v", err) | 
|  | } | 
|  | return &anypb.Any{ | 
|  | TypeUrl: "foobar/pb2.Nested", | 
|  | Value:   b, | 
|  | } | 
|  | }(), | 
|  | }, { | 
|  | desc:         "Any expanded with empty value", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText:    `[foo.com/pb2.Nested]: {}`, | 
|  | wantMessage: &anypb.Any{ | 
|  | TypeUrl: "foo.com/pb2.Nested", | 
|  | }, | 
|  | }, { | 
|  | desc:         "Any expanded with missing required", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText: ` | 
|  | [pb2.PartialRequired]: { | 
|  | opt_string: "embedded inside Any" | 
|  | } | 
|  | `, | 
|  | wantMessage: func() proto.Message { | 
|  | m := &pb2.PartialRequired{ | 
|  | OptString: proto.String("embedded inside Any"), | 
|  | } | 
|  | b, err := proto.MarshalOptions{ | 
|  | AllowPartial:  true, | 
|  | Deterministic: true, | 
|  | }.Marshal(m) | 
|  | if err != nil { | 
|  | t.Fatalf("error in binary marshaling message for Any.value: %v", err) | 
|  | } | 
|  | return &anypb.Any{ | 
|  | TypeUrl: "pb2.PartialRequired", | 
|  | Value:   b, | 
|  | } | 
|  | }(), | 
|  | }, { | 
|  | desc:         "Any with invalid UTF-8", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText: ` | 
|  | [pb3.Nested]: { | 
|  | s_string: "abc\xff" | 
|  | } | 
|  | `, | 
|  | wantErr: "contains invalid UTF-8", | 
|  | }, { | 
|  | desc:         "Any expanded with unregistered type", | 
|  | umo:          prototext.UnmarshalOptions{Resolver: new(protoregistry.Types)}, | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText:    `[SomeMessage]: {}`, | 
|  | wantErr:      "unable to resolve message [SomeMessage]", | 
|  | }, { | 
|  | desc:         "Any expanded with invalid value", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText:    `[pb2.Nested]: 123`, | 
|  | wantErr:      "unexpected token: 123", | 
|  | }, { | 
|  | desc:         "Any expanded with unknown fields", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText: ` | 
|  | [pb2.Nested]: {} | 
|  | unknown: "" | 
|  | `, | 
|  | wantErr: `invalid field name "unknown" in google.protobuf.Any message`, | 
|  | }, { | 
|  | desc:         "Any contains expanded and unexpanded fields", | 
|  | inputMessage: &anypb.Any{}, | 
|  | inputText: ` | 
|  | [pb2.Nested]: {} | 
|  | type_url: "pb2.Nested" | 
|  | `, | 
|  | wantErr: "(line 3:1): conflict with [pb2.Nested] field", | 
|  | }} | 
|  |  | 
|  | for _, msg := range makeMessages(protobuild.Message{}, | 
|  | &pb2.Scalars{}, | 
|  | &pb3.Scalars{}, | 
|  | &pbeditions.Scalars{}, | 
|  | ) { | 
|  | tests = append(tests, test{ | 
|  | desc:         fmt.Sprintf("empty message (%T)", msg), | 
|  | inputMessage: msg.ProtoReflect().Type().New().Interface(), | 
|  | wantMessage:  msg, | 
|  | }) | 
|  | } | 
|  |  | 
|  | for _, msg := range makeMessages(protobuild.Message{ | 
|  | "opt_bool":     false, | 
|  | "opt_int32":    0, | 
|  | "opt_int64":    0, | 
|  | "opt_uint32":   0, | 
|  | "opt_uint64":   0, | 
|  | "opt_sint32":   0, | 
|  | "opt_sint64":   0, | 
|  | "opt_fixed32":  0, | 
|  | "opt_fixed64":  0, | 
|  | "opt_sfixed32": 0, | 
|  | "opt_sfixed64": 0, | 
|  | "opt_float":    0, | 
|  | "opt_double":   0, | 
|  | "opt_bytes":    []byte{}, | 
|  | "opt_string":   "", | 
|  | }, | 
|  | &pb2.Scalars{}, | 
|  | &pb3.Proto3Optional{}, | 
|  | &pbeditions.Scalars{}, | 
|  | ) { | 
|  | tests = append(tests, test{ | 
|  | desc:         fmt.Sprintf("optional scalars set to zero values (%T)", msg), | 
|  | inputMessage: msg.ProtoReflect().Type().New().Interface(), | 
|  | inputText: `opt_bool: false | 
|  | opt_int32: 0 | 
|  | opt_int64: 0 | 
|  | opt_uint32: 0 | 
|  | opt_uint64: 0 | 
|  | opt_sint32: 0 | 
|  | opt_sint64: 0 | 
|  | opt_fixed32: 0 | 
|  | opt_fixed64: 0 | 
|  | opt_sfixed32: 0 | 
|  | opt_sfixed64: 0 | 
|  | opt_float: 0 | 
|  | opt_double: 0 | 
|  | opt_bytes: "" | 
|  | opt_string: "" | 
|  | `, | 
|  | wantMessage: msg, | 
|  | }) | 
|  | } | 
|  |  | 
|  | for _, msg := range makeMessages(protobuild.Message{ | 
|  | "s_bool":     false, | 
|  | "s_int32":    0, | 
|  | "s_int64":    0, | 
|  | "s_uint32":   0, | 
|  | "s_uint64":   0, | 
|  | "s_sint32":   0, | 
|  | "s_sint64":   0, | 
|  | "s_fixed32":  0, | 
|  | "s_fixed64":  0, | 
|  | "s_sfixed32": 0, | 
|  | "s_sfixed64": 0, | 
|  | "s_float":    0, | 
|  | "s_double":   0, | 
|  | "s_bytes":    []byte{}, | 
|  | "s_string":   "", | 
|  | }, | 
|  | &pb3.Scalars{}, | 
|  | &pbeditions.ImplicitScalars{}, | 
|  | ) { | 
|  | tests = append(tests, test{ | 
|  | desc:         fmt.Sprintf("implicit scalars set to zero values (%T)", msg), | 
|  | inputMessage: msg.ProtoReflect().Type().New().Interface(), | 
|  | inputText: `s_bool: false | 
|  | s_int32: 0 | 
|  | s_int64: 0 | 
|  | s_uint32: 0 | 
|  | s_uint64: 0 | 
|  | s_sint32: 0 | 
|  | s_sint64: 0 | 
|  | s_fixed32: 0 | 
|  | s_fixed64: 0 | 
|  | s_sfixed32: 0 | 
|  | s_sfixed64: 0 | 
|  | s_float: 0 | 
|  | s_double: 0 | 
|  | s_bytes: "" | 
|  | s_string: "" | 
|  | `, | 
|  | wantMessage: msg, | 
|  | }) | 
|  | } | 
|  |  | 
|  | for _, msg := range makeMessages(protobuild.Message{ | 
|  | "opt_bool":     true, | 
|  | "opt_int32":    0xff, | 
|  | "opt_int64":    int64(0xdeadbeef), | 
|  | "opt_uint32":   0xff, | 
|  | "opt_uint64":   uint64(0xdeadbeef), | 
|  | "opt_sint32":   -1001, | 
|  | "opt_sint64":   -0xffff, | 
|  | "opt_fixed64":  64, | 
|  | "opt_sfixed32": -32, | 
|  | "opt_float":    1.234, | 
|  | "opt_double":   1.23e100, | 
|  | "opt_bytes":    []byte("\xe8\xb0\xb7\xe6\xad\x8c"), | 
|  | "opt_string":   "谷歌", | 
|  | }, | 
|  | &pb2.Scalars{}, | 
|  | &pb3.Proto3Optional{}, | 
|  | &pbeditions.Scalars{}, | 
|  | ) { | 
|  | tests = append(tests, test{ | 
|  | desc:         fmt.Sprintf("optional scalars (%T)", msg), | 
|  | inputMessage: msg.ProtoReflect().Type().New().Interface(), | 
|  | inputText: `opt_bool: true | 
|  | opt_int32: 255 | 
|  | opt_int64: 3735928559 | 
|  | opt_uint32: 0xff | 
|  | opt_uint64: 0xdeadbeef | 
|  | opt_sint32: -1001 | 
|  | opt_sint64: -  # | 
|  | 0xffff | 
|  | opt_fixed64: 64 | 
|  | opt_sfixed32: -		32 | 
|  | opt_float: 1.234 | 
|  | opt_double: 1.23e+100 | 
|  | opt_bytes: "\xe8\xb0\xb7\xe6\xad\x8c" | 
|  | opt_string: "谷歌" | 
|  | `, | 
|  | wantMessage: msg, | 
|  | }) | 
|  | } | 
|  |  | 
|  | for _, msg := range makeMessages(protobuild.Message{ | 
|  | "opt_enum":        "ONE", | 
|  | "opt_nested_enum": "UNO", | 
|  | }, | 
|  | &pb2.Enums{}, | 
|  | &pb3.OptionalEnums{}, | 
|  | &pbeditions.Enums{}, | 
|  | ) { | 
|  | tests = append(tests, test{ | 
|  | desc:         fmt.Sprintf("enum (%T)", msg), | 
|  | inputMessage: msg.ProtoReflect().Type().New().Interface(), | 
|  | inputText: ` | 
|  | opt_enum: ONE | 
|  | opt_nested_enum: UNO | 
|  | `, | 
|  | wantMessage: msg, | 
|  | }) | 
|  |  | 
|  | tests = append(tests, test{ | 
|  | desc:         fmt.Sprintf("enum set to numeric values (%T)", msg), | 
|  | inputMessage: msg.ProtoReflect().Type().New().Interface(), | 
|  | inputText: ` | 
|  | opt_enum: 1 | 
|  | opt_nested_enum: 1 | 
|  | `, | 
|  | wantMessage: msg, | 
|  | }) | 
|  | } | 
|  |  | 
|  | for _, tt := range tests { | 
|  | tt := tt | 
|  | if tt.skip { | 
|  | continue | 
|  | } | 
|  | t.Run(tt.desc, func(t *testing.T) { | 
|  | err := tt.umo.Unmarshal([]byte(tt.inputText), tt.inputMessage) | 
|  | if err != nil { | 
|  | if tt.wantErr == "" { | 
|  | t.Errorf("Unmarshal() got unexpected error: %v", err) | 
|  | } else if !strings.Contains(err.Error(), tt.wantErr) { | 
|  | t.Errorf("Unmarshal() error got %q, want %q", err, tt.wantErr) | 
|  | } | 
|  | return | 
|  | } | 
|  | if tt.wantErr != "" { | 
|  | t.Errorf("Unmarshal() got nil error, want error %q", tt.wantErr) | 
|  | return | 
|  | } | 
|  | if tt.wantMessage != nil && !proto.Equal(tt.inputMessage, tt.wantMessage) { | 
|  | t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage) | 
|  | } | 
|  | }) | 
|  | } | 
|  | } |