| // 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" |
| "strings" |
| "testing" |
| |
| "github.com/golang/protobuf/v2/encoding/textpb" |
| "github.com/golang/protobuf/v2/internal/detrand" |
| "github.com/golang/protobuf/v2/internal/impl" |
| "github.com/golang/protobuf/v2/internal/scalar" |
| "github.com/golang/protobuf/v2/proto" |
| "github.com/google/go-cmp/cmp" |
| "github.com/google/go-cmp/cmp/cmpopts" |
| |
| // 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" |
| |
| anypb "github.com/golang/protobuf/ptypes/any" |
| durpb "github.com/golang/protobuf/ptypes/duration" |
| emptypb "github.com/golang/protobuf/ptypes/empty" |
| stpb "github.com/golang/protobuf/ptypes/struct" |
| tspb "github.com/golang/protobuf/ptypes/timestamp" |
| wpb "github.com/golang/protobuf/ptypes/wrappers" |
| "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2" |
| "github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3" |
| ) |
| |
| func init() { |
| // Disable detrand to enable direct comparisons on outputs. |
| detrand.Disable() |
| } |
| |
| func M(m interface{}) proto.Message { |
| return impl.Export{}.MessageOf(m).Interface() |
| } |
| |
| // splitLines is a cmpopts.Option for comparing strings with line breaks. |
| var splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string { |
| return strings.Split(s, "\n") |
| }) |
| |
| func TestMarshal(t *testing.T) { |
| tests := []struct { |
| desc string |
| input proto.Message |
| want string |
| wantErr bool |
| }{{ |
| desc: "nil message", |
| want: "\n", |
| }, { |
| desc: "proto2 optional scalar fields not set", |
| input: M(&pb2.Scalars{}), |
| want: "\n", |
| }, { |
| desc: "proto3 scalar fields not set", |
| input: M(&pb3.Scalars{}), |
| want: "\n", |
| }, { |
| desc: "proto2 optional scalar fields set to zero values", |
| input: M(&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(""), |
| }), |
| want: `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: "" |
| `, |
| }, { |
| desc: "proto3 scalar fields set to zero values", |
| input: M(&pb3.Scalars{ |
| SBool: false, |
| SInt32: 0, |
| SInt64: 0, |
| SUint32: 0, |
| SUint64: 0, |
| SSint32: 0, |
| SSint64: 0, |
| SFixed32: 0, |
| SFixed64: 0, |
| SSfixed32: 0, |
| SSfixed64: 0, |
| SFloat: 0, |
| SDouble: 0, |
| SBytes: []byte{}, |
| SString: "", |
| }), |
| want: "\n", |
| }, { |
| desc: "proto2 optional scalar fields set to some values", |
| input: M(&pb2.Scalars{ |
| OptBool: scalar.Bool(true), |
| OptInt32: scalar.Int32(0xff), |
| OptInt64: scalar.Int64(0xdeadbeef), |
| OptUint32: scalar.Uint32(47), |
| OptUint64: scalar.Uint64(0xdeadbeef), |
| OptSint32: scalar.Int32(-1001), |
| OptSint64: scalar.Int64(-0xffff), |
| OptFixed64: scalar.Uint64(64), |
| OptSfixed32: scalar.Int32(-32), |
| // TODO: Update encoder to output same decimals. |
| OptFloat: scalar.Float32(1.02), |
| OptDouble: scalar.Float64(1.23e100), |
| // TODO: Update encoder to not output UTF8 for bytes. |
| OptBytes: []byte("\xe8\xb0\xb7\xe6\xad\x8c"), |
| OptString: scalar.String("谷歌"), |
| }), |
| want: `opt_bool: true |
| opt_int32: 255 |
| opt_int64: 3735928559 |
| opt_uint32: 47 |
| opt_uint64: 3735928559 |
| opt_sint32: -1001 |
| opt_sint64: -65535 |
| opt_fixed64: 64 |
| opt_sfixed32: -32 |
| opt_float: 1.0199999809265137 |
| opt_double: 1.23e+100 |
| opt_bytes: "谷歌" |
| opt_string: "谷歌" |
| `, |
| }, { |
| desc: "proto3 enum empty message", |
| input: M(&pb3.Enums{}), |
| want: "\n", |
| }, { |
| desc: "proto3 enum", |
| input: M(&pb3.Enums{ |
| SEnum: pb3.Enum_ONE, |
| RptEnum: []pb3.Enum{pb3.Enum_ONE, 10, 0, 21, -1}, |
| SNestedEnum: pb3.Enums_DIEZ, |
| RptNestedEnum: []pb3.Enums_NestedEnum{21, pb3.Enums_CERO, -7, 10}, |
| }), |
| want: `s_enum: ONE |
| rpt_enum: ONE |
| rpt_enum: TEN |
| rpt_enum: ZERO |
| rpt_enum: 21 |
| rpt_enum: -1 |
| s_nested_enum: DIEZ |
| rpt_nested_enum: 21 |
| rpt_nested_enum: CERO |
| rpt_nested_enum: -7 |
| rpt_nested_enum: DIEZ |
| `, |
| }, { |
| desc: "float32 nan", |
| input: M(&pb3.Scalars{ |
| SFloat: float32(math.NaN()), |
| }), |
| want: "s_float: nan\n", |
| }, { |
| desc: "float32 positive infinity", |
| input: M(&pb3.Scalars{ |
| SFloat: float32(math.Inf(1)), |
| }), |
| want: "s_float: inf\n", |
| }, { |
| desc: "float32 negative infinity", |
| input: M(&pb3.Scalars{ |
| SFloat: float32(math.Inf(-1)), |
| }), |
| want: "s_float: -inf\n", |
| }, { |
| desc: "float64 nan", |
| input: M(&pb3.Scalars{ |
| SDouble: math.NaN(), |
| }), |
| want: "s_double: nan\n", |
| }, { |
| desc: "float64 positive infinity", |
| input: M(&pb3.Scalars{ |
| SDouble: math.Inf(1), |
| }), |
| want: "s_double: inf\n", |
| }, { |
| desc: "float64 negative infinity", |
| input: M(&pb3.Scalars{ |
| SDouble: math.Inf(-1), |
| }), |
| want: "s_double: -inf\n", |
| }, { |
| desc: "proto2 bytes set to empty string", |
| input: M(&pb2.Scalars{ |
| OptBytes: []byte(""), |
| }), |
| want: "opt_bytes: \"\"\n", |
| }, { |
| desc: "proto3 bytes set to empty string", |
| input: M(&pb3.Scalars{ |
| SBytes: []byte(""), |
| }), |
| want: "\n", |
| }, { |
| desc: "proto2 repeated not set", |
| input: M(&pb2.Repeats{}), |
| want: "\n", |
| }, { |
| desc: "proto2 repeated set to empty slices", |
| input: M(&pb2.Repeats{ |
| RptBool: []bool{}, |
| RptInt32: []int32{}, |
| RptInt64: []int64{}, |
| RptUint32: []uint32{}, |
| RptUint64: []uint64{}, |
| RptFloat: []float32{}, |
| RptDouble: []float64{}, |
| RptBytes: [][]byte{}, |
| }), |
| want: "\n", |
| }, { |
| desc: "proto2 repeated set to some values", |
| input: M(&pb2.Repeats{ |
| RptBool: []bool{true, false, true, true}, |
| RptInt32: []int32{1, 6, 0, 0}, |
| RptInt64: []int64{-64, 47}, |
| RptUint32: []uint32{0xff, 0xffff}, |
| RptUint64: []uint64{0xdeadbeef}, |
| // TODO: add float32 examples. |
| RptDouble: []float64{math.NaN(), math.Inf(1), math.Inf(-1), 1.23e-308}, |
| RptString: []string{"hello", "世界"}, |
| RptBytes: [][]byte{ |
| []byte("hello"), |
| []byte("\xe4\xb8\x96\xe7\x95\x8c"), |
| }, |
| }), |
| want: `rpt_bool: true |
| rpt_bool: false |
| rpt_bool: true |
| rpt_bool: true |
| rpt_int32: 1 |
| rpt_int32: 6 |
| rpt_int32: 0 |
| rpt_int32: 0 |
| rpt_int64: -64 |
| rpt_int64: 47 |
| rpt_uint32: 255 |
| rpt_uint32: 65535 |
| rpt_uint64: 3735928559 |
| rpt_double: nan |
| rpt_double: inf |
| rpt_double: -inf |
| rpt_double: 1.23e-308 |
| rpt_string: "hello" |
| rpt_string: "世界" |
| rpt_bytes: "hello" |
| rpt_bytes: "世界" |
| `, |
| }, { |
| desc: "proto2 enum fields not set", |
| input: M(&pb2.Enums{}), |
| want: "\n", |
| }, { |
| desc: "proto2 enum fields", |
| input: M(&pb2.Enums{ |
| OptEnum: pb2.Enum_FIRST.Enum(), |
| RptEnum: []pb2.Enum{pb2.Enum_FIRST, 2, pb2.Enum_TENTH, 42}, |
| OptNestedEnum: pb2.Enums_UNO.Enum(), |
| RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10}, |
| }), |
| want: `opt_enum: FIRST |
| rpt_enum: FIRST |
| rpt_enum: SECOND |
| rpt_enum: TENTH |
| rpt_enum: 42 |
| opt_nested_enum: UNO |
| rpt_nested_enum: DOS |
| rpt_nested_enum: 47 |
| rpt_nested_enum: DIEZ |
| `, |
| }, { |
| desc: "proto3 enum fields set to zero value", |
| input: M(&pb3.Enums{ |
| SEnum: pb3.Enum_ZERO, |
| RptEnum: []pb3.Enum{}, |
| SNestedEnum: pb3.Enums_CERO, |
| RptNestedEnum: []pb3.Enums_NestedEnum{}, |
| }), |
| want: "\n", |
| }, { |
| desc: "proto3 enum fields", |
| input: M(&pb3.Enums{ |
| SEnum: pb3.Enum_TWO, |
| RptEnum: []pb3.Enum{1, 0, 0}, |
| SNestedEnum: pb3.Enums_DOS, |
| RptNestedEnum: []pb3.Enums_NestedEnum{101, pb3.Enums_DIEZ, 10}, |
| }), |
| want: `s_enum: TWO |
| rpt_enum: ONE |
| rpt_enum: ZERO |
| rpt_enum: ZERO |
| s_nested_enum: DOS |
| rpt_nested_enum: 101 |
| rpt_nested_enum: DIEZ |
| rpt_nested_enum: DIEZ |
| `, |
| }, { |
| desc: "proto2 nested message not set", |
| input: M(&pb2.Nests{}), |
| want: "\n", |
| }, { |
| desc: "proto2 nested message set to empty", |
| input: M(&pb2.Nests{ |
| OptNested: &pb2.Nested{}, |
| Optgroup: &pb2.Nests_OptGroup{}, |
| RptNested: []*pb2.Nested{}, |
| Rptgroup: []*pb2.Nests_RptGroup{}, |
| }), |
| want: `opt_nested: {} |
| optgroup: {} |
| `, |
| }, { |
| desc: "proto2 nested messages", |
| input: M(&pb2.Nests{ |
| OptNested: &pb2.Nested{ |
| OptString: scalar.String("nested message"), |
| OptNested: &pb2.Nested{ |
| OptString: scalar.String("another nested message"), |
| }, |
| }, |
| 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"), |
| }, |
| }, |
| {}, |
| }, |
| }), |
| want: `opt_nested: { |
| opt_string: "nested message" |
| opt_nested: { |
| opt_string: "another nested message" |
| } |
| } |
| rpt_nested: { |
| opt_string: "repeat nested one" |
| } |
| rpt_nested: { |
| opt_string: "repeat nested two" |
| opt_nested: { |
| opt_string: "inside repeat nested two" |
| } |
| } |
| rpt_nested: {} |
| `, |
| }, { |
| desc: "proto2 group fields", |
| input: M(&pb2.Nests{ |
| Optgroup: &pb2.Nests_OptGroup{ |
| OptBool: scalar.Bool(true), |
| OptString: scalar.String("inside a group"), |
| OptNested: &pb2.Nested{ |
| OptString: scalar.String("nested message inside a group"), |
| }, |
| Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{ |
| OptEnum: pb2.Enum_TENTH.Enum(), |
| }, |
| }, |
| Rptgroup: []*pb2.Nests_RptGroup{ |
| { |
| RptBool: []bool{true, false}, |
| }, |
| {}, |
| }, |
| }), |
| want: `optgroup: { |
| opt_bool: true |
| opt_string: "inside a group" |
| opt_nested: { |
| opt_string: "nested message inside a group" |
| } |
| optnestedgroup: { |
| opt_enum: TENTH |
| } |
| } |
| rptgroup: { |
| rpt_bool: true |
| rpt_bool: false |
| } |
| rptgroup: {} |
| `, |
| }, { |
| desc: "proto3 nested message not set", |
| input: M(&pb3.Nests{}), |
| want: "\n", |
| }, { |
| desc: "proto3 nested message", |
| input: M(&pb3.Nests{ |
| SNested: &pb3.Nested{ |
| SString: "nested message", |
| SNested: &pb3.Nested{ |
| SString: "another nested message", |
| }, |
| }, |
| RptNested: []*pb3.Nested{ |
| { |
| SString: "repeated nested one", |
| SNested: &pb3.Nested{ |
| SString: "inside repeated nested one", |
| }, |
| }, |
| { |
| SString: "repeated nested two", |
| }, |
| {}, |
| }, |
| }), |
| want: `s_nested: { |
| s_string: "nested message" |
| s_nested: { |
| s_string: "another nested message" |
| } |
| } |
| rpt_nested: { |
| s_string: "repeated nested one" |
| s_nested: { |
| s_string: "inside repeated nested one" |
| } |
| } |
| rpt_nested: { |
| s_string: "repeated nested two" |
| } |
| rpt_nested: {} |
| `, |
| }, { |
| desc: "proto2 required fields not set", |
| input: M(&pb2.Requireds{}), |
| want: "\n", |
| wantErr: true, |
| }, { |
| desc: "proto2 required fields partially set", |
| input: M(&pb2.Requireds{ |
| ReqBool: scalar.Bool(false), |
| ReqFixed32: scalar.Uint32(47), |
| ReqSfixed64: scalar.Int64(0xbeefcafe), |
| ReqDouble: scalar.Float64(math.NaN()), |
| ReqString: scalar.String("hello"), |
| ReqEnum: pb2.Enum_FIRST.Enum(), |
| }), |
| want: `req_bool: false |
| req_fixed32: 47 |
| req_sfixed64: 3203386110 |
| req_double: nan |
| req_string: "hello" |
| req_enum: FIRST |
| `, |
| wantErr: true, |
| }, { |
| desc: "proto2 required fields all set", |
| input: M(&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{}, |
| }), |
| want: `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: {} |
| `, |
| }, { |
| desc: "oneof fields", |
| input: M(&pb2.Oneofs{}), |
| want: "\n", |
| }, { |
| desc: "oneof field set to empty string", |
| input: M(&pb2.Oneofs{ |
| Union: &pb2.Oneofs_Str{}, |
| }), |
| want: "str: \"\"\n", |
| }, { |
| desc: "oneof field set to string", |
| input: M(&pb2.Oneofs{ |
| Union: &pb2.Oneofs_Str{ |
| Str: "hello", |
| }, |
| }), |
| want: "str: \"hello\"\n", |
| }, { |
| desc: "oneof field set to empty message", |
| input: M(&pb2.Oneofs{ |
| Union: &pb2.Oneofs_Msg{ |
| Msg: &pb2.Nested{}, |
| }, |
| }), |
| want: "msg: {}\n", |
| }, { |
| desc: "oneof field set to message", |
| input: M(&pb2.Oneofs{ |
| Union: &pb2.Oneofs_Msg{ |
| Msg: &pb2.Nested{ |
| OptString: scalar.String("nested message"), |
| }, |
| }, |
| }), |
| want: `msg: { |
| opt_string: "nested message" |
| } |
| `, |
| }, { |
| desc: "map fields empty", |
| input: M(&pb2.Maps{}), |
| want: "\n", |
| }, { |
| desc: "map fields set to empty maps", |
| input: M(&pb2.Maps{ |
| Int32ToStr: map[int32]string{}, |
| Sfixed64ToBool: map[int64]bool{}, |
| BoolToUint32: map[bool]uint32{}, |
| Uint64ToEnum: map[uint64]pb2.Enum{}, |
| StrToNested: map[string]*pb2.Nested{}, |
| StrToOneofs: map[string]*pb2.Oneofs{}, |
| }), |
| want: "\n", |
| }, { |
| desc: "map fields 1", |
| input: M(&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, |
| }, |
| }), |
| want: `int32_to_str: { |
| key: -101 |
| value: "-101" |
| } |
| int32_to_str: { |
| key: 0 |
| value: "zero" |
| } |
| int32_to_str: { |
| key: 255 |
| value: "0xff" |
| } |
| sfixed64_to_bool: { |
| key: 0 |
| value: false |
| } |
| sfixed64_to_bool: { |
| key: 51966 |
| value: true |
| } |
| bool_to_uint32: { |
| key: false |
| value: 101 |
| } |
| bool_to_uint32: { |
| key: true |
| value: 42 |
| } |
| `, |
| }, { |
| desc: "map fields 2", |
| input: M(&pb2.Maps{ |
| Uint64ToEnum: map[uint64]pb2.Enum{ |
| 1: pb2.Enum_FIRST, |
| 2: pb2.Enum_SECOND, |
| 10: pb2.Enum_TENTH, |
| }, |
| }), |
| want: `uint64_to_enum: { |
| key: 1 |
| value: FIRST |
| } |
| uint64_to_enum: { |
| key: 2 |
| value: SECOND |
| } |
| uint64_to_enum: { |
| key: 10 |
| value: TENTH |
| } |
| `, |
| }, { |
| desc: "map fields 3", |
| input: M(&pb2.Maps{ |
| StrToNested: map[string]*pb2.Nested{ |
| "nested_one": &pb2.Nested{ |
| OptString: scalar.String("nested in a map"), |
| }, |
| }, |
| }), |
| want: `str_to_nested: { |
| key: "nested_one" |
| value: { |
| opt_string: "nested in a map" |
| } |
| } |
| `, |
| }, { |
| desc: "map fields 4", |
| input: M(&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"), |
| }, |
| }, |
| }, |
| }, |
| }), |
| want: `str_to_oneofs: { |
| key: "nested" |
| value: { |
| msg: { |
| opt_string: "nested oneof in map field value" |
| } |
| } |
| } |
| str_to_oneofs: { |
| key: "string" |
| value: { |
| str: "hello" |
| } |
| } |
| `, |
| }, { |
| desc: "well-known type fields not set", |
| input: M(&pb2.KnownTypes{}), |
| want: "\n", |
| }, { |
| desc: "well-known type fields set to empty messages", |
| input: M(&pb2.KnownTypes{ |
| OptBool: &wpb.BoolValue{}, |
| OptInt32: &wpb.Int32Value{}, |
| OptInt64: &wpb.Int64Value{}, |
| OptUint32: &wpb.UInt32Value{}, |
| OptUint64: &wpb.UInt64Value{}, |
| OptFloat: &wpb.FloatValue{}, |
| OptDouble: &wpb.DoubleValue{}, |
| OptString: &wpb.StringValue{}, |
| OptBytes: &wpb.BytesValue{}, |
| OptDuration: &durpb.Duration{}, |
| OptTimestamp: &tspb.Timestamp{}, |
| OptStruct: &stpb.Struct{}, |
| OptList: &stpb.ListValue{}, |
| OptValue: &stpb.Value{}, |
| OptEmpty: &emptypb.Empty{}, |
| OptAny: &anypb.Any{}, |
| }), |
| want: `opt_bool: {} |
| opt_int32: {} |
| opt_int64: {} |
| opt_uint32: {} |
| opt_uint64: {} |
| opt_float: {} |
| opt_double: {} |
| opt_string: {} |
| opt_bytes: {} |
| opt_duration: {} |
| opt_timestamp: {} |
| opt_struct: {} |
| opt_list: {} |
| opt_value: {} |
| opt_empty: {} |
| opt_any: {} |
| `, |
| }, { |
| desc: "well-known type scalar fields", |
| input: M(&pb2.KnownTypes{ |
| OptBool: &wpb.BoolValue{ |
| Value: true, |
| }, |
| OptInt32: &wpb.Int32Value{ |
| Value: -42, |
| }, |
| OptInt64: &wpb.Int64Value{ |
| Value: -42, |
| }, |
| OptUint32: &wpb.UInt32Value{ |
| Value: 0xff, |
| }, |
| OptUint64: &wpb.UInt64Value{ |
| Value: 0xffff, |
| }, |
| OptFloat: &wpb.FloatValue{ |
| Value: 1.234, |
| }, |
| OptDouble: &wpb.DoubleValue{ |
| Value: 1.23e308, |
| }, |
| OptString: &wpb.StringValue{ |
| Value: "谷歌", |
| }, |
| OptBytes: &wpb.BytesValue{ |
| Value: []byte("\xe8\xb0\xb7\xe6\xad\x8c"), |
| }, |
| }), |
| want: `opt_bool: { |
| value: true |
| } |
| opt_int32: { |
| value: -42 |
| } |
| opt_int64: { |
| value: -42 |
| } |
| opt_uint32: { |
| value: 255 |
| } |
| opt_uint64: { |
| value: 65535 |
| } |
| opt_float: { |
| value: 1.2339999675750732 |
| } |
| opt_double: { |
| value: 1.23e+308 |
| } |
| opt_string: { |
| value: "谷歌" |
| } |
| opt_bytes: { |
| value: "谷歌" |
| } |
| `, |
| }, { |
| desc: "well-known type time-related fields", |
| input: M(&pb2.KnownTypes{ |
| OptDuration: &durpb.Duration{ |
| Seconds: -3600, |
| Nanos: -123, |
| }, |
| OptTimestamp: &tspb.Timestamp{ |
| Seconds: 1257894000, |
| Nanos: 123, |
| }, |
| }), |
| want: `opt_duration: { |
| seconds: -3600 |
| nanos: -123 |
| } |
| opt_timestamp: { |
| seconds: 1257894000 |
| nanos: 123 |
| } |
| `, |
| }, { |
| desc: "well-known type struct field and different Value types", |
| input: M(&pb2.KnownTypes{ |
| OptStruct: &stpb.Struct{ |
| Fields: map[string]*stpb.Value{ |
| "bool": &stpb.Value{ |
| Kind: &stpb.Value_BoolValue{ |
| BoolValue: true, |
| }, |
| }, |
| "double": &stpb.Value{ |
| Kind: &stpb.Value_NumberValue{ |
| NumberValue: 3.1415, |
| }, |
| }, |
| "null": &stpb.Value{ |
| Kind: &stpb.Value_NullValue{ |
| NullValue: stpb.NullValue_NULL_VALUE, |
| }, |
| }, |
| "string": &stpb.Value{ |
| Kind: &stpb.Value_StringValue{ |
| StringValue: "string", |
| }, |
| }, |
| "struct": &stpb.Value{ |
| Kind: &stpb.Value_StructValue{ |
| StructValue: &stpb.Struct{ |
| Fields: map[string]*stpb.Value{ |
| "bool": &stpb.Value{ |
| Kind: &stpb.Value_BoolValue{ |
| BoolValue: false, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| "list": &stpb.Value{ |
| Kind: &stpb.Value_ListValue{ |
| ListValue: &stpb.ListValue{ |
| Values: []*stpb.Value{ |
| { |
| Kind: &stpb.Value_BoolValue{ |
| BoolValue: false, |
| }, |
| }, |
| { |
| Kind: &stpb.Value_StringValue{ |
| StringValue: "hello", |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }), |
| want: `opt_struct: { |
| fields: { |
| key: "bool" |
| value: { |
| bool_value: true |
| } |
| } |
| fields: { |
| key: "double" |
| value: { |
| number_value: 3.1415 |
| } |
| } |
| fields: { |
| key: "list" |
| value: { |
| list_value: { |
| values: { |
| bool_value: false |
| } |
| values: { |
| string_value: "hello" |
| } |
| } |
| } |
| } |
| fields: { |
| key: "null" |
| value: { |
| null_value: NULL_VALUE |
| } |
| } |
| fields: { |
| key: "string" |
| value: { |
| string_value: "string" |
| } |
| } |
| fields: { |
| key: "struct" |
| value: { |
| struct_value: { |
| fields: { |
| key: "bool" |
| value: { |
| bool_value: false |
| } |
| } |
| } |
| } |
| } |
| } |
| `, |
| }} |
| |
| for _, tt := range tests { |
| tt := tt |
| t.Run(tt.desc, func(t *testing.T) { |
| t.Parallel() |
| want := tt.want |
| b, err := textpb.Marshal(tt.input) |
| if err != nil && !tt.wantErr { |
| t.Errorf("Marshal() returned error: %v\n\n", err) |
| } |
| if tt.wantErr && err == nil { |
| t.Errorf("Marshal() got nil error, want error\n\n") |
| } |
| if got := string(b); got != want { |
| t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, want) |
| if diff := cmp.Diff(want, got, splitLines); diff != "" { |
| t.Errorf("Marshal() diff -want +got\n%v\n", diff) |
| } |
| } |
| }) |
| } |
| } |