mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-04 21:39:58 +00:00
800c990248
This initial textproto unmarshaling implementation covers messages without the use of extensions, Any expansion, nor weak. Updated encoding tests. Split some testcases to keep each simpler. Added TestRoundTrip for example messages like the well-known types. Change-Id: Icffab02834aa004fa8409a9da70624f687f604fb Reviewed-on: https://go-review.googlesource.com/c/153020 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
986 lines
20 KiB
Go
986 lines
20 KiB
Go
// 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 textpb_test
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
|
|
protoV1 "github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/v2/encoding/textpb"
|
|
"github.com/golang/protobuf/v2/internal/scalar"
|
|
|
|
// The legacy package must be imported prior to use of any legacy messages.
|
|
// TODO: Remove this when protoV1 registers these hooks for you.
|
|
_ "github.com/golang/protobuf/v2/internal/legacy"
|
|
|
|
"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
|
|
"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
|
|
)
|
|
|
|
func TestUnmarshal(t *testing.T) {
|
|
// TODO: Switch to using proto.Message for inputMessage and wantMessage fields when v2
|
|
// proto.Equal is implemented.
|
|
tests := []struct {
|
|
desc string
|
|
inputMessage protoV1.Message
|
|
inputText string
|
|
wantMessage protoV1.Message
|
|
wantErr bool
|
|
}{{
|
|
desc: "proto2 empty message",
|
|
inputMessage: &pb2.Scalars{},
|
|
wantMessage: &pb2.Scalars{},
|
|
}, {
|
|
desc: "proto2 optional scalar fields set to zero values",
|
|
inputMessage: &pb2.Scalars{},
|
|
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: &pb2.Scalars{
|
|
OptBool: scalar.Bool(false),
|
|
OptInt32: scalar.Int32(0),
|
|
OptInt64: scalar.Int64(0),
|
|
OptUint32: scalar.Uint32(0),
|
|
OptUint64: scalar.Uint64(0),
|
|
OptSint32: scalar.Int32(0),
|
|
OptSint64: scalar.Int64(0),
|
|
OptFixed32: scalar.Uint32(0),
|
|
OptFixed64: scalar.Uint64(0),
|
|
OptSfixed32: scalar.Int32(0),
|
|
OptSfixed64: scalar.Int64(0),
|
|
OptFloat: scalar.Float32(0),
|
|
OptDouble: scalar.Float64(0),
|
|
OptBytes: []byte{},
|
|
OptString: scalar.String(""),
|
|
},
|
|
}, {
|
|
desc: "proto3 scalar fields set to zero values",
|
|
inputMessage: &pb3.Scalars{},
|
|
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: &pb3.Scalars{},
|
|
}, {
|
|
desc: "proto2 optional scalar fields",
|
|
inputMessage: &pb2.Scalars{},
|
|
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: &pb2.Scalars{
|
|
OptBool: scalar.Bool(true),
|
|
OptInt32: scalar.Int32(0xff),
|
|
OptInt64: scalar.Int64(0xdeadbeef),
|
|
OptUint32: scalar.Uint32(0xff),
|
|
OptUint64: scalar.Uint64(0xdeadbeef),
|
|
OptSint32: scalar.Int32(-1001),
|
|
OptSint64: scalar.Int64(-0xffff),
|
|
OptFixed64: scalar.Uint64(64),
|
|
OptSfixed32: scalar.Int32(-32),
|
|
OptFloat: scalar.Float32(1.234),
|
|
OptDouble: scalar.Float64(1.23e100),
|
|
OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
|
|
OptString: scalar.String("谷歌"),
|
|
},
|
|
}, {
|
|
desc: "proto3 scalar fields",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: `s_bool: true
|
|
s_int32: 255
|
|
s_int64: 3735928559
|
|
s_uint32: 0xff
|
|
s_uint64: 0xdeadbeef
|
|
s_sint32: -1001
|
|
s_sint64: -0xffff
|
|
s_fixed64: 64
|
|
s_sfixed32: -32
|
|
s_float: 1.234
|
|
s_double: 1.23e+100
|
|
s_bytes: "\xe8\xb0\xb7\xe6\xad\x8c"
|
|
s_string: "谷歌"
|
|
`,
|
|
wantMessage: &pb3.Scalars{
|
|
SBool: true,
|
|
SInt32: 0xff,
|
|
SInt64: 0xdeadbeef,
|
|
SUint32: 0xff,
|
|
SUint64: 0xdeadbeef,
|
|
SSint32: -1001,
|
|
SSint64: -0xffff,
|
|
SFixed64: 64,
|
|
SSfixed32: -32,
|
|
SFloat: 1.234,
|
|
SDouble: 1.23e100,
|
|
SBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
|
|
SString: "谷歌",
|
|
},
|
|
}, {
|
|
desc: "proto2 message contains unknown field",
|
|
inputMessage: &pb2.Scalars{},
|
|
inputText: "unknown_field: 123",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto3 message contains unknown field",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "unknown_field: 456",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto2 numeric key field",
|
|
inputMessage: &pb2.Scalars{},
|
|
inputText: "1: true",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto3 numeric key field",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "1: true",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid bool value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_bool: 123",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid int32 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_int32: not_a_num",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid int64 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_int64: 'not a num either'",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid uint32 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_fixed32: -42",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid uint64 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_uint64: -47",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid sint32 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_sint32: '42'",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid sint64 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_sint64: '-47'",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid fixed32 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_fixed32: -42",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid fixed64 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_fixed64: -42",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid sfixed32 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_sfixed32: 'not valid'",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "invalid sfixed64 value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_sfixed64: bad",
|
|
wantErr: true,
|
|
}, {
|
|
desc: "float32 positive infinity",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_float: inf",
|
|
wantMessage: &pb3.Scalars{
|
|
SFloat: float32(math.Inf(1)),
|
|
},
|
|
}, {
|
|
desc: "float32 negative infinity",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_float: -inf",
|
|
wantMessage: &pb3.Scalars{
|
|
SFloat: float32(math.Inf(-1)),
|
|
},
|
|
}, {
|
|
desc: "float64 positive infinity",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_double: inf",
|
|
wantMessage: &pb3.Scalars{
|
|
SDouble: math.Inf(1),
|
|
},
|
|
}, {
|
|
desc: "float64 negative infinity",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_double: -inf",
|
|
wantMessage: &pb3.Scalars{
|
|
SDouble: math.Inf(-1),
|
|
},
|
|
}, {
|
|
desc: "invalid string value",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: "s_string: invalid_string",
|
|
wantErr: true,
|
|
}, {
|
|
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: true,
|
|
}, {
|
|
desc: "proto2 invalid singular field",
|
|
inputMessage: &pb2.Scalars{},
|
|
inputText: `
|
|
opt_bool: [true, false]
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto2 more duplicate singular field",
|
|
inputMessage: &pb2.Scalars{},
|
|
inputText: `
|
|
opt_bool: true
|
|
opt_string: "hello"
|
|
opt_bool: false
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto3 duplicate singular field",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: `
|
|
s_bool: false
|
|
s_bool: true
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto3 more duplicate singular field",
|
|
inputMessage: &pb3.Scalars{},
|
|
inputText: `
|
|
s_bool: false
|
|
s_string: ""
|
|
s_bool: true
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto2 enum",
|
|
inputMessage: &pb2.Enums{},
|
|
inputText: `
|
|
opt_enum: FIRST
|
|
opt_nested_enum: UNO
|
|
`,
|
|
wantMessage: &pb2.Enums{
|
|
OptEnum: pb2.Enum_FIRST.Enum(),
|
|
OptNestedEnum: pb2.Enums_UNO.Enum(),
|
|
},
|
|
}, {
|
|
desc: "proto2 enum set to numeric values",
|
|
inputMessage: &pb2.Enums{},
|
|
inputText: `
|
|
opt_enum: 1
|
|
opt_nested_enum: 2
|
|
`,
|
|
wantMessage: &pb2.Enums{
|
|
OptEnum: pb2.Enum_FIRST.Enum(),
|
|
OptNestedEnum: pb2.Enums_DOS.Enum(),
|
|
},
|
|
}, {
|
|
desc: "proto2 enum set to unnamed numeric values",
|
|
inputMessage: &pb2.Enums{},
|
|
inputText: `
|
|
opt_enum: 101
|
|
opt_nested_enum: -101
|
|
`,
|
|
wantMessage: &pb2.Enums{
|
|
OptEnum: pb2Enum(101),
|
|
OptNestedEnum: pb2Enums_NestedEnum(-101),
|
|
},
|
|
}, {
|
|
desc: "proto2 enum set to invalid named",
|
|
inputMessage: &pb2.Enums{},
|
|
inputText: `
|
|
opt_enum: UNNAMED
|
|
opt_nested_enum: UNNAMED_TOO
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
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: 1
|
|
`,
|
|
wantMessage: &pb3.Enums{
|
|
SEnum: pb3.Enum_TWO,
|
|
SNestedEnum: pb3.Enums_UNO,
|
|
},
|
|
}, {
|
|
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: "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: scalar.String("nested message"),
|
|
OptNested: &pb2.Nested{
|
|
OptString: scalar.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: "oneof field set to empty string",
|
|
inputMessage: &pb2.Oneofs{},
|
|
inputText: "str: ''",
|
|
wantMessage: &pb2.Oneofs{
|
|
Union: &pb2.Oneofs_Str{},
|
|
},
|
|
}, {
|
|
desc: "oneof field set to string",
|
|
inputMessage: &pb2.Oneofs{},
|
|
inputText: "str: 'hello'",
|
|
wantMessage: &pb2.Oneofs{
|
|
Union: &pb2.Oneofs_Str{
|
|
Str: "hello",
|
|
},
|
|
},
|
|
}, {
|
|
desc: "oneof field set to empty message",
|
|
inputMessage: &pb2.Oneofs{},
|
|
inputText: "msg: {}",
|
|
wantMessage: &pb2.Oneofs{
|
|
Union: &pb2.Oneofs_Msg{
|
|
Msg: &pb2.Nested{},
|
|
},
|
|
},
|
|
}, {
|
|
desc: "oneof field set to message",
|
|
inputMessage: &pb2.Oneofs{},
|
|
inputText: `
|
|
msg: {
|
|
opt_string: "nested message"
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Oneofs{
|
|
Union: &pb2.Oneofs_Msg{
|
|
Msg: &pb2.Nested{
|
|
OptString: scalar.String("nested message"),
|
|
},
|
|
},
|
|
},
|
|
}, {
|
|
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 enums",
|
|
inputMessage: &pb2.Enums{},
|
|
inputText: `
|
|
rpt_enum: TENTH
|
|
rpt_enum: 1
|
|
rpt_nested_enum: [DOS, 2]
|
|
rpt_enum: 42
|
|
rpt_nested_enum: -47
|
|
`,
|
|
wantMessage: &pb2.Enums{
|
|
RptEnum: []pb2.Enum{pb2.Enum_TENTH, pb2.Enum_FIRST, 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: scalar.String("repeat nested one"),
|
|
},
|
|
{
|
|
OptString: scalar.String("repeat nested two"),
|
|
OptNested: &pb2.Nested{
|
|
OptString: scalar.String("inside repeat nested two"),
|
|
},
|
|
},
|
|
{},
|
|
},
|
|
},
|
|
}, {
|
|
desc: "repeated group fields",
|
|
inputMessage: &pb2.Nests{},
|
|
inputText: `
|
|
rptgroup: {
|
|
rpt_bool: true
|
|
rpt_bool: false
|
|
}
|
|
rptgroup: {}
|
|
`,
|
|
wantMessage: &pb2.Nests{
|
|
Rptgroup: []*pb2.Nests_RptGroup{
|
|
{
|
|
RptBool: []bool{true, false},
|
|
},
|
|
{},
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map fields 1",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: -101
|
|
value: "-101"
|
|
}
|
|
int32_to_str: {
|
|
key: 0
|
|
value: "zero"
|
|
}
|
|
sfixed64_to_bool: {
|
|
key: 0
|
|
value: false
|
|
}
|
|
int32_to_str: {
|
|
key: 255
|
|
value: "0xff"
|
|
}
|
|
bool_to_uint32: {
|
|
key: false
|
|
value: 101
|
|
}
|
|
sfixed64_to_bool: {
|
|
key: 51966
|
|
value: true
|
|
}
|
|
bool_to_uint32: {
|
|
key: true
|
|
value: 42
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
Int32ToStr: map[int32]string{
|
|
-101: "-101",
|
|
0xff: "0xff",
|
|
0: "zero",
|
|
},
|
|
Sfixed64ToBool: map[int64]bool{
|
|
0xcafe: true,
|
|
0: false,
|
|
},
|
|
BoolToUint32: map[bool]uint32{
|
|
true: 42,
|
|
false: 101,
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map fields 2",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
uint64_to_enum: {
|
|
key: 1
|
|
value: FIRST
|
|
}
|
|
uint64_to_enum: {
|
|
key: 2
|
|
value: SECOND
|
|
}
|
|
uint64_to_enum: {
|
|
key: 10
|
|
value: TENTH
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
Uint64ToEnum: map[uint64]pb2.Enum{
|
|
1: pb2.Enum_FIRST,
|
|
2: pb2.Enum_SECOND,
|
|
10: pb2.Enum_TENTH,
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map fields 3",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
str_to_nested: {
|
|
key: "nested_one"
|
|
value: {
|
|
opt_string: "nested in a map"
|
|
}
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
StrToNested: map[string]*pb2.Nested{
|
|
"nested_one": &pb2.Nested{
|
|
OptString: scalar.String("nested in a map"),
|
|
},
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map fields 4",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
str_to_oneofs: {
|
|
key: "nested"
|
|
value: {
|
|
msg: {
|
|
opt_string: "nested oneof in map field value"
|
|
}
|
|
}
|
|
}
|
|
str_to_oneofs: {
|
|
key: "string"
|
|
value: {
|
|
str: "hello"
|
|
}
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
StrToOneofs: map[string]*pb2.Oneofs{
|
|
"string": &pb2.Oneofs{
|
|
Union: &pb2.Oneofs_Str{
|
|
Str: "hello",
|
|
},
|
|
},
|
|
"nested": &pb2.Oneofs{
|
|
Union: &pb2.Oneofs_Msg{
|
|
Msg: &pb2.Nested{
|
|
OptString: scalar.String("nested oneof in map field value"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map contains duplicate keys",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: 0
|
|
value: "cero"
|
|
}
|
|
int32_to_str: {
|
|
key: 0
|
|
value: "zero"
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
Int32ToStr: map[int32]string{
|
|
0: "zero",
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map contains duplicate key fields",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: 0
|
|
key: 1
|
|
value: "cero"
|
|
}
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "map contains duplicate value fields",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: 1
|
|
value: "cero"
|
|
value: "uno"
|
|
}
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "map contains missing key",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
value: "zero"
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
Int32ToStr: map[int32]string{
|
|
0: "zero",
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map contains missing value",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: 100
|
|
}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
Int32ToStr: map[int32]string{
|
|
100: "",
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map contains missing key and value",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {}
|
|
`,
|
|
wantMessage: &pb2.Maps{
|
|
Int32ToStr: map[int32]string{
|
|
0: "",
|
|
},
|
|
},
|
|
}, {
|
|
desc: "map contains unknown field",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: 0
|
|
value: "cero"
|
|
unknown: "bad"
|
|
}
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "map contains extension-like key field",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
[key]: 10
|
|
value: "ten"
|
|
}
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "map contains invalid key",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: "invalid"
|
|
value: "cero"
|
|
}
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "map contains invalid value",
|
|
inputMessage: &pb2.Maps{},
|
|
inputText: `
|
|
int32_to_str: {
|
|
key: 100
|
|
value: 101
|
|
}
|
|
`,
|
|
wantErr: true,
|
|
}, {
|
|
desc: "map using mix of [] and repeated",
|
|
inputMessage: &pb2.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: &pb2.Maps{
|
|
Int32ToStr: map[int32]string{
|
|
0: "",
|
|
1: "one",
|
|
2: "two",
|
|
3: "three",
|
|
},
|
|
},
|
|
}, {
|
|
desc: "proto2 required fields not set",
|
|
inputMessage: &pb2.Requireds{},
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto2 required field set but not optional",
|
|
inputMessage: &pb2.PartialRequired{},
|
|
inputText: "req_string: 'this is required'",
|
|
wantMessage: &pb2.PartialRequired{
|
|
ReqString: scalar.String("this is required"),
|
|
},
|
|
}, {
|
|
desc: "proto2 required fields partially set",
|
|
inputMessage: &pb2.Requireds{},
|
|
inputText: `
|
|
req_bool: false
|
|
req_fixed32: 47
|
|
req_sfixed64: 3203386110
|
|
req_string: "hello"
|
|
req_enum: FIRST
|
|
`,
|
|
wantMessage: &pb2.Requireds{
|
|
ReqBool: scalar.Bool(false),
|
|
ReqFixed32: scalar.Uint32(47),
|
|
ReqSfixed64: scalar.Int64(0xbeefcafe),
|
|
ReqString: scalar.String("hello"),
|
|
ReqEnum: pb2.Enum_FIRST.Enum(),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
desc: "proto2 required fields all set",
|
|
inputMessage: &pb2.Requireds{},
|
|
inputText: `
|
|
req_bool: false
|
|
req_fixed32: 0
|
|
req_fixed64: 0
|
|
req_sfixed32: 0
|
|
req_sfixed64: 0
|
|
req_float: 0
|
|
req_double: 0
|
|
req_string: ""
|
|
req_bytes: ""
|
|
req_enum: UNKNOWN
|
|
req_nested: {}
|
|
`,
|
|
wantMessage: &pb2.Requireds{
|
|
ReqBool: scalar.Bool(false),
|
|
ReqFixed32: scalar.Uint32(0),
|
|
ReqFixed64: scalar.Uint64(0),
|
|
ReqSfixed32: scalar.Int32(0),
|
|
ReqSfixed64: scalar.Int64(0),
|
|
ReqFloat: scalar.Float32(0),
|
|
ReqDouble: scalar.Float64(0),
|
|
ReqString: scalar.String(""),
|
|
ReqEnum: pb2.Enum_UNKNOWN.Enum(),
|
|
ReqBytes: []byte{},
|
|
ReqNested: &pb2.Nested{},
|
|
},
|
|
}, {
|
|
desc: "indirect required field",
|
|
inputMessage: &pb2.IndirectRequired{},
|
|
inputText: "opt_nested: {}",
|
|
wantMessage: &pb2.IndirectRequired{
|
|
OptNested: &pb2.NestedWithRequired{},
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
desc: "indirect required field in repeated",
|
|
inputMessage: &pb2.IndirectRequired{},
|
|
inputText: `
|
|
rpt_nested: {
|
|
req_string: "one"
|
|
}
|
|
rpt_nested: {}
|
|
rpt_nested: {
|
|
req_string: "three"
|
|
}
|
|
`,
|
|
wantMessage: &pb2.IndirectRequired{
|
|
RptNested: []*pb2.NestedWithRequired{
|
|
{
|
|
ReqString: scalar.String("one"),
|
|
},
|
|
{},
|
|
{
|
|
ReqString: scalar.String("three"),
|
|
},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
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: scalar.String("here"),
|
|
},
|
|
},
|
|
},
|
|
wantErr: true,
|
|
}}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
err := textpb.Unmarshal(M(tt.inputMessage), []byte(tt.inputText))
|
|
if err != nil && !tt.wantErr {
|
|
t.Errorf("Unmarshal() returned error: %v\n\n", err)
|
|
}
|
|
if err == nil && tt.wantErr {
|
|
t.Error("Unmarshal() got nil error, want error\n\n")
|
|
}
|
|
if tt.wantMessage != nil && !protoV1.Equal(tt.inputMessage, tt.wantMessage) {
|
|
t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage)
|
|
}
|
|
})
|
|
}
|
|
}
|