1// Copyright 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package prototext_test 6 7import ( 8 "testing" 9 10 "google.golang.org/protobuf/encoding/prototext" 11 "google.golang.org/protobuf/proto" 12 preg "google.golang.org/protobuf/reflect/protoregistry" 13 14 pb2 "google.golang.org/protobuf/internal/testprotos/textpb2" 15 "google.golang.org/protobuf/types/known/anypb" 16 "google.golang.org/protobuf/types/known/durationpb" 17 "google.golang.org/protobuf/types/known/emptypb" 18 "google.golang.org/protobuf/types/known/structpb" 19 "google.golang.org/protobuf/types/known/timestamppb" 20 "google.golang.org/protobuf/types/known/wrapperspb" 21) 22 23func TestRoundTrip(t *testing.T) { 24 tests := []struct { 25 desc string 26 resolver *preg.Types 27 message proto.Message 28 }{{ 29 desc: "well-known type fields set to empty messages", 30 message: &pb2.KnownTypes{ 31 OptBool: &wrapperspb.BoolValue{}, 32 OptInt32: &wrapperspb.Int32Value{}, 33 OptInt64: &wrapperspb.Int64Value{}, 34 OptUint32: &wrapperspb.UInt32Value{}, 35 OptUint64: &wrapperspb.UInt64Value{}, 36 OptFloat: &wrapperspb.FloatValue{}, 37 OptDouble: &wrapperspb.DoubleValue{}, 38 OptString: &wrapperspb.StringValue{}, 39 OptBytes: &wrapperspb.BytesValue{}, 40 OptDuration: &durationpb.Duration{}, 41 OptTimestamp: ×tamppb.Timestamp{}, 42 OptStruct: &structpb.Struct{}, 43 OptList: &structpb.ListValue{}, 44 OptValue: &structpb.Value{}, 45 OptEmpty: &emptypb.Empty{}, 46 OptAny: &anypb.Any{}, 47 }, 48 }, { 49 desc: "well-known type scalar fields", 50 message: &pb2.KnownTypes{ 51 OptBool: &wrapperspb.BoolValue{ 52 Value: true, 53 }, 54 OptInt32: &wrapperspb.Int32Value{ 55 Value: -42, 56 }, 57 OptInt64: &wrapperspb.Int64Value{ 58 Value: -42, 59 }, 60 OptUint32: &wrapperspb.UInt32Value{ 61 Value: 0xff, 62 }, 63 OptUint64: &wrapperspb.UInt64Value{ 64 Value: 0xffff, 65 }, 66 OptFloat: &wrapperspb.FloatValue{ 67 Value: 1.234, 68 }, 69 OptDouble: &wrapperspb.DoubleValue{ 70 Value: 1.23e308, 71 }, 72 OptString: &wrapperspb.StringValue{ 73 Value: "谷歌", 74 }, 75 OptBytes: &wrapperspb.BytesValue{ 76 Value: []byte("\xe8\xb0\xb7\xe6\xad\x8c"), 77 }, 78 }, 79 }, { 80 desc: "well-known type time-related fields", 81 message: &pb2.KnownTypes{ 82 OptDuration: &durationpb.Duration{ 83 Seconds: -3600, 84 Nanos: -123, 85 }, 86 OptTimestamp: ×tamppb.Timestamp{ 87 Seconds: 1257894000, 88 Nanos: 123, 89 }, 90 }, 91 }, { 92 desc: "Struct field and different Value types", 93 message: &pb2.KnownTypes{ 94 OptStruct: &structpb.Struct{ 95 Fields: map[string]*structpb.Value{ 96 "bool": &structpb.Value{ 97 Kind: &structpb.Value_BoolValue{ 98 BoolValue: true, 99 }, 100 }, 101 "double": &structpb.Value{ 102 Kind: &structpb.Value_NumberValue{ 103 NumberValue: 3.1415, 104 }, 105 }, 106 "null": &structpb.Value{ 107 Kind: &structpb.Value_NullValue{ 108 NullValue: structpb.NullValue_NULL_VALUE, 109 }, 110 }, 111 "string": &structpb.Value{ 112 Kind: &structpb.Value_StringValue{ 113 StringValue: "string", 114 }, 115 }, 116 "struct": &structpb.Value{ 117 Kind: &structpb.Value_StructValue{ 118 StructValue: &structpb.Struct{ 119 Fields: map[string]*structpb.Value{ 120 "bool": &structpb.Value{ 121 Kind: &structpb.Value_BoolValue{ 122 BoolValue: false, 123 }, 124 }, 125 }, 126 }, 127 }, 128 }, 129 "list": &structpb.Value{ 130 Kind: &structpb.Value_ListValue{ 131 ListValue: &structpb.ListValue{ 132 Values: []*structpb.Value{ 133 { 134 Kind: &structpb.Value_BoolValue{ 135 BoolValue: false, 136 }, 137 }, 138 { 139 Kind: &structpb.Value_StringValue{ 140 StringValue: "hello", 141 }, 142 }, 143 }, 144 }, 145 }, 146 }, 147 }, 148 }, 149 }, 150 }, { 151 desc: "Any field without registered type", 152 resolver: new(preg.Types), 153 message: func() proto.Message { 154 m := &pb2.Nested{ 155 OptString: proto.String("embedded inside Any"), 156 OptNested: &pb2.Nested{ 157 OptString: proto.String("inception"), 158 }, 159 } 160 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 161 if err != nil { 162 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 163 } 164 return &pb2.KnownTypes{ 165 OptAny: &anypb.Any{ 166 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()), 167 Value: b, 168 }, 169 } 170 }(), 171 }, { 172 desc: "Any field with registered type", 173 message: func() *pb2.KnownTypes { 174 m := &pb2.Nested{ 175 OptString: proto.String("embedded inside Any"), 176 OptNested: &pb2.Nested{ 177 OptString: proto.String("inception"), 178 }, 179 } 180 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m) 181 if err != nil { 182 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 183 } 184 return &pb2.KnownTypes{ 185 OptAny: &anypb.Any{ 186 TypeUrl: string(m.ProtoReflect().Descriptor().FullName()), 187 Value: b, 188 }, 189 } 190 }(), 191 }, { 192 desc: "Any field containing Any message", 193 message: func() *pb2.KnownTypes { 194 m1 := &pb2.Nested{ 195 OptString: proto.String("message inside Any of another Any field"), 196 } 197 b1, err := proto.MarshalOptions{Deterministic: true}.Marshal(m1) 198 if err != nil { 199 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 200 } 201 m2 := &anypb.Any{ 202 TypeUrl: "pb2.Nested", 203 Value: b1, 204 } 205 b2, err := proto.MarshalOptions{Deterministic: true}.Marshal(m2) 206 if err != nil { 207 t.Fatalf("error in binary marshaling message for Any.value: %v", err) 208 } 209 return &pb2.KnownTypes{ 210 OptAny: &anypb.Any{ 211 TypeUrl: "google.protobuf.Any", 212 Value: b2, 213 }, 214 } 215 }(), 216 }} 217 218 for _, tt := range tests { 219 tt := tt 220 t.Run(tt.desc, func(t *testing.T) { 221 t.Parallel() 222 b, err := prototext.MarshalOptions{Resolver: tt.resolver}.Marshal(tt.message) 223 if err != nil { 224 t.Errorf("Marshal() returned error: %v\n\n", err) 225 } 226 227 gotMessage := new(pb2.KnownTypes) 228 err = prototext.UnmarshalOptions{Resolver: tt.resolver}.Unmarshal(b, gotMessage) 229 if err != nil { 230 t.Errorf("Unmarshal() returned error: %v\n\n", err) 231 } 232 233 if !proto.Equal(gotMessage, tt.message) { 234 t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", gotMessage, tt.message) 235 } 236 }) 237 } 238} 239