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