gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/buffer/buffer_test.go (about) 1 // Copyright 2021 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package buffer 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "math/rand" 23 "slices" 24 "strings" 25 "testing" 26 27 "gvisor.dev/gvisor/pkg/state" 28 "gvisor.dev/gvisor/pkg/tcpip/checksum" 29 ) 30 31 func BenchmarkReadAt(b *testing.B) { 32 b.ReportAllocs() 33 var buf Buffer 34 buf.Append(NewView(100)) 35 defer buf.Release() 36 37 bytes := make([]byte, 10) 38 for i := 0; i < b.N; i++ { 39 buf.ReadAt(bytes, 0) 40 } 41 } 42 43 func BenchmarkWriteRead(b *testing.B) { 44 b.ReportAllocs() 45 var buf Buffer 46 defer buf.Release() 47 sz := 1000 48 rbuf := bytes.NewBuffer(make([]byte, sz)) 49 for i := 0; i < b.N; i++ { 50 buf.Append(NewView(sz)) 51 rbuf.Reset() 52 buf.ReadToWriter(rbuf, int64(sz)) 53 } 54 } 55 56 func fillAppend(b *Buffer, data []byte) { 57 b.Append(NewViewWithData(data)) 58 } 59 60 func fillAppendEnd(b *Buffer, data []byte) { 61 b.GrowTo(baseChunkSize-1, false) 62 b.Append(NewViewWithData(data)) 63 b.TrimFront(baseChunkSize - 1) 64 } 65 66 func fillWriteFromReader(b *Buffer, data []byte) { 67 buf := bytes.NewBuffer(data) 68 b.WriteFromReader(buf, int64(len(data))) 69 } 70 71 func fillWriteFromReaderEnd(b *Buffer, data []byte) { 72 b.GrowTo(baseChunkSize-1, false) 73 buf := bytes.NewBuffer(data) 74 b.WriteFromReader(buf, int64(len(data))) 75 b.TrimFront(baseChunkSize - 1) 76 } 77 78 var fillFuncs = map[string]func(*Buffer, []byte){ 79 "append": fillAppend, 80 "appendEnd": fillAppendEnd, 81 "writeFromReader": fillWriteFromReader, 82 "writeFromReaderEnd": fillWriteFromReaderEnd, 83 } 84 85 func testReadAt(t *testing.T, b *Buffer, offset int64, n int, wantStr string, wantErr error) { 86 t.Helper() 87 d := make([]byte, n) 88 n, err := b.ReadAt(d, offset) 89 if n != len(wantStr) { 90 t.Errorf("got %d, want %d", n, len(wantStr)) 91 } 92 if err != wantErr { 93 t.Errorf("got err %v, want %v", err, wantErr) 94 } 95 if !bytes.Equal(d[:n], []byte(wantStr)) { 96 t.Errorf("got %q, want %q", string(d[:n]), wantStr) 97 } 98 } 99 100 func TestBuffer(t *testing.T) { 101 testCases := []struct { 102 name string 103 input string 104 output string 105 op func(*testing.T, *Buffer) 106 }{ 107 // Preconditions. 108 { 109 name: "truncate-check", 110 input: "hello", 111 output: "hello", // Not touched. 112 op: func(t *testing.T, b *Buffer) { 113 defer func() { 114 if r := recover(); r == nil { 115 t.Errorf("Truncate(-1) did not panic") 116 } 117 }() 118 b.Truncate(-1) 119 }, 120 }, 121 { 122 name: "growto-check", 123 input: "hello", 124 output: "hello", // Not touched. 125 op: func(t *testing.T, b *Buffer) { 126 defer func() { 127 if r := recover(); r == nil { 128 t.Errorf("GrowTo(-1) did not panic") 129 } 130 }() 131 b.GrowTo(-1, false) 132 }, 133 }, 134 { 135 name: "advance-check", 136 input: "hello", 137 output: "", // Consumed. 138 op: func(t *testing.T, b *Buffer) { 139 defer func() { 140 if r := recover(); r == nil { 141 t.Errorf("advanceRead(Size()+1) did not panic") 142 } 143 }() 144 b.advanceRead(b.Size() + 1) 145 }, 146 }, 147 148 // Prepend. 149 { 150 name: "prepend", 151 input: "world", 152 output: "hello world", 153 op: func(t *testing.T, b *Buffer) { 154 b.Prepend(NewViewWithData([]byte("hello "))) 155 }, 156 }, 157 { 158 name: "prepend-backfill-full", 159 input: "hello world", 160 output: "jello world", 161 op: func(t *testing.T, b *Buffer) { 162 b.TrimFront(1) 163 b.Prepend(NewViewWithData([]byte("j"))) 164 }, 165 }, 166 { 167 name: "prepend-backfill-under", 168 input: "hello world", 169 output: "hola world", 170 op: func(t *testing.T, b *Buffer) { 171 b.TrimFront(5) 172 b.Prepend(NewViewWithData([]byte("hola"))) 173 }, 174 }, 175 { 176 name: "prepend-backfill-over", 177 input: "hello world", 178 output: "smello world", 179 op: func(t *testing.T, b *Buffer) { 180 b.TrimFront(1) 181 b.Prepend(NewViewWithData([]byte("sm"))) 182 }, 183 }, 184 { 185 name: "prepend-fill", 186 input: strings.Repeat("1", baseChunkSize-1), 187 output: "0" + strings.Repeat("1", baseChunkSize-1), 188 op: func(t *testing.T, b *Buffer) { 189 b.Prepend(NewViewWithData([]byte("0"))) 190 }, 191 }, 192 { 193 name: "prepend-overflow", 194 input: strings.Repeat("1", baseChunkSize), 195 output: "0" + strings.Repeat("1", baseChunkSize), 196 op: func(t *testing.T, b *Buffer) { 197 b.Prepend(NewViewWithData([]byte("0"))) 198 }, 199 }, 200 { 201 name: "prepend-multiple-buffers", 202 input: strings.Repeat("1", baseChunkSize-1), 203 output: strings.Repeat("0", baseChunkSize*3) + strings.Repeat("1", baseChunkSize-1), 204 op: func(t *testing.T, b *Buffer) { 205 b.Prepend(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize)))) 206 b.Prepend(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize)))) 207 b.Prepend(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize)))) 208 }, 209 }, 210 211 // Append and write. 212 { 213 name: "append", 214 input: "hello", 215 output: "hello world", 216 op: func(t *testing.T, b *Buffer) { 217 b.Append(NewViewWithData([]byte(" world"))) 218 }, 219 }, 220 { 221 name: "append-fill", 222 input: strings.Repeat("1", baseChunkSize-1), 223 output: strings.Repeat("1", baseChunkSize-1) + "0", 224 op: func(t *testing.T, b *Buffer) { 225 b.Append(NewViewWithData([]byte("0"))) 226 }, 227 }, 228 { 229 name: "append-overflow", 230 input: strings.Repeat("1", baseChunkSize), 231 output: strings.Repeat("1", baseChunkSize) + "0", 232 op: func(t *testing.T, b *Buffer) { 233 b.Append(NewViewWithData([]byte("0"))) 234 }, 235 }, 236 { 237 name: "append-multiple-views", 238 input: strings.Repeat("1", baseChunkSize-1), 239 output: strings.Repeat("1", baseChunkSize-1) + strings.Repeat("0", baseChunkSize*3), 240 op: func(t *testing.T, b *Buffer) { 241 b.Append(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize)))) 242 b.Append(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize)))) 243 b.Append(NewViewWithData([]byte(strings.Repeat("0", baseChunkSize)))) 244 }, 245 }, 246 247 // AppendOwned. 248 { 249 name: "append-owned", 250 input: "hello", 251 output: "hello world", 252 op: func(t *testing.T, b *Buffer) { 253 v := NewViewWithData([]byte("Xworld")) 254 // Appending to a buffer that has extra references means this will 255 // degrade into an "appendOwned" for the chunk being added. 256 c := b.Clone() 257 defer c.Release() 258 b.Append(v) 259 v.chunk.data[0] = ' ' 260 }, 261 }, 262 263 // Truncate. 264 { 265 name: "truncate", 266 input: "hello world", 267 output: "hello", 268 op: func(t *testing.T, b *Buffer) { 269 b.Truncate(5) 270 }, 271 }, 272 { 273 name: "truncate-noop", 274 input: "hello world", 275 output: "hello world", 276 op: func(t *testing.T, b *Buffer) { 277 b.Truncate(b.Size() + 1) 278 }, 279 }, 280 { 281 name: "truncate-multiple-buffers", 282 input: strings.Repeat("1", baseChunkSize), 283 output: strings.Repeat("1", baseChunkSize*2-1), 284 op: func(t *testing.T, b *Buffer) { 285 b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize)))) 286 b.Truncate(baseChunkSize*2 - 1) 287 }, 288 }, 289 { 290 name: "truncate-multiple-buffers-to-one", 291 input: strings.Repeat("1", baseChunkSize), 292 output: "11111", 293 op: func(t *testing.T, b *Buffer) { 294 b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize)))) 295 b.Truncate(5) 296 }, 297 }, 298 299 // TrimFront. 300 { 301 name: "trim", 302 input: "hello world", 303 output: "world", 304 op: func(t *testing.T, b *Buffer) { 305 b.TrimFront(6) 306 }, 307 }, 308 { 309 name: "trim-too-large", 310 input: "hello world", 311 output: "", 312 op: func(t *testing.T, b *Buffer) { 313 b.TrimFront(b.Size() + 1) 314 }, 315 }, 316 { 317 name: "trim-multiple-buffers", 318 input: strings.Repeat("1", baseChunkSize), 319 output: strings.Repeat("1", baseChunkSize*2-1), 320 op: func(t *testing.T, b *Buffer) { 321 b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize)))) 322 b.TrimFront(1) 323 }, 324 }, 325 { 326 name: "trim-multiple-buffers-to-one-buffer", 327 input: strings.Repeat("1", baseChunkSize), 328 output: "1", 329 op: func(t *testing.T, b *Buffer) { 330 b.Append(NewViewWithData([]byte(strings.Repeat("1", baseChunkSize)))) 331 b.TrimFront(baseChunkSize*2 - 1) 332 }, 333 }, 334 335 // GrowTo. 336 { 337 name: "growto", 338 input: "hello world", 339 output: "hello world", 340 op: func(t *testing.T, b *Buffer) { 341 b.GrowTo(1, true) 342 }, 343 }, 344 { 345 name: "growto-from-zero", 346 output: strings.Repeat("\x00", 1024), 347 op: func(t *testing.T, b *Buffer) { 348 b.GrowTo(1024, true) 349 }, 350 }, 351 { 352 name: "growto-from-non-zero", 353 input: strings.Repeat("1", baseChunkSize), 354 output: strings.Repeat("1", baseChunkSize) + strings.Repeat("\x00", baseChunkSize), 355 op: func(t *testing.T, b *Buffer) { 356 b.GrowTo(baseChunkSize*2, true) 357 }, 358 }, 359 360 // Clone. 361 { 362 name: "clone", 363 input: "hello", 364 output: "hello", 365 op: func(t *testing.T, b *Buffer) { 366 other := b.Clone() 367 bs := other.Flatten() 368 want := []byte("hello") 369 if !bytes.Equal(bs, want) { 370 t.Errorf("expected %v, got %v", want, bs) 371 } 372 }, 373 }, 374 { 375 name: "copy-large", 376 input: strings.Repeat("1", baseChunkSize+1), 377 output: strings.Repeat("1", baseChunkSize+1), 378 op: func(t *testing.T, b *Buffer) { 379 other := b.Clone() 380 bs := other.Flatten() 381 want := []byte(strings.Repeat("1", baseChunkSize+1)) 382 if !bytes.Equal(bs, want) { 383 t.Errorf("expected %v, got %v", want, bs) 384 } 385 }, 386 }, 387 388 // Merge. 389 { 390 name: "merge", 391 input: "hello", 392 output: "hello world", 393 op: func(t *testing.T, b *Buffer) { 394 var other Buffer 395 other.Append(NewViewWithData([]byte(" world"))) 396 b.Merge(&other) 397 if sz := other.Size(); sz != 0 { 398 t.Errorf("expected 0, got %d", sz) 399 } 400 }, 401 }, 402 { 403 name: "merge-large", 404 input: strings.Repeat("1", baseChunkSize+1), 405 output: strings.Repeat("1", baseChunkSize+1) + strings.Repeat("0", baseChunkSize+1), 406 op: func(t *testing.T, b *Buffer) { 407 var other Buffer 408 other.Append(NewViewWithData(([]byte(strings.Repeat("0", baseChunkSize+1))))) 409 b.Merge(&other) 410 if sz := other.Size(); sz != 0 { 411 t.Errorf("expected 0, got %d", sz) 412 } 413 }, 414 }, 415 416 // ReadAt. 417 { 418 name: "readat", 419 input: "hello", 420 output: "hello", 421 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, 0, 6, "hello", io.EOF) }, 422 }, 423 { 424 name: "readat-long", 425 input: "hello", 426 output: "hello", 427 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, 0, 8, "hello", io.EOF) }, 428 }, 429 { 430 name: "readat-short", 431 input: "hello", 432 output: "hello", 433 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, 0, 3, "hel", nil) }, 434 }, 435 { 436 name: "readat-offset", 437 input: "hello", 438 output: "hello", 439 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, 2, 3, "llo", io.EOF) }, 440 }, 441 { 442 name: "readat-long-offset", 443 input: "hello", 444 output: "hello", 445 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, 2, 8, "llo", io.EOF) }, 446 }, 447 { 448 name: "readat-short-offset", 449 input: "hello", 450 output: "hello", 451 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, 2, 2, "ll", nil) }, 452 }, 453 { 454 name: "readat-skip-all", 455 input: "hello", 456 output: "hello", 457 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, baseChunkSize+1, 1, "", io.EOF) }, 458 }, 459 { 460 name: "readat-second-view", 461 input: strings.Repeat("0", baseChunkSize+1) + "12", 462 output: strings.Repeat("0", baseChunkSize+1) + "12", 463 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, baseChunkSize+1, 1, "1", nil) }, 464 }, 465 { 466 name: "readat-second-buffer-end", 467 input: strings.Repeat("0", baseChunkSize+1) + "12", 468 output: strings.Repeat("0", baseChunkSize+1) + "12", 469 op: func(t *testing.T, b *Buffer) { testReadAt(t, b, baseChunkSize+1, 2, "12", io.EOF) }, 470 }, 471 } 472 473 for _, tc := range testCases { 474 for fillName, fn := range fillFuncs { 475 t.Run(fillName+"/"+tc.name, func(t *testing.T) { 476 // Construct & fill the view. 477 var buf Buffer 478 fn(&buf, []byte(tc.input)) 479 480 // Run the operation. 481 if tc.op != nil { 482 tc.op(t, &buf) 483 } 484 485 // Flatten and validate. 486 out := buf.Flatten() 487 if !bytes.Equal([]byte(tc.output), out) { 488 t.Errorf("expected %q, got %q", tc.output, string(out)) 489 } 490 491 // Ensure the size is correct. 492 if len(out) != int(buf.Size()) { 493 t.Errorf("size is wrong: expected %d, got %d", len(out), buf.Size()) 494 } 495 496 // Calculate contents via apply. 497 var appliedOut []byte 498 buf.Apply(func(v *View) { 499 appliedOut = append(appliedOut, v.AsSlice()...) 500 }) 501 if len(appliedOut) != len(out) { 502 t.Errorf("expected %d, got %d", len(out), len(appliedOut)) 503 } 504 if !bytes.Equal(appliedOut, out) { 505 t.Errorf("expected %v, got %v", out, appliedOut) 506 } 507 508 // Calculate contents via ReadToWriter. 509 var b bytes.Buffer 510 n, err := buf.ReadToWriter(&b, int64(len(out))) 511 if n != int64(len(out)) { 512 t.Errorf("expected %d, got %d", len(out), n) 513 } 514 if err != nil { 515 t.Errorf("expected nil, got %v", err) 516 } 517 if !bytes.Equal(b.Bytes(), out) { 518 t.Errorf("expected %v, got %v", out, b.Bytes()) 519 } 520 }) 521 } 522 } 523 } 524 525 func TestBufferPullUp(t *testing.T) { 526 for _, tc := range []struct { 527 desc string 528 inputs []string 529 offset int 530 length int 531 output string 532 failed bool 533 // lengths is the lengths of each buffer node after the pull up. 534 lengths []int 535 }{ 536 { 537 desc: "whole empty view", 538 }, 539 { 540 desc: "zero pull", 541 inputs: []string{"hello", " world"}, 542 lengths: []int{5, 6}, 543 }, 544 { 545 desc: "whole view", 546 inputs: []string{"hello", " world"}, 547 offset: 0, 548 length: 11, 549 output: "hello world", 550 lengths: []int{11}, 551 }, 552 { 553 desc: "middle to end aligned", 554 inputs: []string{"0123", "45678", "9abcd"}, 555 offset: 4, 556 length: 10, 557 output: "456789abcd", 558 lengths: []int{4, 10}, 559 }, 560 { 561 desc: "middle to end unaligned", 562 inputs: []string{"0123", "45678", "9abcd"}, 563 offset: 6, 564 length: 8, 565 output: "6789abcd", 566 lengths: []int{4, 10}, 567 }, 568 { 569 desc: "middle aligned", 570 inputs: []string{"0123", "45678", "9abcd", "efgh"}, 571 offset: 6, 572 length: 5, 573 output: "6789a", 574 lengths: []int{4, 10, 4}, 575 }, 576 577 // Failed cases. 578 { 579 desc: "empty view - length too long", 580 offset: 0, 581 length: 1, 582 failed: true, 583 }, 584 { 585 desc: "empty view - offset too large", 586 offset: 1, 587 length: 1, 588 failed: true, 589 }, 590 { 591 desc: "length too long", 592 inputs: []string{"0123", "45678", "9abcd"}, 593 offset: 4, 594 length: 100, 595 failed: true, 596 lengths: []int{4, 5, 5}, 597 }, 598 { 599 desc: "offset too large", 600 inputs: []string{"0123", "45678", "9abcd"}, 601 offset: 100, 602 length: 1, 603 failed: true, 604 lengths: []int{4, 5, 5}, 605 }, 606 } { 607 t.Run(tc.desc, func(t *testing.T) { 608 var b Buffer 609 defer b.Release() 610 for _, s := range tc.inputs { 611 v := NewViewWithData([]byte(s)) 612 b.appendOwned(v) 613 } 614 615 got, gotOk := b.PullUp(tc.offset, tc.length) 616 want, wantOk := []byte(tc.output), !tc.failed 617 if gotOk == wantOk && got.Size() == 0 && len(want) == 0 { 618 return 619 } 620 if gotOk != wantOk || !bytes.Equal(got.AsSlice(), want) { 621 t.Errorf("v.PullUp(%d, %d) = %q, %t; %q, %t", tc.offset, tc.length, got.AsSlice(), gotOk, want, wantOk) 622 } 623 624 var gotLengths []int 625 for v := b.data.Front(); v != nil; v = v.Next() { 626 gotLengths = append(gotLengths, v.Size()) 627 } 628 if !slices.Equal(gotLengths, tc.lengths) { 629 t.Errorf("lengths = %v; want %v", gotLengths, tc.lengths) 630 } 631 }) 632 } 633 } 634 635 func TestReadFromLargeWriter(t *testing.T) { 636 writeSize := int64(1 << 20) 637 largeWriter := bytes.NewBuffer(make([]byte, writeSize)) 638 b := Buffer{} 639 // Expect this write to be buffered into several MaxChunkSize sized views. 640 n, err := b.WriteFromReader(largeWriter, writeSize) 641 if err != nil { 642 t.Fatalf("b.WriteFromReader() failed: want err=nil, got %v", err) 643 } 644 if n != writeSize { 645 t.Errorf("got b.WriteFromReader()=%d, want %d", n, writeSize) 646 } 647 nChunks := int(writeSize / MaxChunkSize) 648 if b.data.Len() != nChunks { 649 t.Errorf("b.WriteFromReader() failed, got b.data.Len()=%d, want %d", b.data.Len(), nChunks) 650 } 651 } 652 653 func TestRead(t *testing.T) { 654 readStrings := []string{"abcdef", "123456", "ghijkl"} 655 totalSize := len(readStrings) * len(readStrings[0]) 656 for readSz := 0; readSz < totalSize+1; readSz++ { 657 b := Buffer{} 658 for _, s := range readStrings { 659 v := NewViewWithData([]byte(s)) 660 b.appendOwned(v) 661 } 662 orig := b.Clone() 663 orig.Truncate(int64(readSz)) 664 p := make([]byte, readSz) 665 _, err := b.read(p) 666 if err != nil { 667 t.Fatalf("Read([]byte(%d)) failed: %v", readSz, err) 668 } 669 if !bytes.Equal(p, orig.Flatten()) { 670 t.Errorf("Read([]byte(%d)) failed, want p=%v, got %v", readSz, orig.Flatten(), p) 671 } 672 if int(b.Size()) != totalSize-readSz { 673 t.Errorf("Read([]byte(%d)) failed, want b.Size()=%v, got %v", readSz, totalSize-readSz, b.Size()) 674 } 675 } 676 } 677 678 func TestReadByte(t *testing.T) { 679 readString := "abcdef123456ghijkl" 680 b := Buffer{} 681 nViews := 3 682 for i := 0; i < nViews; i++ { 683 vLen := len(readString) / nViews 684 v := NewViewWithData([]byte(readString[i*vLen : (i+1)*vLen])) 685 b.appendOwned(v) 686 } 687 for i := 0; i < len(readString); i++ { 688 orig := readString[i] 689 bt, err := b.readByte() 690 if err != nil { 691 t.Fatalf("readByte() failed: %v", err) 692 } 693 if bt != orig { 694 t.Errorf("readByte() failed, want %v, got %v", orig, bt) 695 } 696 if int(b.Size()) != len(readString[i+1:]) { 697 t.Errorf("readByte() failed, want b.Size()=%v, got %v", len(readString[i+1:]), b.Size()) 698 } 699 } 700 } 701 702 func TestPullUpModifiedViews(t *testing.T) { 703 var b Buffer 704 defer b.Release() 705 for _, s := range []string{"abcdef", "123456", "ghijkl"} { 706 v := NewViewWithData([]byte(s)) 707 v.TrimFront(3) 708 b.appendOwned(v) 709 } 710 711 v, ok := b.PullUp(3, 3) 712 if !ok { 713 t.Errorf("PullUp failed: want ok=true, got ok=false") 714 } 715 want := []byte("456") 716 if !bytes.Equal(v.AsSlice(), want) { 717 t.Errorf("PullUp failed: want %v, got %v", want, v.AsSlice()) 718 } 719 } 720 721 func TestBufferClone(t *testing.T) { 722 const ( 723 originalSize = 90 724 bytesToDelete = 30 725 ) 726 b := MakeWithData(bytes.Repeat([]byte{originalSize}, originalSize)) 727 clonedB := b.Clone() 728 b.TrimFront(bytesToDelete) 729 730 if got, want := int(b.Size()), originalSize-bytesToDelete; got != want { 731 t.Errorf("original buffer was not changed: size expected = %d, got = %d", want, got) 732 } 733 if got := clonedB.Size(); got != originalSize { 734 t.Errorf("cloned buffer should not be modified: expected size = %d, got = %d", originalSize, got) 735 } 736 } 737 738 func TestBufferSubApply(t *testing.T) { 739 var b Buffer 740 defer b.Release() 741 b.appendOwned(NewViewWithData([]byte("0123"))) 742 b.appendOwned(NewViewWithData([]byte("45678"))) 743 b.appendOwned(NewViewWithData([]byte("9abcd"))) 744 data := []byte("0123456789abcd") 745 746 for i := 0; i <= len(data); i++ { 747 for j := i; j <= len(data); j++ { 748 t.Run(fmt.Sprintf("SubApply(%d,%d)", i, j), func(t *testing.T) { 749 var got []byte 750 b.SubApply(i, j-i, func(v *View) { 751 got = append(got, v.AsSlice()...) 752 }) 753 if want := data[i:j]; !bytes.Equal(got, want) { 754 t.Errorf("got = %q; want %q", got, want) 755 } 756 }) 757 } 758 } 759 } 760 761 func doSaveAndLoad(t *testing.T, toSave, toLoad *Buffer) { 762 t.Helper() 763 var buf bytes.Buffer 764 ctx := context.Background() 765 if _, err := state.Save(ctx, &buf, toSave); err != nil { 766 t.Fatal("state.Save:", err) 767 } 768 if _, err := state.Load(ctx, bytes.NewReader(buf.Bytes()), toLoad); err != nil { 769 t.Fatal("state.Load:", err) 770 } 771 } 772 773 func TestSaveRestoreBufferEmpty(t *testing.T) { 774 var toSave Buffer 775 var b Buffer 776 doSaveAndLoad(t, &toSave, &b) 777 778 if got := b.Flatten(); len(got) != 0 { 779 t.Errorf("v.Flatten() = %x, want []", got) 780 } 781 } 782 783 func TestSaveRestoreBuffer(t *testing.T) { 784 // Create data that fits slots. 785 data := bytes.Join([][]byte{ 786 bytes.Repeat([]byte{1, 2}, baseChunkSize), 787 }, nil) 788 789 var toSave Buffer 790 toSave.appendOwned(NewViewWithData(data)) 791 792 var b Buffer 793 doSaveAndLoad(t, &toSave, &b) 794 795 // Next available slot at index 3; 0-2 slot are used. 796 if got := b.Flatten(); !bytes.Equal(got, data) { 797 t.Errorf("v.Flatten() = %x, want %x", got, data) 798 } 799 } 800 801 func TestRangeIntersect(t *testing.T) { 802 for _, tc := range []struct { 803 desc string 804 x, y, want Range 805 }{ 806 { 807 desc: "empty intersects empty", 808 }, 809 { 810 desc: "empty intersection", 811 x: Range{end: 10}, 812 y: Range{begin: 10, end: 20}, 813 }, 814 { 815 desc: "some intersection", 816 x: Range{begin: 5, end: 20}, 817 y: Range{end: 10}, 818 want: Range{begin: 5, end: 10}, 819 }, 820 } { 821 t.Run(tc.desc, func(t *testing.T) { 822 if got := tc.x.Intersect(tc.y); got != tc.want { 823 t.Errorf("(%#v).Intersect(%#v) = %#v; want %#v", tc.x, tc.y, got, tc.want) 824 } 825 if got := tc.y.Intersect(tc.x); got != tc.want { 826 t.Errorf("(%#v).Intersect(%#v) = %#v; want %#v", tc.y, tc.x, got, tc.want) 827 } 828 }) 829 } 830 } 831 832 func TestRangeOffset(t *testing.T) { 833 for _, tc := range []struct { 834 input Range 835 offset int 836 output Range 837 }{ 838 { 839 input: Range{}, 840 offset: 0, 841 output: Range{}, 842 }, 843 { 844 input: Range{}, 845 offset: -1, 846 output: Range{begin: -1, end: -1}, 847 }, 848 { 849 input: Range{begin: 10, end: 20}, 850 offset: -1, 851 output: Range{begin: 9, end: 19}, 852 }, 853 { 854 input: Range{begin: 10, end: 20}, 855 offset: 2, 856 output: Range{begin: 12, end: 22}, 857 }, 858 } { 859 if got := tc.input.Offset(tc.offset); got != tc.output { 860 t.Errorf("(%#v).Offset(%d) = %#v, want %#v", tc.input, tc.offset, got, tc.output) 861 } 862 } 863 } 864 865 func TestRangeLen(t *testing.T) { 866 for _, tc := range []struct { 867 r Range 868 want int 869 }{ 870 {r: Range{}, want: 0}, 871 {r: Range{begin: 1, end: 1}, want: 0}, 872 {r: Range{begin: -1, end: -1}, want: 0}, 873 {r: Range{end: 10}, want: 10}, 874 {r: Range{begin: 5, end: 10}, want: 5}, 875 } { 876 if got := tc.r.Len(); got != tc.want { 877 t.Errorf("(%#v).Len() = %d, want %d", tc.r, got, tc.want) 878 } 879 } 880 } 881 882 func TestChecksum(t *testing.T) { 883 data := make([]byte, 100) 884 rand.Read(data) 885 886 b := MakeWithData(data[:30]) 887 b.appendOwned(NewViewWithData(data[30:70])) 888 b.appendOwned(NewViewWithData(data[70:])) 889 890 for offset := 0; offset < 100; offset++ { 891 var cs checksum.Checksumer 892 cs.Add(data[offset:]) 893 dataChecksum := cs.Checksum() 894 bufChecksum := b.Checksum(offset) 895 896 if dataChecksum != bufChecksum { 897 t.Errorf("(%#v).Checksum(%d) = %d, want %d", b, offset, bufChecksum, dataChecksum) 898 } 899 } 900 }