1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package protopack 6 7import ( 8 "bytes" 9 "encoding/hex" 10 "fmt" 11 "math" 12 "testing" 13 14 "github.com/google/go-cmp/cmp" 15 16 "google.golang.org/protobuf/encoding/prototext" 17 pdesc "google.golang.org/protobuf/reflect/protodesc" 18 "google.golang.org/protobuf/reflect/protoreflect" 19 pref "google.golang.org/protobuf/reflect/protoreflect" 20 21 "google.golang.org/protobuf/types/descriptorpb" 22) 23 24var msgDesc = func() pref.MessageDescriptor { 25 const s = ` 26 name: "test.proto" 27 syntax: "proto2" 28 message_type: [{ 29 name: "Message" 30 field: [ 31 {name:"f1" number:1 label:LABEL_REPEATED type:TYPE_BOOL options:{packed:true}}, 32 {name:"f2" number:2 label:LABEL_REPEATED type:TYPE_INT64 options:{packed:true}}, 33 {name:"f3" number:3 label:LABEL_REPEATED type:TYPE_SINT64 options:{packed:true}}, 34 {name:"f4" number:4 label:LABEL_REPEATED type:TYPE_UINT64 options:{packed:true}}, 35 {name:"f5" number:5 label:LABEL_REPEATED type:TYPE_FIXED32 options:{packed:true}}, 36 {name:"f6" number:6 label:LABEL_REPEATED type:TYPE_SFIXED32 options:{packed:true}}, 37 {name:"f7" number:7 label:LABEL_REPEATED type:TYPE_FLOAT options:{packed:true}}, 38 {name:"f8" number:8 label:LABEL_REPEATED type:TYPE_FIXED64 options:{packed:true}}, 39 {name:"f9" number:9 label:LABEL_REPEATED type:TYPE_SFIXED64 options:{packed:true}}, 40 {name:"f10" number:10 label:LABEL_REPEATED type:TYPE_DOUBLE options:{packed:true}}, 41 {name:"f11" number:11 label:LABEL_OPTIONAL type:TYPE_STRING}, 42 {name:"f12" number:12 label:LABEL_OPTIONAL type:TYPE_BYTES}, 43 {name:"f13" number:13 label:LABEL_OPTIONAL type:TYPE_MESSAGE type_name:".Message"}, 44 {name:"f14" number:14 label:LABEL_OPTIONAL type:TYPE_GROUP type_name:".Message.F14"} 45 ] 46 nested_type: [{name: "F14"}] 47 }] 48 ` 49 pb := new(descriptorpb.FileDescriptorProto) 50 if err := prototext.Unmarshal([]byte(s), pb); err != nil { 51 panic(err) 52 } 53 fd, err := pdesc.NewFile(pb, nil) 54 if err != nil { 55 panic(err) 56 } 57 return fd.Messages().Get(0) 58}() 59 60// dhex decodes a hex-string and returns the bytes and panics if s is invalid. 61func dhex(s string) []byte { 62 b, err := hex.DecodeString(s) 63 if err != nil { 64 panic(err) 65 } 66 return b 67} 68 69func TestPack(t *testing.T) { 70 tests := []struct { 71 raw []byte 72 msg Message 73 msgDesc protoreflect.MessageDescriptor 74 inferMsg bool 75 76 wantOutCompact string 77 wantOutMulti string 78 wantOutSource string 79 }{{ 80 raw: dhex("080088808080800002088280808080000a09010002828080808000"), 81 msg: Message{ 82 Tag{1, VarintType}, Bool(false), 83 Denormalized{5, Tag{1, VarintType}}, Uvarint(2), 84 Tag{1, VarintType}, Denormalized{5, Uvarint(2)}, 85 Tag{1, BytesType}, LengthPrefix{Bool(true), Bool(false), Uvarint(2), Denormalized{5, Uvarint(2)}}, 86 }, 87 msgDesc: msgDesc, 88 wantOutSource: `protopack.Message{ 89 protopack.Tag{1, protopack.VarintType}, protopack.Bool(false), 90 protopack.Denormalized{+5, protopack.Tag{1, protopack.VarintType}}, protopack.Uvarint(2), 91 protopack.Tag{1, protopack.VarintType}, protopack.Denormalized{+5, protopack.Uvarint(2)}, 92 protopack.Tag{1, protopack.BytesType}, protopack.LengthPrefix{protopack.Bool(true), protopack.Bool(false), protopack.Uvarint(2), protopack.Denormalized{+5, protopack.Uvarint(2)}}, 93}`, 94 }, { 95 raw: dhex("080088808080800002088280808080000a09010002828080808000"), 96 msg: Message{ 97 Tag{1, VarintType}, Uvarint(0), 98 Denormalized{5, Tag{1, VarintType}}, Uvarint(2), 99 Tag{1, VarintType}, Denormalized{5, Uvarint(2)}, 100 Tag{1, BytesType}, Bytes(Message{Bool(true), Bool(false), Uvarint(2), Denormalized{5, Uvarint(2)}}.Marshal()), 101 }, 102 inferMsg: true, 103 }, { 104 raw: dhex("100010828080808000121980808080808080808001ffffffffffffffff7f828080808000"), 105 msg: Message{ 106 Tag{2, VarintType}, Varint(0), 107 Tag{2, VarintType}, Denormalized{5, Varint(2)}, 108 Tag{2, BytesType}, LengthPrefix{Varint(math.MinInt64), Varint(math.MaxInt64), Denormalized{5, Varint(2)}}, 109 }, 110 msgDesc: msgDesc, 111 wantOutCompact: `Message{Tag{2, Varint}, Varint(0), Tag{2, Varint}, Denormalized{+5, Varint(2)}, Tag{2, Bytes}, LengthPrefix{Varint(-9223372036854775808), Varint(9223372036854775807), Denormalized{+5, Varint(2)}}}`, 112 }, { 113 raw: dhex("1801188180808080001a1affffffffffffffffff01feffffffffffffffff01818080808000"), 114 msg: Message{ 115 Tag{3, VarintType}, Svarint(-1), 116 Tag{3, VarintType}, Denormalized{5, Svarint(-1)}, 117 Tag{3, BytesType}, LengthPrefix{Svarint(math.MinInt64), Svarint(math.MaxInt64), Denormalized{5, Svarint(-1)}}, 118 }, 119 msgDesc: msgDesc, 120 wantOutMulti: `Message{ 121 Tag{3, Varint}, Svarint(-1), 122 Tag{3, Varint}, Denormalized{+5, Svarint(-1)}, 123 Tag{3, Bytes}, LengthPrefix{Svarint(-9223372036854775808), Svarint(9223372036854775807), Denormalized{+5, Svarint(-1)}}, 124}`, 125 }, { 126 raw: dhex("200120818080808000221100ffffffffffffffffff01818080808000"), 127 msg: Message{ 128 Tag{4, VarintType}, Uvarint(+1), 129 Tag{4, VarintType}, Denormalized{5, Uvarint(+1)}, 130 Tag{4, BytesType}, LengthPrefix{Uvarint(0), Uvarint(math.MaxUint64), Denormalized{5, Uvarint(+1)}}, 131 }, 132 msgDesc: msgDesc, 133 wantOutSource: `protopack.Message{ 134 protopack.Tag{4, protopack.VarintType}, protopack.Uvarint(1), 135 protopack.Tag{4, protopack.VarintType}, protopack.Denormalized{+5, protopack.Uvarint(1)}, 136 protopack.Tag{4, protopack.BytesType}, protopack.LengthPrefix{protopack.Uvarint(0), protopack.Uvarint(18446744073709551615), protopack.Denormalized{+5, protopack.Uvarint(1)}}, 137}`, 138 }, { 139 raw: dhex("2d010000002a0800000000ffffffff"), 140 msg: Message{ 141 Tag{5, Fixed32Type}, Uint32(+1), 142 Tag{5, BytesType}, LengthPrefix{Uint32(0), Uint32(math.MaxUint32)}, 143 }, 144 msgDesc: msgDesc, 145 wantOutCompact: `Message{Tag{5, Fixed32}, Uint32(1), Tag{5, Bytes}, LengthPrefix{Uint32(0), Uint32(4294967295)}}`, 146 }, { 147 raw: dhex("35ffffffff320800000080ffffff7f"), 148 msg: Message{ 149 Tag{6, Fixed32Type}, Int32(-1), 150 Tag{6, BytesType}, LengthPrefix{Int32(math.MinInt32), Int32(math.MaxInt32)}, 151 }, 152 msgDesc: msgDesc, 153 wantOutMulti: `Message{ 154 Tag{6, Fixed32}, Int32(-1), 155 Tag{6, Bytes}, LengthPrefix{Int32(-2147483648), Int32(2147483647)}, 156}`, 157 }, { 158 raw: dhex("3ddb0f49403a1001000000ffff7f7f0000807f000080ff"), 159 msg: Message{ 160 Tag{7, Fixed32Type}, Float32(math.Pi), 161 Tag{7, BytesType}, LengthPrefix{Float32(math.SmallestNonzeroFloat32), Float32(math.MaxFloat32), Float32(math.Inf(+1)), Float32(math.Inf(-1))}, 162 }, 163 msgDesc: msgDesc, 164 wantOutSource: `protopack.Message{ 165 protopack.Tag{7, protopack.Fixed32Type}, protopack.Float32(3.1415927), 166 protopack.Tag{7, protopack.BytesType}, protopack.LengthPrefix{protopack.Float32(1e-45), protopack.Float32(3.4028235e+38), protopack.Float32(math.Inf(+1)), protopack.Float32(math.Inf(-1))}, 167}`, 168 }, { 169 raw: dhex("41010000000000000042100000000000000000ffffffffffffffff"), 170 msg: Message{ 171 Tag{8, Fixed64Type}, Uint64(+1), 172 Tag{8, BytesType}, LengthPrefix{Uint64(0), Uint64(math.MaxUint64)}, 173 }, 174 msgDesc: msgDesc, 175 wantOutCompact: `Message{Tag{8, Fixed64}, Uint64(1), Tag{8, Bytes}, LengthPrefix{Uint64(0), Uint64(18446744073709551615)}}`, 176 }, { 177 raw: dhex("49ffffffffffffffff4a100000000000000080ffffffffffffff7f"), 178 msg: Message{ 179 Tag{9, Fixed64Type}, Int64(-1), 180 Tag{9, BytesType}, LengthPrefix{Int64(math.MinInt64), Int64(math.MaxInt64)}, 181 }, 182 msgDesc: msgDesc, 183 wantOutMulti: `Message{ 184 Tag{9, Fixed64}, Int64(-1), 185 Tag{9, Bytes}, LengthPrefix{Int64(-9223372036854775808), Int64(9223372036854775807)}, 186}`, 187 }, { 188 raw: dhex("51182d4454fb21094052200100000000000000ffffffffffffef7f000000000000f07f000000000000f0ff"), 189 msg: Message{ 190 Tag{10, Fixed64Type}, Float64(math.Pi), 191 Tag{10, BytesType}, LengthPrefix{Float64(math.SmallestNonzeroFloat64), Float64(math.MaxFloat64), Float64(math.Inf(+1)), Float64(math.Inf(-1))}, 192 }, 193 msgDesc: msgDesc, 194 wantOutMulti: `Message{ 195 Tag{10, Fixed64}, Float64(3.141592653589793), 196 Tag{10, Bytes}, LengthPrefix{Float64(5e-324), Float64(1.7976931348623157e+308), Float64(+Inf), Float64(-Inf)}, 197}`, 198 }, { 199 raw: dhex("5a06737472696e675a868080808000737472696e67"), 200 msg: Message{ 201 Tag{11, BytesType}, String("string"), 202 Tag{11, BytesType}, Denormalized{+5, String("string")}, 203 }, 204 msgDesc: msgDesc, 205 wantOutCompact: `Message{Tag{11, Bytes}, String("string"), Tag{11, Bytes}, Denormalized{+5, String("string")}}`, 206 }, { 207 raw: dhex("62056279746573628580808080006279746573"), 208 msg: Message{ 209 Tag{12, BytesType}, Bytes("bytes"), 210 Tag{12, BytesType}, Denormalized{+5, Bytes("bytes")}, 211 }, 212 msgDesc: msgDesc, 213 wantOutMulti: `Message{ 214 Tag{12, Bytes}, Bytes("bytes"), 215 Tag{12, Bytes}, Denormalized{+5, Bytes("bytes")}, 216}`, 217 }, { 218 raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"), 219 msg: Message{ 220 Tag{13, BytesType}, LengthPrefix(Message{ 221 Tag{100, VarintType}, Uvarint(math.MaxUint64), 222 Tag{100, Fixed32Type}, Uint32(math.MaxUint32), 223 Tag{100, Fixed64Type}, Uint64(math.MaxUint64), 224 Tag{100, BytesType}, Bytes("bytes"), 225 Tag{100, StartGroupType}, Tag{100, EndGroupType}, 226 }), 227 }, 228 msgDesc: msgDesc, 229 wantOutSource: `protopack.Message{ 230 protopack.Tag{13, protopack.BytesType}, protopack.LengthPrefix(protopack.Message{ 231 protopack.Tag{100, protopack.VarintType}, protopack.Uvarint(18446744073709551615), 232 protopack.Tag{100, protopack.Fixed32Type}, protopack.Uint32(4294967295), 233 protopack.Tag{100, protopack.Fixed64Type}, protopack.Uint64(18446744073709551615), 234 protopack.Tag{100, protopack.BytesType}, protopack.Bytes("bytes"), 235 protopack.Tag{100, protopack.StartGroupType}, 236 protopack.Tag{100, protopack.EndGroupType}, 237 }), 238}`, 239 }, { 240 raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"), 241 msg: Message{ 242 Tag{13, BytesType}, LengthPrefix(Message{ 243 Tag{100, VarintType}, Uvarint(math.MaxUint64), 244 Tag{100, Fixed32Type}, Uint32(math.MaxUint32), 245 Tag{100, Fixed64Type}, Uint64(math.MaxUint64), 246 Tag{100, BytesType}, Bytes("bytes"), 247 Tag{100, StartGroupType}, Tag{100, EndGroupType}, 248 }), 249 }, 250 inferMsg: true, 251 }, { 252 raw: dhex("6a28a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306ac06"), 253 msg: Message{ 254 Tag{13, BytesType}, Bytes(Message{ 255 Tag{100, VarintType}, Uvarint(math.MaxUint64), 256 Tag{100, Fixed32Type}, Uint32(math.MaxUint32), 257 Tag{100, Fixed64Type}, Uint64(math.MaxUint64), 258 Tag{100, BytesType}, Bytes("bytes"), 259 Tag{100, StartGroupType}, Tag{101, EndGroupType}, 260 }.Marshal()), 261 }, 262 inferMsg: true, 263 }, { 264 raw: dhex("6aa88080808000a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a406"), 265 msg: Message{ 266 Tag{13, BytesType}, Denormalized{5, LengthPrefix(Message{ 267 Tag{100, VarintType}, Uvarint(math.MaxUint64), 268 Tag{100, Fixed32Type}, Uint32(math.MaxUint32), 269 Tag{100, Fixed64Type}, Uint64(math.MaxUint64), 270 Tag{100, BytesType}, Bytes("bytes"), 271 Tag{100, StartGroupType}, Tag{100, EndGroupType}, 272 })}, 273 }, 274 msgDesc: msgDesc, 275 wantOutCompact: `Message{Tag{13, Bytes}, Denormalized{+5, LengthPrefix(Message{Tag{100, Varint}, Uvarint(18446744073709551615), Tag{100, Fixed32}, Uint32(4294967295), Tag{100, Fixed64}, Uint64(18446744073709551615), Tag{100, Bytes}, Bytes("bytes"), Tag{100, StartGroup}, Tag{100, EndGroup}})}}`, 276 }, { 277 raw: dhex("73a006ffffffffffffffffff01a506ffffffffa106ffffffffffffffffa206056279746573a306a40674"), 278 msg: Message{ 279 Tag{14, StartGroupType}, Message{ 280 Tag{100, VarintType}, Uvarint(math.MaxUint64), 281 Tag{100, Fixed32Type}, Uint32(math.MaxUint32), 282 Tag{100, Fixed64Type}, Uint64(math.MaxUint64), 283 Tag{100, BytesType}, Bytes("bytes"), 284 Tag{100, StartGroupType}, Tag{100, EndGroupType}, 285 }, 286 Tag{14, EndGroupType}, 287 }, 288 msgDesc: msgDesc, 289 wantOutMulti: `Message{ 290 Tag{14, StartGroup}, 291 Message{ 292 Tag{100, Varint}, Uvarint(18446744073709551615), 293 Tag{100, Fixed32}, Uint32(4294967295), 294 Tag{100, Fixed64}, Uint64(18446744073709551615), 295 Tag{100, Bytes}, Bytes("bytes"), 296 Tag{100, StartGroup}, 297 Tag{100, EndGroup}, 298 }, 299 Tag{14, EndGroup}, 300}`, 301 }, { 302 raw: dhex("d0faa972cd02a5f09051c2d8aa0d6a26a89c311eddef024b423c0f6f47b64227a1600db56e3f73d4113096c9a88e2b99f2d847516853d76a1a6e9811c85a2ab3"), 303 msg: Message{ 304 Tag{29970346, VarintType}, Uvarint(333), 305 Tag{21268228, Fixed32Type}, Uint32(229300418), 306 Tag{13, BytesType}, LengthPrefix(Message{ 307 Tag{100805, VarintType}, Uvarint(30), 308 Tag{5883, Fixed32Type}, Uint32(255607371), 309 Tag{13, Type(7)}, 310 Raw("G\xb6B'\xa1`\r\xb5n?s\xd4\x110\x96ɨ\x8e+\x99\xf2\xd8GQhS"), 311 }), 312 Tag{1706, Type(7)}, 313 Raw("\x1an\x98\x11\xc8Z*\xb3"), 314 }, 315 msgDesc: msgDesc, 316 }, { 317 raw: dhex("3d08d0e57f"), 318 msg: Message{ 319 Tag{7, Fixed32Type}, Float32(math.Float32frombits( 320 // TODO: Remove workaround for compiler bug (see https://golang.org/issues/27193). 321 func() uint32 { return 0x7fe5d008 }(), 322 )), 323 }, 324 msgDesc: msgDesc, 325 wantOutSource: `protopack.Message{ 326 protopack.Tag{7, protopack.Fixed32Type}, protopack.Float32(math.Float32frombits(0x7fe5d008)), 327}`, 328 }, { 329 raw: dhex("51a8d65110771bf97f"), 330 msg: Message{ 331 Tag{10, Fixed64Type}, Float64(math.Float64frombits(0x7ff91b771051d6a8)), 332 }, 333 msgDesc: msgDesc, 334 wantOutSource: `protopack.Message{ 335 protopack.Tag{10, protopack.Fixed64Type}, protopack.Float64(math.Float64frombits(0x7ff91b771051d6a8)), 336}`, 337 }, { 338 raw: dhex("ab2c14481ab3e9a76d937fb4dd5e6c616ef311f62b7fe888785fca5609ffe81c1064e50dd7a9edb408d317e2891c0d54c719446938d41ab0ccf8e61dc28b0ebb"), 339 msg: Message{ 340 Tag{709, StartGroupType}, 341 Tag{2, EndGroupType}, 342 Tag{9, VarintType}, Uvarint(26), 343 Tag{28655254, StartGroupType}, 344 Message{ 345 Tag{2034, StartGroupType}, 346 Tag{194006, EndGroupType}, 347 }, 348 Tag{13, EndGroupType}, 349 Tag{12, Fixed64Type}, Uint64(9865274810543764334), 350 Tag{15, VarintType}, Uvarint(95), 351 Tag{1385, BytesType}, Bytes("\xff\xe8\x1c\x10d\xe5\rש"), 352 Tag{17229, Fixed32Type}, Uint32(2313295827), 353 Tag{3, EndGroupType}, 354 Tag{1, Fixed32Type}, Uint32(1142540116), 355 Tag{13, Fixed64Type}, Uint64(2154683029754926136), 356 Tag{28856, BytesType}, 357 Raw("\xbb"), 358 }, 359 msgDesc: msgDesc, 360 }, { 361 raw: dhex("29baa4ac1c1e0a20183393bac434b8d3559337ec940050038770eaa9937f98e4"), 362 msg: Message{ 363 Tag{5, Fixed64Type}, Uint64(1738400580611384506), 364 Tag{6, StartGroupType}, 365 Message{ 366 Tag{13771682, StartGroupType}, 367 Message{ 368 Tag{175415, VarintType}, Uvarint(7059), 369 }, 370 Denormalized{+1, Tag{333, EndGroupType}}, 371 Tag{10, VarintType}, Uvarint(3), 372 Tag{1792, Type(7)}, 373 Raw("꩓\u007f\x98\xe4"), 374 }, 375 }, 376 msgDesc: msgDesc, 377 }} 378 379 equateFloatBits := cmp.Options{ 380 cmp.Comparer(func(x, y Float32) bool { 381 return math.Float32bits(float32(x)) == math.Float32bits(float32(y)) 382 }), 383 cmp.Comparer(func(x, y Float64) bool { 384 return math.Float64bits(float64(x)) == math.Float64bits(float64(y)) 385 }), 386 } 387 for _, tt := range tests { 388 t.Run("", func(t *testing.T) { 389 var msg Message 390 raw := tt.msg.Marshal() 391 msg.unmarshal(tt.raw, tt.msgDesc, tt.inferMsg) 392 393 if !bytes.Equal(raw, tt.raw) { 394 t.Errorf("Marshal() mismatch:\ngot %x\nwant %x", raw, tt.raw) 395 } 396 if diff := cmp.Diff(tt.msg, msg, equateFloatBits); diff != "" { 397 t.Errorf("Unmarshal() mismatch (-want +got):\n%s", diff) 398 } 399 if got, want := tt.msg.Size(), len(tt.raw); got != want { 400 t.Errorf("Size() = %v, want %v", got, want) 401 } 402 if tt.wantOutCompact != "" { 403 gotOut := fmt.Sprintf("%v", tt.msg) 404 if string(gotOut) != tt.wantOutCompact { 405 t.Errorf("fmt.Sprintf(%q, msg):\ngot: %s\nwant: %s", "%v", gotOut, tt.wantOutCompact) 406 } 407 } 408 if tt.wantOutMulti != "" { 409 gotOut := fmt.Sprintf("%+v", tt.msg) 410 if string(gotOut) != tt.wantOutMulti { 411 t.Errorf("fmt.Sprintf(%q, msg):\ngot: %s\nwant: %s", "%+v", gotOut, tt.wantOutMulti) 412 } 413 } 414 if tt.wantOutSource != "" { 415 gotOut := fmt.Sprintf("%#v", tt.msg) 416 if string(gotOut) != tt.wantOutSource { 417 t.Errorf("fmt.Sprintf(%q, msg):\ngot: %s\nwant: %s", "%#v", gotOut, tt.wantOutSource) 418 } 419 } 420 }) 421 } 422} 423