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 protowire 6 7import ( 8 "bytes" 9 "encoding/hex" 10 "io" 11 "math" 12 "strings" 13 "testing" 14) 15 16type ( 17 testOps struct { 18 // appendOps is a sequence of append operations, each appending to 19 // the output of the previous append operation. 20 appendOps []appendOp 21 22 // wantRaw (if not nil) is the bytes that the appendOps should produce. 23 wantRaw []byte 24 25 // consumeOps are a sequence of consume operations, each consuming the 26 // remaining output after the previous consume operation. 27 // The first consume operation starts with the output of appendOps. 28 consumeOps []consumeOp 29 } 30 31 // appendOp represents an Append operation. 32 appendOp = interface{} 33 appendTag struct { 34 inNum Number 35 inType Type 36 } 37 appendVarint struct { 38 inVal uint64 39 } 40 appendFixed32 struct { 41 inVal uint32 42 } 43 appendFixed64 struct { 44 inVal uint64 45 } 46 appendBytes struct { 47 inVal []byte 48 } 49 appendGroup struct { 50 inNum Number 51 inVal []byte 52 } 53 appendRaw []byte 54 55 // consumeOp represents an Consume operation. 56 consumeOp = interface{} 57 consumeField struct { 58 wantNum Number 59 wantType Type 60 wantCnt int 61 wantErr error 62 } 63 consumeFieldValue struct { 64 inNum Number 65 inType Type 66 wantCnt int 67 wantErr error 68 } 69 consumeTag struct { 70 wantNum Number 71 wantType Type 72 wantCnt int 73 wantErr error 74 } 75 consumeVarint struct { 76 wantVal uint64 77 wantCnt int 78 wantErr error 79 } 80 consumeFixed32 struct { 81 wantVal uint32 82 wantCnt int 83 wantErr error 84 } 85 consumeFixed64 struct { 86 wantVal uint64 87 wantCnt int 88 wantErr error 89 } 90 consumeBytes struct { 91 wantVal []byte 92 wantCnt int 93 wantErr error 94 } 95 consumeGroup struct { 96 inNum Number 97 wantVal []byte 98 wantCnt int 99 wantErr error 100 } 101 102 ops []interface{} 103) 104 105// dhex decodes a hex-string and returns the bytes and panics if s is invalid. 106func dhex(s string) []byte { 107 b, err := hex.DecodeString(s) 108 if err != nil { 109 panic(err) 110 } 111 return b 112} 113 114func TestTag(t *testing.T) { 115 runTests(t, []testOps{{ 116 appendOps: ops{appendRaw(dhex(""))}, 117 consumeOps: ops{consumeTag{wantErr: io.ErrUnexpectedEOF}}, 118 }, { 119 appendOps: ops{appendTag{inNum: 0, inType: Fixed32Type}}, 120 wantRaw: dhex("05"), 121 consumeOps: ops{consumeTag{wantErr: errFieldNumber}}, 122 }, { 123 appendOps: ops{appendTag{inNum: 1, inType: Fixed32Type}}, 124 wantRaw: dhex("0d"), 125 consumeOps: ops{consumeTag{wantNum: 1, wantType: Fixed32Type, wantCnt: 1}}, 126 }, { 127 appendOps: ops{appendTag{inNum: FirstReservedNumber, inType: BytesType}}, 128 wantRaw: dhex("c2a309"), 129 consumeOps: ops{consumeTag{wantNum: FirstReservedNumber, wantType: BytesType, wantCnt: 3}}, 130 }, { 131 appendOps: ops{appendTag{inNum: LastReservedNumber, inType: StartGroupType}}, 132 wantRaw: dhex("fbe109"), 133 consumeOps: ops{consumeTag{wantNum: LastReservedNumber, wantType: StartGroupType, wantCnt: 3}}, 134 }, { 135 appendOps: ops{appendTag{inNum: MaxValidNumber, inType: VarintType}}, 136 wantRaw: dhex("f8ffffff0f"), 137 consumeOps: ops{consumeTag{wantNum: MaxValidNumber, wantType: VarintType, wantCnt: 5}}, 138 }, { 139 appendOps: ops{appendVarint{inVal: ((math.MaxInt32+1)<<3 | uint64(VarintType))}}, 140 wantRaw: dhex("8080808040"), 141 consumeOps: ops{consumeTag{wantErr: errFieldNumber}}, 142 }}) 143} 144 145func TestVarint(t *testing.T) { 146 runTests(t, []testOps{{ 147 appendOps: ops{appendRaw(dhex(""))}, 148 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 149 }, { 150 appendOps: ops{appendRaw(dhex("80"))}, 151 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 152 }, { 153 appendOps: ops{appendRaw(dhex("8080"))}, 154 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 155 }, { 156 appendOps: ops{appendRaw(dhex("808080"))}, 157 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 158 }, { 159 appendOps: ops{appendRaw(dhex("80808080"))}, 160 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 161 }, { 162 appendOps: ops{appendRaw(dhex("8080808080"))}, 163 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 164 }, { 165 appendOps: ops{appendRaw(dhex("808080808080"))}, 166 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 167 }, { 168 appendOps: ops{appendRaw(dhex("80808080808080"))}, 169 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 170 }, { 171 appendOps: ops{appendRaw(dhex("8080808080808080"))}, 172 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 173 }, { 174 appendOps: ops{appendRaw(dhex("808080808080808080"))}, 175 consumeOps: ops{consumeVarint{wantErr: io.ErrUnexpectedEOF}}, 176 }, { 177 appendOps: ops{appendRaw(dhex("80808080808080808080"))}, 178 consumeOps: ops{consumeVarint{wantErr: errOverflow}}, 179 }, { 180 // Test varints at various boundaries where the length changes. 181 appendOps: ops{appendVarint{inVal: 0x0}}, 182 wantRaw: dhex("00"), 183 consumeOps: ops{consumeVarint{wantVal: 0, wantCnt: 1}}, 184 }, { 185 appendOps: ops{appendVarint{inVal: 0x1}}, 186 wantRaw: dhex("01"), 187 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 1}}, 188 }, { 189 appendOps: ops{appendVarint{inVal: 0x7f}}, 190 wantRaw: dhex("7f"), 191 consumeOps: ops{consumeVarint{wantVal: 0x7f, wantCnt: 1}}, 192 }, { 193 appendOps: ops{appendVarint{inVal: 0x7f + 1}}, 194 wantRaw: dhex("8001"), 195 consumeOps: ops{consumeVarint{wantVal: 0x7f + 1, wantCnt: 2}}, 196 }, { 197 appendOps: ops{appendVarint{inVal: 0x3fff}}, 198 wantRaw: dhex("ff7f"), 199 consumeOps: ops{consumeVarint{wantVal: 0x3fff, wantCnt: 2}}, 200 }, { 201 appendOps: ops{appendVarint{inVal: 0x3fff + 1}}, 202 wantRaw: dhex("808001"), 203 consumeOps: ops{consumeVarint{wantVal: 0x3fff + 1, wantCnt: 3}}, 204 }, { 205 appendOps: ops{appendVarint{inVal: 0x1fffff}}, 206 wantRaw: dhex("ffff7f"), 207 consumeOps: ops{consumeVarint{wantVal: 0x1fffff, wantCnt: 3}}, 208 }, { 209 appendOps: ops{appendVarint{inVal: 0x1fffff + 1}}, 210 wantRaw: dhex("80808001"), 211 consumeOps: ops{consumeVarint{wantVal: 0x1fffff + 1, wantCnt: 4}}, 212 }, { 213 appendOps: ops{appendVarint{inVal: 0xfffffff}}, 214 wantRaw: dhex("ffffff7f"), 215 consumeOps: ops{consumeVarint{wantVal: 0xfffffff, wantCnt: 4}}, 216 }, { 217 appendOps: ops{appendVarint{inVal: 0xfffffff + 1}}, 218 wantRaw: dhex("8080808001"), 219 consumeOps: ops{consumeVarint{wantVal: 0xfffffff + 1, wantCnt: 5}}, 220 }, { 221 appendOps: ops{appendVarint{inVal: 0x7ffffffff}}, 222 wantRaw: dhex("ffffffff7f"), 223 consumeOps: ops{consumeVarint{wantVal: 0x7ffffffff, wantCnt: 5}}, 224 }, { 225 appendOps: ops{appendVarint{inVal: 0x7ffffffff + 1}}, 226 wantRaw: dhex("808080808001"), 227 consumeOps: ops{consumeVarint{wantVal: 0x7ffffffff + 1, wantCnt: 6}}, 228 }, { 229 appendOps: ops{appendVarint{inVal: 0x3ffffffffff}}, 230 wantRaw: dhex("ffffffffff7f"), 231 consumeOps: ops{consumeVarint{wantVal: 0x3ffffffffff, wantCnt: 6}}, 232 }, { 233 appendOps: ops{appendVarint{inVal: 0x3ffffffffff + 1}}, 234 wantRaw: dhex("80808080808001"), 235 consumeOps: ops{consumeVarint{wantVal: 0x3ffffffffff + 1, wantCnt: 7}}, 236 }, { 237 appendOps: ops{appendVarint{inVal: 0x1ffffffffffff}}, 238 wantRaw: dhex("ffffffffffff7f"), 239 consumeOps: ops{consumeVarint{wantVal: 0x1ffffffffffff, wantCnt: 7}}, 240 }, { 241 appendOps: ops{appendVarint{inVal: 0x1ffffffffffff + 1}}, 242 wantRaw: dhex("8080808080808001"), 243 consumeOps: ops{consumeVarint{wantVal: 0x1ffffffffffff + 1, wantCnt: 8}}, 244 }, { 245 appendOps: ops{appendVarint{inVal: 0xffffffffffffff}}, 246 wantRaw: dhex("ffffffffffffff7f"), 247 consumeOps: ops{consumeVarint{wantVal: 0xffffffffffffff, wantCnt: 8}}, 248 }, { 249 appendOps: ops{appendVarint{inVal: 0xffffffffffffff + 1}}, 250 wantRaw: dhex("808080808080808001"), 251 consumeOps: ops{consumeVarint{wantVal: 0xffffffffffffff + 1, wantCnt: 9}}, 252 }, { 253 appendOps: ops{appendVarint{inVal: 0x7fffffffffffffff}}, 254 wantRaw: dhex("ffffffffffffffff7f"), 255 consumeOps: ops{consumeVarint{wantVal: 0x7fffffffffffffff, wantCnt: 9}}, 256 }, { 257 appendOps: ops{appendVarint{inVal: 0x7fffffffffffffff + 1}}, 258 wantRaw: dhex("80808080808080808001"), 259 consumeOps: ops{consumeVarint{wantVal: 0x7fffffffffffffff + 1, wantCnt: 10}}, 260 }, { 261 appendOps: ops{appendVarint{inVal: math.MaxUint64}}, 262 wantRaw: dhex("ffffffffffffffffff01"), 263 consumeOps: ops{consumeVarint{wantVal: math.MaxUint64, wantCnt: 10}}, 264 }, { 265 appendOps: ops{appendRaw(dhex("ffffffffffffffffff02"))}, 266 consumeOps: ops{consumeVarint{wantErr: errOverflow}}, 267 }, { 268 // Test denormalized varints; where the encoding, while valid, is 269 // larger than necessary. 270 appendOps: ops{appendRaw(dhex("01"))}, 271 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 1}}, 272 }, { 273 appendOps: ops{appendRaw(dhex("8100"))}, 274 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 2}}, 275 }, { 276 appendOps: ops{appendRaw(dhex("818000"))}, 277 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 3}}, 278 }, { 279 appendOps: ops{appendRaw(dhex("81808000"))}, 280 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 4}}, 281 }, { 282 appendOps: ops{appendRaw(dhex("8180808000"))}, 283 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 5}}, 284 }, { 285 appendOps: ops{appendRaw(dhex("818080808000"))}, 286 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 6}}, 287 }, { 288 appendOps: ops{appendRaw(dhex("81808080808000"))}, 289 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 7}}, 290 }, { 291 appendOps: ops{appendRaw(dhex("8180808080808000"))}, 292 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 8}}, 293 }, { 294 appendOps: ops{appendRaw(dhex("818080808080808000"))}, 295 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 9}}, 296 }, { 297 appendOps: ops{appendRaw(dhex("81808080808080808000"))}, 298 consumeOps: ops{consumeVarint{wantVal: 1, wantCnt: 10}}, 299 }, { 300 appendOps: ops{appendRaw(dhex("8180808080808080808000"))}, 301 consumeOps: ops{consumeVarint{wantErr: errOverflow}}, 302 }}) 303} 304 305func TestFixed32(t *testing.T) { 306 runTests(t, []testOps{{ 307 appendOps: ops{appendRaw(dhex(""))}, 308 consumeOps: ops{consumeFixed32{wantErr: io.ErrUnexpectedEOF}}, 309 }, { 310 appendOps: ops{appendRaw(dhex("000000"))}, 311 consumeOps: ops{consumeFixed32{wantErr: io.ErrUnexpectedEOF}}, 312 }, { 313 appendOps: ops{appendFixed32{0}}, 314 wantRaw: dhex("00000000"), 315 consumeOps: ops{consumeFixed32{wantVal: 0, wantCnt: 4}}, 316 }, { 317 appendOps: ops{appendFixed32{math.MaxUint32}}, 318 wantRaw: dhex("ffffffff"), 319 consumeOps: ops{consumeFixed32{wantVal: math.MaxUint32, wantCnt: 4}}, 320 }, { 321 appendOps: ops{appendFixed32{0xf0e1d2c3}}, 322 wantRaw: dhex("c3d2e1f0"), 323 consumeOps: ops{consumeFixed32{wantVal: 0xf0e1d2c3, wantCnt: 4}}, 324 }}) 325} 326 327func TestFixed64(t *testing.T) { 328 runTests(t, []testOps{{ 329 appendOps: ops{appendRaw(dhex(""))}, 330 consumeOps: ops{consumeFixed64{wantErr: io.ErrUnexpectedEOF}}, 331 }, { 332 appendOps: ops{appendRaw(dhex("00000000000000"))}, 333 consumeOps: ops{consumeFixed64{wantErr: io.ErrUnexpectedEOF}}, 334 }, { 335 appendOps: ops{appendFixed64{0}}, 336 wantRaw: dhex("0000000000000000"), 337 consumeOps: ops{consumeFixed64{wantVal: 0, wantCnt: 8}}, 338 }, { 339 appendOps: ops{appendFixed64{math.MaxUint64}}, 340 wantRaw: dhex("ffffffffffffffff"), 341 consumeOps: ops{consumeFixed64{wantVal: math.MaxUint64, wantCnt: 8}}, 342 }, { 343 appendOps: ops{appendFixed64{0xf0e1d2c3b4a59687}}, 344 wantRaw: dhex("8796a5b4c3d2e1f0"), 345 consumeOps: ops{consumeFixed64{wantVal: 0xf0e1d2c3b4a59687, wantCnt: 8}}, 346 }}) 347} 348 349func TestBytes(t *testing.T) { 350 runTests(t, []testOps{{ 351 appendOps: ops{appendRaw(dhex(""))}, 352 consumeOps: ops{consumeBytes{wantErr: io.ErrUnexpectedEOF}}, 353 }, { 354 appendOps: ops{appendRaw(dhex("01"))}, 355 consumeOps: ops{consumeBytes{wantErr: io.ErrUnexpectedEOF}}, 356 }, { 357 appendOps: ops{appendVarint{0}, appendRaw("")}, 358 wantRaw: dhex("00"), 359 consumeOps: ops{consumeBytes{wantVal: dhex(""), wantCnt: 1}}, 360 }, { 361 appendOps: ops{appendBytes{[]byte("hello")}}, 362 wantRaw: []byte("\x05hello"), 363 consumeOps: ops{consumeBytes{wantVal: []byte("hello"), wantCnt: 6}}, 364 }, { 365 appendOps: ops{appendBytes{[]byte(strings.Repeat("hello", 50))}}, 366 wantRaw: []byte("\xfa\x01" + strings.Repeat("hello", 50)), 367 consumeOps: ops{consumeBytes{wantVal: []byte(strings.Repeat("hello", 50)), wantCnt: 252}}, 368 }, { 369 appendOps: ops{appendRaw("\x85\x80\x00hello")}, 370 consumeOps: ops{consumeBytes{wantVal: []byte("hello"), wantCnt: 8}}, 371 }, { 372 appendOps: ops{appendRaw("\x85\x80\x00hell")}, 373 consumeOps: ops{consumeBytes{wantErr: io.ErrUnexpectedEOF}}, 374 }}) 375} 376 377func TestGroup(t *testing.T) { 378 runTests(t, []testOps{{ 379 appendOps: ops{appendRaw(dhex(""))}, 380 consumeOps: ops{consumeGroup{wantErr: io.ErrUnexpectedEOF}}, 381 }, { 382 appendOps: ops{appendTag{inNum: 0, inType: StartGroupType}}, 383 consumeOps: ops{consumeGroup{inNum: 1, wantErr: errFieldNumber}}, 384 }, { 385 appendOps: ops{appendTag{inNum: 2, inType: EndGroupType}}, 386 consumeOps: ops{consumeGroup{inNum: 1, wantErr: errEndGroup}}, 387 }, { 388 appendOps: ops{appendTag{inNum: 1, inType: EndGroupType}}, 389 consumeOps: ops{consumeGroup{inNum: 1, wantCnt: 1}}, 390 }, { 391 appendOps: ops{ 392 appendTag{inNum: 5, inType: Fixed32Type}, 393 appendFixed32{0xf0e1d2c3}, 394 appendTag{inNum: 5, inType: EndGroupType}, 395 }, 396 wantRaw: dhex("2dc3d2e1f02c"), 397 consumeOps: ops{consumeGroup{inNum: 5, wantVal: dhex("2dc3d2e1f0"), wantCnt: 6}}, 398 }, { 399 appendOps: ops{ 400 appendTag{inNum: 5, inType: Fixed32Type}, 401 appendFixed32{0xf0e1d2c3}, 402 appendRaw(dhex("ac808000")), 403 }, 404 consumeOps: ops{consumeGroup{inNum: 5, wantVal: dhex("2dc3d2e1f0"), wantCnt: 9}}, 405 }}) 406} 407 408func TestField(t *testing.T) { 409 runTests(t, []testOps{{ 410 appendOps: ops{appendRaw(dhex(""))}, 411 consumeOps: ops{consumeField{wantErr: io.ErrUnexpectedEOF}}, 412 }, { 413 appendOps: ops{ 414 appendTag{inNum: 5000, inType: StartGroupType}, 415 appendTag{inNum: 1, inType: VarintType}, 416 appendVarint{123456789}, 417 appendTag{inNum: 12, inType: Fixed32Type}, 418 appendFixed32{123456789}, 419 appendTag{inNum: 123, inType: Fixed64Type}, 420 appendFixed64{123456789}, 421 appendTag{inNum: 1234, inType: BytesType}, 422 appendBytes{[]byte("hello")}, 423 appendTag{inNum: 12345, inType: StartGroupType}, 424 appendTag{inNum: 11, inType: VarintType}, 425 appendVarint{123456789}, 426 appendTag{inNum: 1212, inType: Fixed32Type}, 427 appendFixed32{123456789}, 428 appendTag{inNum: 123123, inType: Fixed64Type}, 429 appendFixed64{123456789}, 430 appendTag{inNum: 12341234, inType: BytesType}, 431 appendBytes{[]byte("goodbye")}, 432 appendTag{inNum: 12345, inType: EndGroupType}, 433 appendTag{inNum: 5000, inType: EndGroupType}, 434 }, 435 wantRaw: dhex("c3b80208959aef3a6515cd5b07d90715cd5b0700000000924d0568656c6c6fcb830658959aef3ae54b15cd5b07998f3c15cd5b070000000092ff892f07676f6f64627965cc8306c4b802"), 436 consumeOps: ops{ 437 consumeTag{wantNum: 5000, wantType: StartGroupType, wantCnt: 3}, 438 consumeTag{wantNum: 1, wantType: VarintType, wantCnt: 1}, 439 consumeVarint{wantVal: 123456789, wantCnt: 4}, 440 consumeTag{wantNum: 12, wantType: Fixed32Type, wantCnt: 1}, 441 consumeFixed32{wantVal: 123456789, wantCnt: 4}, 442 consumeTag{wantNum: 123, wantType: Fixed64Type, wantCnt: 2}, 443 consumeFixed64{wantVal: 123456789, wantCnt: 8}, 444 consumeTag{wantNum: 1234, wantType: BytesType, wantCnt: 2}, 445 consumeBytes{wantVal: []byte("hello"), wantCnt: 6}, 446 consumeTag{wantNum: 12345, wantType: StartGroupType, wantCnt: 3}, 447 consumeTag{wantNum: 11, wantType: VarintType, wantCnt: 1}, 448 consumeVarint{wantVal: 123456789, wantCnt: 4}, 449 consumeTag{wantNum: 1212, wantType: Fixed32Type, wantCnt: 2}, 450 consumeFixed32{wantVal: 123456789, wantCnt: 4}, 451 consumeTag{wantNum: 123123, wantType: Fixed64Type, wantCnt: 3}, 452 consumeFixed64{wantVal: 123456789, wantCnt: 8}, 453 consumeTag{wantNum: 12341234, wantType: BytesType, wantCnt: 4}, 454 consumeBytes{wantVal: []byte("goodbye"), wantCnt: 8}, 455 consumeTag{wantNum: 12345, wantType: EndGroupType, wantCnt: 3}, 456 consumeTag{wantNum: 5000, wantType: EndGroupType, wantCnt: 3}, 457 }, 458 }, { 459 appendOps: ops{appendRaw(dhex("c3b80208959aef3a6515cd5b07d90715cd5b0700000000924d0568656c6c6fcb830658959aef3ae54b15cd5b07998f3c15cd5b070000000092ff892f07676f6f64627965cc8306c4b802"))}, 460 consumeOps: ops{consumeField{wantNum: 5000, wantType: StartGroupType, wantCnt: 74}}, 461 }, { 462 appendOps: ops{appendTag{inNum: 5, inType: EndGroupType}}, 463 wantRaw: dhex("2c"), 464 consumeOps: ops{consumeField{wantErr: errEndGroup}}, 465 }, { 466 appendOps: ops{ 467 appendTag{inNum: 1, inType: StartGroupType}, 468 appendTag{inNum: 22, inType: StartGroupType}, 469 appendTag{inNum: 333, inType: StartGroupType}, 470 appendTag{inNum: 4444, inType: StartGroupType}, 471 appendTag{inNum: 4444, inType: EndGroupType}, 472 appendTag{inNum: 333, inType: EndGroupType}, 473 appendTag{inNum: 22, inType: EndGroupType}, 474 appendTag{inNum: 1, inType: EndGroupType}, 475 }, 476 wantRaw: dhex("0bb301eb14e39502e49502ec14b4010c"), 477 consumeOps: ops{consumeField{wantNum: 1, wantType: StartGroupType, wantCnt: 16}}, 478 }, { 479 appendOps: ops{ 480 appendTag{inNum: 1, inType: StartGroupType}, 481 appendGroup{inNum: 1, inVal: dhex("b301eb14e39502e49502ec14b401")}, 482 }, 483 wantRaw: dhex("0b" + "b301eb14e39502e49502ec14b401" + "0c"), 484 consumeOps: ops{consumeField{wantNum: 1, wantType: StartGroupType, wantCnt: 16}}, 485 }, { 486 appendOps: ops{ 487 appendTag{inNum: 1, inType: StartGroupType}, 488 appendTag{inNum: 22, inType: StartGroupType}, 489 appendTag{inNum: 333, inType: StartGroupType}, 490 appendTag{inNum: 4444, inType: StartGroupType}, 491 appendTag{inNum: 333, inType: EndGroupType}, 492 appendTag{inNum: 22, inType: EndGroupType}, 493 appendTag{inNum: 1, inType: EndGroupType}, 494 }, 495 consumeOps: ops{consumeField{wantErr: errEndGroup}}, 496 }, { 497 appendOps: ops{ 498 appendTag{inNum: 1, inType: StartGroupType}, 499 appendTag{inNum: 22, inType: StartGroupType}, 500 appendTag{inNum: 333, inType: StartGroupType}, 501 appendTag{inNum: 4444, inType: StartGroupType}, 502 appendTag{inNum: 4444, inType: EndGroupType}, 503 appendTag{inNum: 333, inType: EndGroupType}, 504 appendTag{inNum: 22, inType: EndGroupType}, 505 }, 506 consumeOps: ops{consumeField{wantErr: io.ErrUnexpectedEOF}}, 507 }, { 508 appendOps: ops{ 509 appendTag{inNum: 1, inType: StartGroupType}, 510 appendTag{inNum: 22, inType: StartGroupType}, 511 appendTag{inNum: 333, inType: StartGroupType}, 512 appendTag{inNum: 4444, inType: StartGroupType}, 513 appendTag{inNum: 0, inType: VarintType}, 514 appendTag{inNum: 4444, inType: EndGroupType}, 515 appendTag{inNum: 333, inType: EndGroupType}, 516 appendTag{inNum: 22, inType: EndGroupType}, 517 appendTag{inNum: 1, inType: EndGroupType}, 518 }, 519 consumeOps: ops{consumeField{wantErr: errFieldNumber}}, 520 }, { 521 appendOps: ops{ 522 appendTag{inNum: 1, inType: StartGroupType}, 523 appendTag{inNum: 22, inType: StartGroupType}, 524 appendTag{inNum: 333, inType: StartGroupType}, 525 appendTag{inNum: 4444, inType: StartGroupType}, 526 appendTag{inNum: 1, inType: 6}, 527 appendTag{inNum: 4444, inType: EndGroupType}, 528 appendTag{inNum: 333, inType: EndGroupType}, 529 appendTag{inNum: 22, inType: EndGroupType}, 530 appendTag{inNum: 1, inType: EndGroupType}, 531 }, 532 consumeOps: ops{consumeField{wantErr: errReserved}}, 533 }}) 534} 535 536func runTests(t *testing.T, tests []testOps) { 537 for _, tt := range tests { 538 t.Run("", func(t *testing.T) { 539 var b []byte 540 for _, op := range tt.appendOps { 541 b0 := b 542 switch op := op.(type) { 543 case appendTag: 544 b = AppendTag(b, op.inNum, op.inType) 545 case appendVarint: 546 b = AppendVarint(b, op.inVal) 547 case appendFixed32: 548 b = AppendFixed32(b, op.inVal) 549 case appendFixed64: 550 b = AppendFixed64(b, op.inVal) 551 case appendBytes: 552 b = AppendBytes(b, op.inVal) 553 case appendGroup: 554 b = AppendGroup(b, op.inNum, op.inVal) 555 case appendRaw: 556 b = append(b, op...) 557 } 558 559 check := func(label string, want int) { 560 t.Helper() 561 if got := len(b) - len(b0); got != want { 562 t.Errorf("len(Append%v) and Size%v mismatch: got %v, want %v", label, label, got, want) 563 } 564 } 565 switch op := op.(type) { 566 case appendTag: 567 check("Tag", SizeTag(op.inNum)) 568 case appendVarint: 569 check("Varint", SizeVarint(op.inVal)) 570 case appendFixed32: 571 check("Fixed32", SizeFixed32()) 572 case appendFixed64: 573 check("Fixed64", SizeFixed64()) 574 case appendBytes: 575 check("Bytes", SizeBytes(len(op.inVal))) 576 case appendGroup: 577 check("Group", SizeGroup(op.inNum, len(op.inVal))) 578 } 579 } 580 581 if tt.wantRaw != nil && !bytes.Equal(b, tt.wantRaw) { 582 t.Errorf("raw output mismatch:\ngot %x\nwant %x", b, tt.wantRaw) 583 } 584 585 for _, op := range tt.consumeOps { 586 check := func(label string, gotCnt, wantCnt int, wantErr error) { 587 t.Helper() 588 gotErr := ParseError(gotCnt) 589 if gotCnt < 0 { 590 gotCnt = 0 591 } 592 if gotCnt != wantCnt { 593 t.Errorf("Consume%v(): consumed %d bytes, want %d bytes consumed", label, gotCnt, wantCnt) 594 } 595 if gotErr != wantErr { 596 t.Errorf("Consume%v(): got %v error, want %v error", label, gotErr, wantErr) 597 } 598 b = b[gotCnt:] 599 } 600 switch op := op.(type) { 601 case consumeField: 602 gotNum, gotType, n := ConsumeField(b) 603 if gotNum != op.wantNum || gotType != op.wantType { 604 t.Errorf("ConsumeField() = (%d, %v), want (%d, %v)", gotNum, gotType, op.wantNum, op.wantType) 605 } 606 check("Field", n, op.wantCnt, op.wantErr) 607 case consumeFieldValue: 608 n := ConsumeFieldValue(op.inNum, op.inType, b) 609 check("FieldValue", n, op.wantCnt, op.wantErr) 610 case consumeTag: 611 gotNum, gotType, n := ConsumeTag(b) 612 if gotNum != op.wantNum || gotType != op.wantType { 613 t.Errorf("ConsumeTag() = (%d, %v), want (%d, %v)", gotNum, gotType, op.wantNum, op.wantType) 614 } 615 check("Tag", n, op.wantCnt, op.wantErr) 616 case consumeVarint: 617 gotVal, n := ConsumeVarint(b) 618 if gotVal != op.wantVal { 619 t.Errorf("ConsumeVarint() = %d, want %d", gotVal, op.wantVal) 620 } 621 check("Varint", n, op.wantCnt, op.wantErr) 622 case consumeFixed32: 623 gotVal, n := ConsumeFixed32(b) 624 if gotVal != op.wantVal { 625 t.Errorf("ConsumeFixed32() = %d, want %d", gotVal, op.wantVal) 626 } 627 check("Fixed32", n, op.wantCnt, op.wantErr) 628 case consumeFixed64: 629 gotVal, n := ConsumeFixed64(b) 630 if gotVal != op.wantVal { 631 t.Errorf("ConsumeFixed64() = %d, want %d", gotVal, op.wantVal) 632 } 633 check("Fixed64", n, op.wantCnt, op.wantErr) 634 case consumeBytes: 635 gotVal, n := ConsumeBytes(b) 636 if !bytes.Equal(gotVal, op.wantVal) { 637 t.Errorf("ConsumeBytes() = %x, want %x", gotVal, op.wantVal) 638 } 639 check("Bytes", n, op.wantCnt, op.wantErr) 640 case consumeGroup: 641 gotVal, n := ConsumeGroup(op.inNum, b) 642 if !bytes.Equal(gotVal, op.wantVal) { 643 t.Errorf("ConsumeGroup() = %x, want %x", gotVal, op.wantVal) 644 } 645 check("Group", n, op.wantCnt, op.wantErr) 646 } 647 } 648 }) 649 } 650} 651 652func TestZigZag(t *testing.T) { 653 tests := []struct { 654 dec int64 655 enc uint64 656 }{ 657 {math.MinInt64 + 0, math.MaxUint64 - 0}, 658 {math.MinInt64 + 1, math.MaxUint64 - 2}, 659 {math.MinInt64 + 2, math.MaxUint64 - 4}, 660 {-3, 5}, 661 {-2, 3}, 662 {-1, 1}, 663 {0, 0}, 664 {+1, 2}, 665 {+2, 4}, 666 {+3, 6}, 667 {math.MaxInt64 - 2, math.MaxUint64 - 5}, 668 {math.MaxInt64 - 1, math.MaxUint64 - 3}, 669 {math.MaxInt64 - 0, math.MaxUint64 - 1}, 670 } 671 672 for _, tt := range tests { 673 if enc := EncodeZigZag(tt.dec); enc != tt.enc { 674 t.Errorf("EncodeZigZag(%d) = %d, want %d", tt.dec, enc, tt.enc) 675 } 676 if dec := DecodeZigZag(tt.enc); dec != tt.dec { 677 t.Errorf("DecodeZigZag(%d) = %d, want %d", tt.enc, dec, tt.dec) 678 } 679 } 680} 681