github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/transform/transform_test.go (about) 1 // Copyright 2013 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 5 package transform 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io/ioutil" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 "unicode/utf8" 17 ) 18 19 type lowerCaseASCII struct{ NopResetter } 20 21 func (lowerCaseASCII) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 22 n := len(src) 23 if n > len(dst) { 24 n, err = len(dst), ErrShortDst 25 } 26 for i, c := range src[:n] { 27 if 'A' <= c && c <= 'Z' { 28 c += 'a' - 'A' 29 } 30 dst[i] = c 31 } 32 return n, n, err 33 } 34 35 var errYouMentionedX = errors.New("you mentioned X") 36 37 type dontMentionX struct{ NopResetter } 38 39 func (dontMentionX) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 40 n := len(src) 41 if n > len(dst) { 42 n, err = len(dst), ErrShortDst 43 } 44 for i, c := range src[:n] { 45 if c == 'X' { 46 return i, i, errYouMentionedX 47 } 48 dst[i] = c 49 } 50 return n, n, err 51 } 52 53 // doublerAtEOF is a strange Transformer that transforms "this" to "tthhiiss", 54 // but only if atEOF is true. 55 type doublerAtEOF struct{ NopResetter } 56 57 func (doublerAtEOF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 58 if !atEOF { 59 return 0, 0, ErrShortSrc 60 } 61 for i, c := range src { 62 if 2*i+2 >= len(dst) { 63 return 2 * i, i, ErrShortDst 64 } 65 dst[2*i+0] = c 66 dst[2*i+1] = c 67 } 68 return 2 * len(src), len(src), nil 69 } 70 71 // rleDecode and rleEncode implement a toy run-length encoding: "aabbbbbbbbbb" 72 // is encoded as "2a10b". The decoding is assumed to not contain any numbers. 73 74 type rleDecode struct{ NopResetter } 75 76 func (rleDecode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 77 loop: 78 for len(src) > 0 { 79 n := 0 80 for i, c := range src { 81 if '0' <= c && c <= '9' { 82 n = 10*n + int(c-'0') 83 continue 84 } 85 if i == 0 { 86 return nDst, nSrc, errors.New("rleDecode: bad input") 87 } 88 if n > len(dst) { 89 return nDst, nSrc, ErrShortDst 90 } 91 for j := 0; j < n; j++ { 92 dst[j] = c 93 } 94 dst, src = dst[n:], src[i+1:] 95 nDst, nSrc = nDst+n, nSrc+i+1 96 continue loop 97 } 98 if atEOF { 99 return nDst, nSrc, errors.New("rleDecode: bad input") 100 } 101 return nDst, nSrc, ErrShortSrc 102 } 103 return nDst, nSrc, nil 104 } 105 106 type rleEncode struct { 107 NopResetter 108 109 // allowStutter means that "xxxxxxxx" can be encoded as "5x3x" 110 // instead of always as "8x". 111 allowStutter bool 112 } 113 114 func (e rleEncode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 115 for len(src) > 0 { 116 n, c0 := len(src), src[0] 117 for i, c := range src[1:] { 118 if c != c0 { 119 n = i + 1 120 break 121 } 122 } 123 if n == len(src) && !atEOF && !e.allowStutter { 124 return nDst, nSrc, ErrShortSrc 125 } 126 s := strconv.Itoa(n) 127 if len(s) >= len(dst) { 128 return nDst, nSrc, ErrShortDst 129 } 130 copy(dst, s) 131 dst[len(s)] = c0 132 dst, src = dst[len(s)+1:], src[n:] 133 nDst, nSrc = nDst+len(s)+1, nSrc+n 134 } 135 return nDst, nSrc, nil 136 } 137 138 // trickler consumes all input bytes, but writes a single byte at a time to dst. 139 type trickler []byte 140 141 func (t *trickler) Reset() { 142 *t = nil 143 } 144 145 func (t *trickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 146 *t = append(*t, src...) 147 if len(*t) == 0 { 148 return 0, 0, nil 149 } 150 if len(dst) == 0 { 151 return 0, len(src), ErrShortDst 152 } 153 dst[0] = (*t)[0] 154 *t = (*t)[1:] 155 if len(*t) > 0 { 156 err = ErrShortDst 157 } 158 return 1, len(src), err 159 } 160 161 // delayedTrickler is like trickler, but delays writing output to dst. This is 162 // highly unlikely to be relevant in practice, but it seems like a good idea 163 // to have some tolerance as long as progress can be detected. 164 type delayedTrickler []byte 165 166 func (t *delayedTrickler) Reset() { 167 *t = nil 168 } 169 func (t *delayedTrickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) { 170 if len(*t) > 0 && len(dst) > 0 { 171 dst[0] = (*t)[0] 172 *t = (*t)[1:] 173 nDst = 1 174 } 175 *t = append(*t, src...) 176 if len(*t) > 0 { 177 err = ErrShortDst 178 } 179 return nDst, len(src), err 180 } 181 182 type testCase struct { 183 desc string 184 t Transformer 185 src string 186 dstSize int 187 srcSize int 188 ioSize int 189 wantStr string 190 wantErr error 191 wantIter int // number of iterations taken; 0 means we don't care. 192 } 193 194 func (t testCase) String() string { 195 return tstr(t.t) + "; " + t.desc 196 } 197 198 func tstr(t Transformer) string { 199 if stringer, ok := t.(fmt.Stringer); ok { 200 return stringer.String() 201 } 202 s := fmt.Sprintf("%T", t) 203 return s[1+strings.Index(s, "."):] 204 } 205 206 func (c chain) String() string { 207 buf := &bytes.Buffer{} 208 buf.WriteString("Chain(") 209 for i, l := range c.link[:len(c.link)-1] { 210 if i != 0 { 211 fmt.Fprint(buf, ", ") 212 } 213 buf.WriteString(tstr(l.t)) 214 } 215 buf.WriteString(")") 216 return buf.String() 217 } 218 219 var testCases = []testCase{ 220 { 221 desc: "empty", 222 t: lowerCaseASCII{}, 223 src: "", 224 dstSize: 100, 225 srcSize: 100, 226 wantStr: "", 227 }, 228 229 { 230 desc: "basic", 231 t: lowerCaseASCII{}, 232 src: "Hello WORLD.", 233 dstSize: 100, 234 srcSize: 100, 235 wantStr: "hello world.", 236 }, 237 238 { 239 desc: "small dst", 240 t: lowerCaseASCII{}, 241 src: "Hello WORLD.", 242 dstSize: 3, 243 srcSize: 100, 244 wantStr: "hello world.", 245 }, 246 247 { 248 desc: "small src", 249 t: lowerCaseASCII{}, 250 src: "Hello WORLD.", 251 dstSize: 100, 252 srcSize: 4, 253 wantStr: "hello world.", 254 }, 255 256 { 257 desc: "small buffers", 258 t: lowerCaseASCII{}, 259 src: "Hello WORLD.", 260 dstSize: 3, 261 srcSize: 4, 262 wantStr: "hello world.", 263 }, 264 265 { 266 desc: "very small buffers", 267 t: lowerCaseASCII{}, 268 src: "Hello WORLD.", 269 dstSize: 1, 270 srcSize: 1, 271 wantStr: "hello world.", 272 }, 273 274 { 275 desc: "basic", 276 t: dontMentionX{}, 277 src: "The First Rule of Transform Club: don't mention Mister X, ever.", 278 dstSize: 100, 279 srcSize: 100, 280 wantStr: "The First Rule of Transform Club: don't mention Mister ", 281 wantErr: errYouMentionedX, 282 }, 283 284 { 285 desc: "small buffers", 286 t: dontMentionX{}, 287 src: "The First Rule of Transform Club: don't mention Mister X, ever.", 288 dstSize: 10, 289 srcSize: 10, 290 wantStr: "The First Rule of Transform Club: don't mention Mister ", 291 wantErr: errYouMentionedX, 292 }, 293 294 { 295 desc: "very small buffers", 296 t: dontMentionX{}, 297 src: "The First Rule of Transform Club: don't mention Mister X, ever.", 298 dstSize: 1, 299 srcSize: 1, 300 wantStr: "The First Rule of Transform Club: don't mention Mister ", 301 wantErr: errYouMentionedX, 302 }, 303 304 { 305 desc: "only transform at EOF", 306 t: doublerAtEOF{}, 307 src: "this", 308 dstSize: 100, 309 srcSize: 100, 310 wantStr: "tthhiiss", 311 }, 312 313 { 314 desc: "basic", 315 t: rleDecode{}, 316 src: "1a2b3c10d11e0f1g", 317 dstSize: 100, 318 srcSize: 100, 319 wantStr: "abbcccddddddddddeeeeeeeeeeeg", 320 }, 321 322 { 323 desc: "long", 324 t: rleDecode{}, 325 src: "12a23b34c45d56e99z", 326 dstSize: 100, 327 srcSize: 100, 328 wantStr: strings.Repeat("a", 12) + 329 strings.Repeat("b", 23) + 330 strings.Repeat("c", 34) + 331 strings.Repeat("d", 45) + 332 strings.Repeat("e", 56) + 333 strings.Repeat("z", 99), 334 }, 335 336 { 337 desc: "tight buffers", 338 t: rleDecode{}, 339 src: "1a2b3c10d11e0f1g", 340 dstSize: 11, 341 srcSize: 3, 342 wantStr: "abbcccddddddddddeeeeeeeeeeeg", 343 }, 344 345 { 346 desc: "short dst", 347 t: rleDecode{}, 348 src: "1a2b3c10d11e0f1g", 349 dstSize: 10, 350 srcSize: 3, 351 wantStr: "abbcccdddddddddd", 352 wantErr: ErrShortDst, 353 }, 354 355 { 356 desc: "short src", 357 t: rleDecode{}, 358 src: "1a2b3c10d11e0f1g", 359 dstSize: 11, 360 srcSize: 2, 361 ioSize: 2, 362 wantStr: "abbccc", 363 wantErr: ErrShortSrc, 364 }, 365 366 { 367 desc: "basic", 368 t: rleEncode{}, 369 src: "abbcccddddddddddeeeeeeeeeeeg", 370 dstSize: 100, 371 srcSize: 100, 372 wantStr: "1a2b3c10d11e1g", 373 }, 374 375 { 376 desc: "long", 377 t: rleEncode{}, 378 src: strings.Repeat("a", 12) + 379 strings.Repeat("b", 23) + 380 strings.Repeat("c", 34) + 381 strings.Repeat("d", 45) + 382 strings.Repeat("e", 56) + 383 strings.Repeat("z", 99), 384 dstSize: 100, 385 srcSize: 100, 386 wantStr: "12a23b34c45d56e99z", 387 }, 388 389 { 390 desc: "tight buffers", 391 t: rleEncode{}, 392 src: "abbcccddddddddddeeeeeeeeeeeg", 393 dstSize: 3, 394 srcSize: 12, 395 wantStr: "1a2b3c10d11e1g", 396 }, 397 398 { 399 desc: "short dst", 400 t: rleEncode{}, 401 src: "abbcccddddddddddeeeeeeeeeeeg", 402 dstSize: 2, 403 srcSize: 12, 404 wantStr: "1a2b3c", 405 wantErr: ErrShortDst, 406 }, 407 408 { 409 desc: "short src", 410 t: rleEncode{}, 411 src: "abbcccddddddddddeeeeeeeeeeeg", 412 dstSize: 3, 413 srcSize: 11, 414 ioSize: 11, 415 wantStr: "1a2b3c10d", 416 wantErr: ErrShortSrc, 417 }, 418 419 { 420 desc: "allowStutter = false", 421 t: rleEncode{allowStutter: false}, 422 src: "aaaabbbbbbbbccccddddd", 423 dstSize: 10, 424 srcSize: 10, 425 wantStr: "4a8b4c5d", 426 }, 427 428 { 429 desc: "allowStutter = true", 430 t: rleEncode{allowStutter: true}, 431 src: "aaaabbbbbbbbccccddddd", 432 dstSize: 10, 433 srcSize: 10, 434 ioSize: 10, 435 wantStr: "4a6b2b4c4d1d", 436 }, 437 438 { 439 desc: "trickler", 440 t: &trickler{}, 441 src: "abcdefghijklm", 442 dstSize: 3, 443 srcSize: 15, 444 wantStr: "abcdefghijklm", 445 }, 446 447 { 448 desc: "delayedTrickler", 449 t: &delayedTrickler{}, 450 src: "abcdefghijklm", 451 dstSize: 3, 452 srcSize: 15, 453 wantStr: "abcdefghijklm", 454 }, 455 } 456 457 func TestReader(t *testing.T) { 458 for _, tc := range testCases { 459 r := NewReader(strings.NewReader(tc.src), tc.t) 460 // Differently sized dst and src buffers are not part of the 461 // exported API. We override them manually. 462 r.dst = make([]byte, tc.dstSize) 463 r.src = make([]byte, tc.srcSize) 464 got, err := ioutil.ReadAll(r) 465 str := string(got) 466 if str != tc.wantStr || err != tc.wantErr { 467 t.Errorf("%s:\ngot %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr) 468 } 469 } 470 } 471 472 func TestWriter(t *testing.T) { 473 tests := append(testCases, chainTests()...) 474 for _, tc := range tests { 475 sizes := []int{1, 2, 3, 4, 5, 10, 100, 1000} 476 if tc.ioSize > 0 { 477 sizes = []int{tc.ioSize} 478 } 479 for _, sz := range sizes { 480 bb := &bytes.Buffer{} 481 w := NewWriter(bb, tc.t) 482 // Differently sized dst and src buffers are not part of the 483 // exported API. We override them manually. 484 w.dst = make([]byte, tc.dstSize) 485 w.src = make([]byte, tc.srcSize) 486 src := make([]byte, sz) 487 var err error 488 for b := tc.src; len(b) > 0 && err == nil; { 489 n := copy(src, b) 490 b = b[n:] 491 m := 0 492 m, err = w.Write(src[:n]) 493 if m != n && err == nil { 494 t.Errorf("%s:%d: did not consume all bytes %d < %d", tc, sz, m, n) 495 } 496 } 497 if err == nil { 498 err = w.Close() 499 } 500 str := bb.String() 501 if str != tc.wantStr || err != tc.wantErr { 502 t.Errorf("%s:%d:\ngot %q, %v\nwant %q, %v", tc, sz, str, err, tc.wantStr, tc.wantErr) 503 } 504 } 505 } 506 } 507 508 func TestNop(t *testing.T) { 509 testCases := []struct { 510 str string 511 dstSize int 512 err error 513 }{ 514 {"", 0, nil}, 515 {"", 10, nil}, 516 {"a", 0, ErrShortDst}, 517 {"a", 1, nil}, 518 {"a", 10, nil}, 519 } 520 for i, tc := range testCases { 521 dst := make([]byte, tc.dstSize) 522 nDst, nSrc, err := Nop.Transform(dst, []byte(tc.str), true) 523 want := tc.str 524 if tc.dstSize < len(want) { 525 want = want[:tc.dstSize] 526 } 527 if got := string(dst[:nDst]); got != want || err != tc.err || nSrc != nDst { 528 t.Errorf("%d:\ngot %q, %d, %v\nwant %q, %d, %v", i, got, nSrc, err, want, nDst, tc.err) 529 } 530 } 531 } 532 533 func TestDiscard(t *testing.T) { 534 testCases := []struct { 535 str string 536 dstSize int 537 }{ 538 {"", 0}, 539 {"", 10}, 540 {"a", 0}, 541 {"ab", 10}, 542 } 543 for i, tc := range testCases { 544 nDst, nSrc, err := Discard.Transform(make([]byte, tc.dstSize), []byte(tc.str), true) 545 if nDst != 0 || nSrc != len(tc.str) || err != nil { 546 t.Errorf("%d:\ngot %q, %d, %v\nwant 0, %d, nil", i, nDst, nSrc, err, len(tc.str)) 547 } 548 } 549 } 550 551 // mkChain creates a Chain transformer. x must be alternating between transformer 552 // and bufSize, like T, (sz, T)* 553 func mkChain(x ...interface{}) *chain { 554 t := []Transformer{} 555 for i := 0; i < len(x); i += 2 { 556 t = append(t, x[i].(Transformer)) 557 } 558 c := Chain(t...).(*chain) 559 for i, j := 1, 1; i < len(x); i, j = i+2, j+1 { 560 c.link[j].b = make([]byte, x[i].(int)) 561 } 562 return c 563 } 564 565 func chainTests() []testCase { 566 return []testCase{ 567 { 568 desc: "nil error", 569 t: mkChain(rleEncode{}, 100, lowerCaseASCII{}), 570 src: "ABB", 571 dstSize: 100, 572 srcSize: 100, 573 wantStr: "1a2b", 574 wantErr: nil, 575 wantIter: 1, 576 }, 577 578 { 579 desc: "short dst buffer", 580 t: mkChain(lowerCaseASCII{}, 3, rleDecode{}), 581 src: "1a2b3c10d11e0f1g", 582 dstSize: 10, 583 srcSize: 3, 584 wantStr: "abbcccdddddddddd", 585 wantErr: ErrShortDst, 586 }, 587 588 { 589 desc: "short internal dst buffer", 590 t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop), 591 src: "1a2b3c10d11e0f1g", 592 dstSize: 100, 593 srcSize: 3, 594 wantStr: "abbcccdddddddddd", 595 wantErr: errShortInternal, 596 }, 597 598 { 599 desc: "short internal dst buffer from input", 600 t: mkChain(rleDecode{}, 10, Nop), 601 src: "1a2b3c10d11e0f1g", 602 dstSize: 100, 603 srcSize: 3, 604 wantStr: "abbcccdddddddddd", 605 wantErr: errShortInternal, 606 }, 607 608 { 609 desc: "empty short internal dst buffer", 610 t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop), 611 src: "4a7b11e0f1g", 612 dstSize: 100, 613 srcSize: 3, 614 wantStr: "aaaabbbbbbb", 615 wantErr: errShortInternal, 616 }, 617 618 { 619 desc: "empty short internal dst buffer from input", 620 t: mkChain(rleDecode{}, 10, Nop), 621 src: "4a7b11e0f1g", 622 dstSize: 100, 623 srcSize: 3, 624 wantStr: "aaaabbbbbbb", 625 wantErr: errShortInternal, 626 }, 627 628 { 629 desc: "short internal src buffer after full dst buffer", 630 t: mkChain(Nop, 5, rleEncode{}, 10, Nop), 631 src: "cccccddddd", 632 dstSize: 100, 633 srcSize: 100, 634 wantStr: "", 635 wantErr: errShortInternal, 636 wantIter: 1, 637 }, 638 639 { 640 desc: "short internal src buffer after short dst buffer; test lastFull", 641 t: mkChain(rleDecode{}, 5, rleEncode{}, 4, Nop), 642 src: "2a1b4c6d", 643 dstSize: 100, 644 srcSize: 100, 645 wantStr: "2a1b", 646 wantErr: errShortInternal, 647 }, 648 649 { 650 desc: "short internal src buffer after successful complete fill", 651 t: mkChain(Nop, 3, rleDecode{}), 652 src: "123a4b", 653 dstSize: 4, 654 srcSize: 3, 655 wantStr: "", 656 wantErr: errShortInternal, 657 wantIter: 1, 658 }, 659 660 { 661 desc: "short internal src buffer after short dst buffer; test lastFull", 662 t: mkChain(rleDecode{}, 5, rleEncode{}), 663 src: "2a1b4c6d", 664 dstSize: 4, 665 srcSize: 100, 666 wantStr: "2a1b", 667 wantErr: errShortInternal, 668 }, 669 670 { 671 desc: "short src buffer", 672 t: mkChain(rleEncode{}, 5, Nop), 673 src: "abbcccddddeeeee", 674 dstSize: 4, 675 srcSize: 4, 676 ioSize: 4, 677 wantStr: "1a2b3c", 678 wantErr: ErrShortSrc, 679 }, 680 681 { 682 desc: "process all in one go", 683 t: mkChain(rleEncode{}, 5, Nop), 684 src: "abbcccddddeeeeeffffff", 685 dstSize: 100, 686 srcSize: 100, 687 wantStr: "1a2b3c4d5e6f", 688 wantErr: nil, 689 wantIter: 1, 690 }, 691 692 { 693 desc: "complete processing downstream after error", 694 t: mkChain(dontMentionX{}, 2, rleDecode{}, 5, Nop), 695 src: "3a4b5eX", 696 dstSize: 100, 697 srcSize: 100, 698 ioSize: 100, 699 wantStr: "aaabbbbeeeee", 700 wantErr: errYouMentionedX, 701 }, 702 703 { 704 desc: "return downstream fatal errors first (followed by short dst)", 705 t: mkChain(dontMentionX{}, 8, rleDecode{}, 4, Nop), 706 src: "3a4b5eX", 707 dstSize: 100, 708 srcSize: 100, 709 ioSize: 100, 710 wantStr: "aaabbbb", 711 wantErr: errShortInternal, 712 }, 713 714 { 715 desc: "return downstream fatal errors first (followed by short src)", 716 t: mkChain(dontMentionX{}, 5, Nop, 1, rleDecode{}), 717 src: "1a5bX", 718 dstSize: 100, 719 srcSize: 100, 720 ioSize: 100, 721 wantStr: "", 722 wantErr: errShortInternal, 723 }, 724 725 { 726 desc: "short internal", 727 t: mkChain(Nop, 11, rleEncode{}, 3, Nop), 728 src: "abbcccddddddddddeeeeeeeeeeeg", 729 dstSize: 3, 730 srcSize: 100, 731 wantStr: "1a2b3c10d", 732 wantErr: errShortInternal, 733 }, 734 } 735 } 736 737 func doTransform(tc testCase) (res string, iter int, err error) { 738 tc.t.Reset() 739 dst := make([]byte, tc.dstSize) 740 out, in := make([]byte, 0, 2*len(tc.src)), []byte(tc.src) 741 for { 742 iter++ 743 src, atEOF := in, true 744 if len(src) > tc.srcSize { 745 src, atEOF = src[:tc.srcSize], false 746 } 747 nDst, nSrc, err := tc.t.Transform(dst, src, atEOF) 748 out = append(out, dst[:nDst]...) 749 in = in[nSrc:] 750 switch { 751 case err == nil && len(in) != 0: 752 case err == ErrShortSrc && nSrc > 0: 753 case err == ErrShortDst && (nDst > 0 || nSrc > 0): 754 default: 755 return string(out), iter, err 756 } 757 } 758 } 759 760 func TestChain(t *testing.T) { 761 if c, ok := Chain().(nop); !ok { 762 t.Errorf("empty chain: %v; want Nop", c) 763 } 764 765 // Test Chain for a single Transformer. 766 for _, tc := range testCases { 767 tc.t = Chain(tc.t) 768 str, _, err := doTransform(tc) 769 if str != tc.wantStr || err != tc.wantErr { 770 t.Errorf("%s:\ngot %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr) 771 } 772 } 773 774 tests := chainTests() 775 sizes := []int{1, 2, 3, 4, 5, 7, 10, 100, 1000} 776 addTest := func(tc testCase, t *chain) { 777 if t.link[0].t != tc.t && tc.wantErr == ErrShortSrc { 778 tc.wantErr = errShortInternal 779 } 780 if t.link[len(t.link)-2].t != tc.t && tc.wantErr == ErrShortDst { 781 tc.wantErr = errShortInternal 782 } 783 tc.t = t 784 tests = append(tests, tc) 785 } 786 for _, tc := range testCases { 787 for _, sz := range sizes { 788 tt := tc 789 tt.dstSize = sz 790 addTest(tt, mkChain(tc.t, tc.dstSize, Nop)) 791 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 2, Nop)) 792 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop)) 793 if sz >= tc.dstSize && (tc.wantErr != ErrShortDst || sz == tc.dstSize) { 794 addTest(tt, mkChain(Nop, tc.srcSize, tc.t)) 795 addTest(tt, mkChain(Nop, 100, Nop, tc.srcSize, tc.t)) 796 } 797 } 798 } 799 for _, tc := range testCases { 800 tt := tc 801 tt.dstSize = 1 802 tt.wantStr = "" 803 addTest(tt, mkChain(tc.t, tc.dstSize, Discard)) 804 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Discard)) 805 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, tc.dstSize, Discard)) 806 } 807 for _, tc := range testCases { 808 tt := tc 809 tt.dstSize = 100 810 tt.wantStr = strings.Replace(tc.src, "0f", "", -1) 811 // Chain encoders and decoders. 812 if _, ok := tc.t.(rleEncode); ok && tc.wantErr == nil { 813 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 1000, rleDecode{})) 814 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, tc.dstSize, rleDecode{})) 815 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{})) 816 // decoding needs larger destinations 817 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, rleDecode{}, 100, Nop)) 818 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{}, 100, Nop)) 819 } else if _, ok := tc.t.(rleDecode); ok && tc.wantErr == nil { 820 // The internal buffer size may need to be the sum of the maximum segment 821 // size of the two encoders! 822 addTest(tt, mkChain(tc.t, 2*tc.dstSize, rleEncode{})) 823 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 101, rleEncode{})) 824 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleEncode{})) 825 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 200, rleEncode{}, 100, Nop)) 826 } 827 } 828 for _, tc := range tests { 829 str, iter, err := doTransform(tc) 830 mi := tc.wantIter != 0 && tc.wantIter != iter 831 if str != tc.wantStr || err != tc.wantErr || mi { 832 t.Errorf("%s:\ngot iter:%d, %q, %v\nwant iter:%d, %q, %v", tc, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr) 833 } 834 break 835 } 836 } 837 838 func TestRemoveFunc(t *testing.T) { 839 filter := RemoveFunc(func(r rune) bool { 840 return strings.IndexRune("ab\u0300\u1234,", r) != -1 841 }) 842 tests := []testCase{ 843 { 844 src: ",", 845 wantStr: "", 846 }, 847 848 { 849 src: "c", 850 wantStr: "c", 851 }, 852 853 { 854 src: "\u2345", 855 wantStr: "\u2345", 856 }, 857 858 { 859 src: "tschüß", 860 wantStr: "tschüß", 861 }, 862 863 { 864 src: ",до,свидания,", 865 wantStr: "досвидания", 866 }, 867 868 { 869 src: "a\xbd\xb2=\xbc ⌘", 870 wantStr: "\uFFFD\uFFFD=\uFFFD ⌘", 871 }, 872 873 { 874 // If we didn't replace illegal bytes with RuneError, the result 875 // would be \u0300 or the code would need to be more complex. 876 src: "\xcc\u0300\x80", 877 wantStr: "\uFFFD\uFFFD", 878 }, 879 880 { 881 src: "\xcc\u0300\x80", 882 dstSize: 3, 883 wantStr: "\uFFFD\uFFFD", 884 wantIter: 2, 885 }, 886 887 { 888 // Test a long buffer greater than the internal buffer size 889 src: "hello\xcc\xcc\xccworld", 890 srcSize: 13, 891 wantStr: "hello\uFFFD\uFFFD\uFFFDworld", 892 wantIter: 1, 893 }, 894 895 { 896 src: "\u2345", 897 dstSize: 2, 898 wantStr: "", 899 wantErr: ErrShortDst, 900 }, 901 902 { 903 src: "\xcc", 904 dstSize: 2, 905 wantStr: "", 906 wantErr: ErrShortDst, 907 }, 908 909 { 910 src: "\u0300", 911 dstSize: 2, 912 srcSize: 1, 913 wantStr: "", 914 wantErr: ErrShortSrc, 915 }, 916 917 { 918 t: RemoveFunc(func(r rune) bool { 919 return r == utf8.RuneError 920 }), 921 src: "\xcc\u0300\x80", 922 wantStr: "\u0300", 923 }, 924 } 925 926 for _, tc := range tests { 927 tc.desc = tc.src 928 if tc.t == nil { 929 tc.t = filter 930 } 931 if tc.dstSize == 0 { 932 tc.dstSize = 100 933 } 934 if tc.srcSize == 0 { 935 tc.srcSize = 100 936 } 937 str, iter, err := doTransform(tc) 938 mi := tc.wantIter != 0 && tc.wantIter != iter 939 if str != tc.wantStr || err != tc.wantErr || mi { 940 t.Errorf("%+q:\ngot iter:%d, %+q, %v\nwant iter:%d, %+q, %v", tc.src, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr) 941 } 942 943 tc.src = str 944 idem, _, _ := doTransform(tc) 945 if str != idem { 946 t.Errorf("%+q: found %+q; want %+q", tc.src, idem, str) 947 } 948 } 949 } 950 951 func testString(t *testing.T, f func(Transformer, string) (string, int, error)) { 952 for _, tt := range append(testCases, chainTests()...) { 953 if tt.desc == "allowStutter = true" { 954 // We don't have control over the buffer size, so we eliminate tests 955 // that depend on a specific buffer size being set. 956 continue 957 } 958 if tt.wantErr == ErrShortDst || tt.wantErr == ErrShortSrc { 959 // The result string will be different. 960 continue 961 } 962 got, n, err := f(tt.t, tt.src) 963 if tt.wantErr != err { 964 t.Errorf("%s:error: got %v; want %v", tt.desc, err, tt.wantErr) 965 } 966 if got, want := err == nil, n == len(tt.src); got != want { 967 t.Errorf("%s:n: got %v; want %v", tt.desc, got, want) 968 } 969 if got != tt.wantStr { 970 t.Errorf("%s:string: got %q; want %q", tt.desc, got, tt.wantStr) 971 } 972 } 973 } 974 975 func TestBytes(t *testing.T) { 976 testString(t, func(z Transformer, s string) (string, int, error) { 977 b, n, err := Bytes(z, []byte(s)) 978 return string(b), n, err 979 }) 980 } 981 982 func TestString(t *testing.T) { 983 testString(t, String) 984 985 // Overrun the internal destination buffer. 986 for i, s := range []string{ 987 strings.Repeat("a", initialBufSize-1), 988 strings.Repeat("a", initialBufSize+0), 989 strings.Repeat("a", initialBufSize+1), 990 strings.Repeat("A", initialBufSize-1), 991 strings.Repeat("A", initialBufSize+0), 992 strings.Repeat("A", initialBufSize+1), 993 strings.Repeat("A", 2*initialBufSize-1), 994 strings.Repeat("A", 2*initialBufSize+0), 995 strings.Repeat("A", 2*initialBufSize+1), 996 strings.Repeat("a", initialBufSize-2) + "A", 997 strings.Repeat("a", initialBufSize-1) + "A", 998 strings.Repeat("a", initialBufSize+0) + "A", 999 strings.Repeat("a", initialBufSize+1) + "A", 1000 } { 1001 got, _, _ := String(lowerCaseASCII{}, s) 1002 if want := strings.ToLower(s); got != want { 1003 t.Errorf("%d:dst buffer test: got %s (%d); want %s (%d)", i, got, len(got), want, len(want)) 1004 } 1005 } 1006 1007 // Overrun the internal source buffer. 1008 for i, s := range []string{ 1009 strings.Repeat("a", initialBufSize-1), 1010 strings.Repeat("a", initialBufSize+0), 1011 strings.Repeat("a", initialBufSize+1), 1012 strings.Repeat("a", 2*initialBufSize+1), 1013 strings.Repeat("a", 2*initialBufSize+0), 1014 strings.Repeat("a", 2*initialBufSize+1), 1015 } { 1016 got, _, _ := String(rleEncode{}, s) 1017 if want := fmt.Sprintf("%da", len(s)); got != want { 1018 t.Errorf("%d:src buffer test: got %s (%d); want %s (%d)", i, got, len(got), want, len(want)) 1019 } 1020 } 1021 1022 // Test allocations for non-changing strings. 1023 // Note we still need to allocate a single buffer. 1024 for i, s := range []string{ 1025 "", 1026 "123", 1027 "123456789", 1028 strings.Repeat("a", initialBufSize), 1029 strings.Repeat("a", 10*initialBufSize), 1030 } { 1031 if n := testing.AllocsPerRun(5, func() { String(&lowerCaseASCII{}, s) }); n > 1 { 1032 t.Errorf("%d: #allocs was %f; want 1", i, n) 1033 } 1034 } 1035 } 1036 1037 // TestBytesAllocation tests that buffer growth stays limited with the trickler 1038 // transformer, which behaves oddly but within spec. In case buffer growth is 1039 // not correctly handled, the test will either panic with a failed allocation or 1040 // thrash. To ensure the tests terminate under the last condition, we time out 1041 // after some sufficiently long period of time. 1042 func TestBytesAllocation(t *testing.T) { 1043 done := make(chan bool) 1044 go func() { 1045 in := bytes.Repeat([]byte{'a'}, 1000) 1046 tr := trickler(make([]byte, 1)) 1047 Bytes(&tr, in) 1048 done <- true 1049 }() 1050 select { 1051 case <-done: 1052 case <-time.After(3 * time.Second): 1053 t.Error("time out, likely due to excessive allocation") 1054 } 1055 } 1056 1057 // TestStringAllocation tests that buffer growth stays limited with the trickler 1058 // transformer, which behaves oddly but within spec. In case buffer growth is 1059 // not correctly handled, the test will either panic with a failed allocation or 1060 // thrash. To ensure the tests terminate under the last condition, we time out 1061 // after some sufficiently long period of time. 1062 func TestStringAllocation(t *testing.T) { 1063 done := make(chan bool) 1064 go func() { 1065 in := strings.Repeat("a", 1000) 1066 tr := trickler(make([]byte, 1)) 1067 String(&tr, in) 1068 done <- true 1069 }() 1070 select { 1071 case <-done: 1072 case <-time.After(3 * time.Second): 1073 t.Error("time out, likely due to excessive allocation") 1074 } 1075 } 1076 1077 func BenchmarkStringLower(b *testing.B) { 1078 in := strings.Repeat("a", 4096) 1079 for i := 0; i < b.N; i++ { 1080 String(&lowerCaseASCII{}, in) 1081 } 1082 }