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.
18 "golang.org/x/text/internal/testtext"
21 type lowerCaseASCII struct{ NopResetter }
23 func (lowerCaseASCII) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
26 n, err = len(dst), ErrShortDst
28 for i, c := range src[:n] {
29 if 'A' <= c && c <= 'Z' {
37 // lowerCaseASCIILookahead lowercases the string and reports ErrShortSrc as long
38 // as the input is not atEOF.
39 type lowerCaseASCIILookahead struct{ NopResetter }
41 func (lowerCaseASCIILookahead) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
44 n, err = len(dst), ErrShortDst
46 for i, c := range src[:n] {
47 if 'A' <= c && c <= 'Z' {
58 var errYouMentionedX = errors.New("you mentioned X")
60 type dontMentionX struct{ NopResetter }
62 func (dontMentionX) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
65 n, err = len(dst), ErrShortDst
67 for i, c := range src[:n] {
69 return i, i, errYouMentionedX
76 var errAtEnd = errors.New("error after all text")
78 type errorAtEnd struct{ NopResetter }
80 func (errorAtEnd) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
83 return n, n, ErrShortDst
91 type replaceWithConstant struct {
96 func (t *replaceWithConstant) Reset() {
100 func (t *replaceWithConstant) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
102 nDst = copy(dst, t.replacement[t.written:])
104 if t.written < len(t.replacement) {
108 return nDst, len(src), err
111 type addAnXAtTheEnd struct{ NopResetter }
113 func (addAnXAtTheEnd) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
116 return n, n, ErrShortDst
122 return n, n, ErrShortDst
128 // doublerAtEOF is a strange Transformer that transforms "this" to "tthhiiss",
129 // but only if atEOF is true.
130 type doublerAtEOF struct{ NopResetter }
132 func (doublerAtEOF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
134 return 0, 0, ErrShortSrc
136 for i, c := range src {
137 if 2*i+2 >= len(dst) {
138 return 2 * i, i, ErrShortDst
143 return 2 * len(src), len(src), nil
146 // rleDecode and rleEncode implement a toy run-length encoding: "aabbbbbbbbbb"
147 // is encoded as "2a10b". The decoding is assumed to not contain any numbers.
149 type rleDecode struct{ NopResetter }
151 func (rleDecode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
155 for i, c := range src {
156 if '0' <= c && c <= '9' {
157 n = 10*n + int(c-'0')
161 return nDst, nSrc, errors.New("rleDecode: bad input")
164 return nDst, nSrc, ErrShortDst
166 for j := 0; j < n; j++ {
169 dst, src = dst[n:], src[i+1:]
170 nDst, nSrc = nDst+n, nSrc+i+1
174 return nDst, nSrc, errors.New("rleDecode: bad input")
176 return nDst, nSrc, ErrShortSrc
178 return nDst, nSrc, nil
181 type rleEncode struct {
184 // allowStutter means that "xxxxxxxx" can be encoded as "5x3x"
185 // instead of always as "8x".
189 func (e rleEncode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
191 n, c0 := len(src), src[0]
192 for i, c := range src[1:] {
198 if n == len(src) && !atEOF && !e.allowStutter {
199 return nDst, nSrc, ErrShortSrc
202 if len(s) >= len(dst) {
203 return nDst, nSrc, ErrShortDst
207 dst, src = dst[len(s)+1:], src[n:]
208 nDst, nSrc = nDst+len(s)+1, nSrc+n
210 return nDst, nSrc, nil
213 // trickler consumes all input bytes, but writes a single byte at a time to dst.
216 func (t *trickler) Reset() {
220 func (t *trickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
221 *t = append(*t, src...)
226 return 0, len(src), ErrShortDst
233 return 1, len(src), err
236 // delayedTrickler is like trickler, but delays writing output to dst. This is
237 // highly unlikely to be relevant in practice, but it seems like a good idea
238 // to have some tolerance as long as progress can be detected.
239 type delayedTrickler []byte
241 func (t *delayedTrickler) Reset() {
245 func (t *delayedTrickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
246 if len(*t) > 0 && len(dst) > 0 {
251 *t = append(*t, src...)
255 return nDst, len(src), err
258 type testCase struct {
267 wantIter int // number of iterations taken; 0 means we don't care.
270 func (t testCase) String() string {
271 return tstr(t.t) + "; " + t.desc
274 func tstr(t Transformer) string {
275 if stringer, ok := t.(fmt.Stringer); ok {
276 return stringer.String()
278 s := fmt.Sprintf("%T", t)
279 return s[1+strings.Index(s, "."):]
282 func (c chain) String() string {
283 buf := &bytes.Buffer{}
284 buf.WriteString("Chain(")
285 for i, l := range c.link[:len(c.link)-1] {
287 fmt.Fprint(buf, ", ")
289 buf.WriteString(tstr(l.t))
295 var testCases = []testCase{
311 wantStr: "hello world.",
320 wantStr: "hello world.",
329 wantStr: "hello world.",
333 desc: "small buffers",
338 wantStr: "hello world.",
342 desc: "very small buffers",
347 wantStr: "hello world.",
351 desc: "small dst with lookahead",
352 t: lowerCaseASCIILookahead{},
356 wantStr: "hello world.",
360 desc: "small src with lookahead",
361 t: lowerCaseASCIILookahead{},
365 wantStr: "hello world.",
369 desc: "small buffers with lookahead",
370 t: lowerCaseASCIILookahead{},
374 wantStr: "hello world.",
378 desc: "very small buffers with lookahead",
379 t: lowerCaseASCIILookahead{},
383 wantStr: "hello world.",
389 src: "The First Rule of Transform Club: don't mention Mister X, ever.",
392 wantStr: "The First Rule of Transform Club: don't mention Mister ",
393 wantErr: errYouMentionedX,
397 desc: "user error at end",
399 src: "All goes well until it doesn't.",
402 wantStr: "All goes well until it doesn't.",
407 desc: "user error at end, incremental",
409 src: "All goes well until it doesn't.",
412 wantStr: "All goes well until it doesn't.",
417 desc: "replace entire non-empty string with one byte",
418 t: &replaceWithConstant{replacement: "X"},
419 src: "none of this will be copied",
426 desc: "replace entire empty string with one byte",
427 t: &replaceWithConstant{replacement: "X"},
435 desc: "replace entire empty string with seven bytes",
436 t: &replaceWithConstant{replacement: "ABCDEFG"},
444 desc: "add an X (initialBufSize-1)",
446 src: aaa[:initialBufSize-1],
449 wantStr: aaa[:initialBufSize-1] + "X",
453 desc: "add an X (initialBufSize+0)",
455 src: aaa[:initialBufSize+0],
458 wantStr: aaa[:initialBufSize+0] + "X",
462 desc: "add an X (initialBufSize+1)",
464 src: aaa[:initialBufSize+1],
467 wantStr: aaa[:initialBufSize+1] + "X",
471 desc: "small buffers",
473 src: "The First Rule of Transform Club: don't mention Mister X, ever.",
476 wantStr: "The First Rule of Transform Club: don't mention Mister ",
477 wantErr: errYouMentionedX,
481 desc: "very small buffers",
483 src: "The First Rule of Transform Club: don't mention Mister X, ever.",
486 wantStr: "The First Rule of Transform Club: don't mention Mister ",
487 wantErr: errYouMentionedX,
491 desc: "only transform at EOF",
502 src: "1a2b3c10d11e0f1g",
505 wantStr: "abbcccddddddddddeeeeeeeeeeeg",
511 src: "12a23b34c45d56e99z",
514 wantStr: strings.Repeat("a", 12) +
515 strings.Repeat("b", 23) +
516 strings.Repeat("c", 34) +
517 strings.Repeat("d", 45) +
518 strings.Repeat("e", 56) +
519 strings.Repeat("z", 99),
523 desc: "tight buffers",
525 src: "1a2b3c10d11e0f1g",
528 wantStr: "abbcccddddddddddeeeeeeeeeeeg",
534 src: "1a2b3c10d11e0f1g",
537 wantStr: "abbcccdddddddddd",
538 wantErr: ErrShortDst,
544 src: "1a2b3c10d11e0f1g",
549 wantErr: ErrShortSrc,
555 src: "abbcccddddddddddeeeeeeeeeeeg",
558 wantStr: "1a2b3c10d11e1g",
564 src: strings.Repeat("a", 12) +
565 strings.Repeat("b", 23) +
566 strings.Repeat("c", 34) +
567 strings.Repeat("d", 45) +
568 strings.Repeat("e", 56) +
569 strings.Repeat("z", 99),
572 wantStr: "12a23b34c45d56e99z",
576 desc: "tight buffers",
578 src: "abbcccddddddddddeeeeeeeeeeeg",
581 wantStr: "1a2b3c10d11e1g",
587 src: "abbcccddddddddddeeeeeeeeeeeg",
591 wantErr: ErrShortDst,
597 src: "abbcccddddddddddeeeeeeeeeeeg",
601 wantStr: "1a2b3c10d",
602 wantErr: ErrShortSrc,
606 desc: "allowStutter = false",
607 t: rleEncode{allowStutter: false},
608 src: "aaaabbbbbbbbccccddddd",
615 desc: "allowStutter = true",
616 t: rleEncode{allowStutter: true},
617 src: "aaaabbbbbbbbccccddddd",
621 wantStr: "4a6b2b4c4d1d",
627 src: "abcdefghijklm",
630 wantStr: "abcdefghijklm",
634 desc: "delayedTrickler",
635 t: &delayedTrickler{},
636 src: "abcdefghijklm",
639 wantStr: "abcdefghijklm",
643 func TestReader(t *testing.T) {
644 for _, tc := range testCases {
645 testtext.Run(t, tc.desc, func(t *testing.T) {
646 r := NewReader(strings.NewReader(tc.src), tc.t)
647 // Differently sized dst and src buffers are not part of the
648 // exported API. We override them manually.
649 r.dst = make([]byte, tc.dstSize)
650 r.src = make([]byte, tc.srcSize)
651 got, err := ioutil.ReadAll(r)
653 if str != tc.wantStr || err != tc.wantErr {
654 t.Errorf("\ngot %q, %v\nwant %q, %v", str, err, tc.wantStr, tc.wantErr)
660 func TestWriter(t *testing.T) {
661 tests := append(testCases, chainTests()...)
662 for _, tc := range tests {
663 sizes := []int{1, 2, 3, 4, 5, 10, 100, 1000}
665 sizes = []int{tc.ioSize}
667 for _, sz := range sizes {
668 testtext.Run(t, fmt.Sprintf("%s/%d", tc.desc, sz), func(t *testing.T) {
669 bb := &bytes.Buffer{}
670 w := NewWriter(bb, tc.t)
671 // Differently sized dst and src buffers are not part of the
672 // exported API. We override them manually.
673 w.dst = make([]byte, tc.dstSize)
674 w.src = make([]byte, tc.srcSize)
675 src := make([]byte, sz)
677 for b := tc.src; len(b) > 0 && err == nil; {
681 m, err = w.Write(src[:n])
682 if m != n && err == nil {
683 t.Errorf("did not consume all bytes %d < %d", m, n)
690 if str != tc.wantStr || err != tc.wantErr {
691 t.Errorf("\ngot %q, %v\nwant %q, %v", str, err, tc.wantStr, tc.wantErr)
698 func TestNop(t *testing.T) {
699 testCases := []struct {
706 {"a", 0, ErrShortDst},
710 for i, tc := range testCases {
711 dst := make([]byte, tc.dstSize)
712 nDst, nSrc, err := Nop.Transform(dst, []byte(tc.str), true)
714 if tc.dstSize < len(want) {
715 want = want[:tc.dstSize]
717 if got := string(dst[:nDst]); got != want || err != tc.err || nSrc != nDst {
718 t.Errorf("%d:\ngot %q, %d, %v\nwant %q, %d, %v", i, got, nSrc, err, want, nDst, tc.err)
723 func TestDiscard(t *testing.T) {
724 testCases := []struct {
733 for i, tc := range testCases {
734 nDst, nSrc, err := Discard.Transform(make([]byte, tc.dstSize), []byte(tc.str), true)
735 if nDst != 0 || nSrc != len(tc.str) || err != nil {
736 t.Errorf("%d:\ngot %q, %d, %v\nwant 0, %d, nil", i, nDst, nSrc, err, len(tc.str))
741 // mkChain creates a Chain transformer. x must be alternating between transformer
742 // and bufSize, like T, (sz, T)*
743 func mkChain(x ...interface{}) *chain {
745 for i := 0; i < len(x); i += 2 {
746 t = append(t, x[i].(Transformer))
748 c := Chain(t...).(*chain)
749 for i, j := 1, 1; i < len(x); i, j = i+2, j+1 {
750 c.link[j].b = make([]byte, x[i].(int))
755 func chainTests() []testCase {
759 t: mkChain(rleEncode{}, 100, lowerCaseASCII{}),
769 desc: "short dst buffer",
770 t: mkChain(lowerCaseASCII{}, 3, rleDecode{}),
771 src: "1a2b3c10d11e0f1g",
774 wantStr: "abbcccdddddddddd",
775 wantErr: ErrShortDst,
779 desc: "short internal dst buffer",
780 t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop),
781 src: "1a2b3c10d11e0f1g",
784 wantStr: "abbcccdddddddddd",
785 wantErr: errShortInternal,
789 desc: "short internal dst buffer from input",
790 t: mkChain(rleDecode{}, 10, Nop),
791 src: "1a2b3c10d11e0f1g",
794 wantStr: "abbcccdddddddddd",
795 wantErr: errShortInternal,
799 desc: "empty short internal dst buffer",
800 t: mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop),
804 wantStr: "aaaabbbbbbb",
805 wantErr: errShortInternal,
809 desc: "empty short internal dst buffer from input",
810 t: mkChain(rleDecode{}, 10, Nop),
814 wantStr: "aaaabbbbbbb",
815 wantErr: errShortInternal,
819 desc: "short internal src buffer after full dst buffer",
820 t: mkChain(Nop, 5, rleEncode{}, 10, Nop),
825 wantErr: errShortInternal,
830 desc: "short internal src buffer after short dst buffer; test lastFull",
831 t: mkChain(rleDecode{}, 5, rleEncode{}, 4, Nop),
836 wantErr: errShortInternal,
840 desc: "short internal src buffer after successful complete fill",
841 t: mkChain(Nop, 3, rleDecode{}),
846 wantErr: errShortInternal,
851 desc: "short internal src buffer after short dst buffer; test lastFull",
852 t: mkChain(rleDecode{}, 5, rleEncode{}),
857 wantErr: errShortInternal,
861 desc: "short src buffer",
862 t: mkChain(rleEncode{}, 5, Nop),
863 src: "abbcccddddeeeee",
868 wantErr: ErrShortSrc,
872 desc: "process all in one go",
873 t: mkChain(rleEncode{}, 5, Nop),
874 src: "abbcccddddeeeeeffffff",
877 wantStr: "1a2b3c4d5e6f",
883 desc: "complete processing downstream after error",
884 t: mkChain(dontMentionX{}, 2, rleDecode{}, 5, Nop),
889 wantStr: "aaabbbbeeeee",
890 wantErr: errYouMentionedX,
894 desc: "return downstream fatal errors first (followed by short dst)",
895 t: mkChain(dontMentionX{}, 8, rleDecode{}, 4, Nop),
901 wantErr: errShortInternal,
905 desc: "return downstream fatal errors first (followed by short src)",
906 t: mkChain(dontMentionX{}, 5, Nop, 1, rleDecode{}),
912 wantErr: errShortInternal,
916 desc: "short internal",
917 t: mkChain(Nop, 11, rleEncode{}, 3, Nop),
918 src: "abbcccddddddddddeeeeeeeeeeeg",
921 wantStr: "1a2b3c10d",
922 wantErr: errShortInternal,
927 func doTransform(tc testCase) (res string, iter int, err error) {
929 dst := make([]byte, tc.dstSize)
930 out, in := make([]byte, 0, 2*len(tc.src)), []byte(tc.src)
933 src, atEOF := in, true
934 if len(src) > tc.srcSize {
935 src, atEOF = src[:tc.srcSize], false
937 nDst, nSrc, err := tc.t.Transform(dst, src, atEOF)
938 out = append(out, dst[:nDst]...)
941 case err == nil && len(in) != 0:
942 case err == ErrShortSrc && nSrc > 0:
943 case err == ErrShortDst && (nDst > 0 || nSrc > 0):
945 return string(out), iter, err
950 func TestChain(t *testing.T) {
951 if c, ok := Chain().(nop); !ok {
952 t.Errorf("empty chain: %v; want Nop", c)
955 // Test Chain for a single Transformer.
956 for _, tc := range testCases {
958 str, _, err := doTransform(tc)
959 if str != tc.wantStr || err != tc.wantErr {
960 t.Errorf("%s:\ngot %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr)
964 tests := chainTests()
965 sizes := []int{1, 2, 3, 4, 5, 7, 10, 100, 1000}
966 addTest := func(tc testCase, t *chain) {
967 if t.link[0].t != tc.t && tc.wantErr == ErrShortSrc {
968 tc.wantErr = errShortInternal
970 if t.link[len(t.link)-2].t != tc.t && tc.wantErr == ErrShortDst {
971 tc.wantErr = errShortInternal
974 tests = append(tests, tc)
976 for _, tc := range testCases {
977 for _, sz := range sizes {
980 addTest(tt, mkChain(tc.t, tc.dstSize, Nop))
981 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 2, Nop))
982 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop))
983 if sz >= tc.dstSize && (tc.wantErr != ErrShortDst || sz == tc.dstSize) {
984 addTest(tt, mkChain(Nop, tc.srcSize, tc.t))
985 addTest(tt, mkChain(Nop, 100, Nop, tc.srcSize, tc.t))
989 for _, tc := range testCases {
993 addTest(tt, mkChain(tc.t, tc.dstSize, Discard))
994 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Discard))
995 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, tc.dstSize, Discard))
997 for _, tc := range testCases {
1000 tt.wantStr = strings.Replace(tc.src, "0f", "", -1)
1001 // Chain encoders and decoders.
1002 if _, ok := tc.t.(rleEncode); ok && tc.wantErr == nil {
1003 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 1000, rleDecode{}))
1004 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, tc.dstSize, rleDecode{}))
1005 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{}))
1006 // decoding needs larger destinations
1007 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, rleDecode{}, 100, Nop))
1008 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleDecode{}, 100, Nop))
1009 } else if _, ok := tc.t.(rleDecode); ok && tc.wantErr == nil {
1010 // The internal buffer size may need to be the sum of the maximum segment
1011 // size of the two encoders!
1012 addTest(tt, mkChain(tc.t, 2*tc.dstSize, rleEncode{}))
1013 addTest(tt, mkChain(tc.t, tc.dstSize, Nop, 101, rleEncode{}))
1014 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 100, rleEncode{}))
1015 addTest(tt, mkChain(Nop, tc.srcSize, tc.t, tc.dstSize, Nop, 200, rleEncode{}, 100, Nop))
1018 for _, tc := range tests {
1019 str, iter, err := doTransform(tc)
1020 mi := tc.wantIter != 0 && tc.wantIter != iter
1021 if str != tc.wantStr || err != tc.wantErr || mi {
1022 t.Errorf("%s:\ngot iter:%d, %q, %v\nwant iter:%d, %q, %v", tc, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr)
1028 func TestRemoveFunc(t *testing.T) {
1029 filter := RemoveFunc(func(r rune) bool {
1030 return strings.IndexRune("ab\u0300\u1234,", r) != -1
1032 tests := []testCase{
1054 src: ",до,свидания,",
1055 wantStr: "досвидания",
1059 src: "a\xbd\xb2=\xbc ⌘",
1060 wantStr: "\uFFFD\uFFFD=\uFFFD ⌘",
1064 // If we didn't replace illegal bytes with RuneError, the result
1065 // would be \u0300 or the code would need to be more complex.
1066 src: "\xcc\u0300\x80",
1067 wantStr: "\uFFFD\uFFFD",
1071 src: "\xcc\u0300\x80",
1073 wantStr: "\uFFFD\uFFFD",
1078 // Test a long buffer greater than the internal buffer size
1079 src: "hello\xcc\xcc\xccworld",
1081 wantStr: "hello\uFFFD\uFFFD\uFFFDworld",
1089 wantErr: ErrShortDst,
1096 wantErr: ErrShortDst,
1104 wantErr: ErrShortSrc,
1108 t: RemoveFunc(func(r rune) bool {
1109 return r == utf8.RuneError
1111 src: "\xcc\u0300\x80",
1116 for _, tc := range tests {
1121 if tc.dstSize == 0 {
1124 if tc.srcSize == 0 {
1127 str, iter, err := doTransform(tc)
1128 mi := tc.wantIter != 0 && tc.wantIter != iter
1129 if str != tc.wantStr || err != tc.wantErr || mi {
1130 t.Errorf("%+q:\ngot iter:%d, %+q, %v\nwant iter:%d, %+q, %v", tc.src, iter, str, err, tc.wantIter, tc.wantStr, tc.wantErr)
1134 idem, _, _ := doTransform(tc)
1136 t.Errorf("%+q: found %+q; want %+q", tc.src, idem, str)
1141 func testString(t *testing.T, f func(Transformer, string) (string, int, error)) {
1142 for _, tt := range append(testCases, chainTests()...) {
1143 if tt.desc == "allowStutter = true" {
1144 // We don't have control over the buffer size, so we eliminate tests
1145 // that depend on a specific buffer size being set.
1148 if tt.wantErr == ErrShortDst || tt.wantErr == ErrShortSrc {
1149 // The result string will be different.
1152 testtext.Run(t, tt.desc, func(t *testing.T) {
1153 got, n, err := f(tt.t, tt.src)
1154 if tt.wantErr != err {
1155 t.Errorf("error: got %v; want %v", err, tt.wantErr)
1157 // Check that err == nil implies that n == len(tt.src). Note that vice
1158 // versa isn't necessarily true.
1159 if err == nil && n != len(tt.src) {
1160 t.Errorf("err == nil: got %d bytes, want %d", n, err)
1162 if got != tt.wantStr {
1163 t.Errorf("string: got %q; want %q", got, tt.wantStr)
1169 func TestBytes(t *testing.T) {
1170 testString(t, func(z Transformer, s string) (string, int, error) {
1171 b, n, err := Bytes(z, []byte(s))
1172 return string(b), n, err
1176 func TestAppend(t *testing.T) {
1177 // Create a bunch of subtests for different buffer sizes.
1178 testCases := [][]byte{
1184 make([]byte, 100, 100),
1185 make([]byte, 100, 200),
1187 for _, tc := range testCases {
1188 testString(t, func(z Transformer, s string) (string, int, error) {
1189 b, n, err := Append(z, tc, []byte(s))
1190 return string(b[len(tc):]), n, err
1195 func TestString(t *testing.T) {
1196 testtext.Run(t, "transform", func(t *testing.T) { testString(t, String) })
1198 // Overrun the internal destination buffer.
1199 for i, s := range []string{
1200 aaa[:1*initialBufSize-1],
1201 aaa[:1*initialBufSize+0],
1202 aaa[:1*initialBufSize+1],
1203 AAA[:1*initialBufSize-1],
1204 AAA[:1*initialBufSize+0],
1205 AAA[:1*initialBufSize+1],
1206 AAA[:2*initialBufSize-1],
1207 AAA[:2*initialBufSize+0],
1208 AAA[:2*initialBufSize+1],
1209 aaa[:1*initialBufSize-2] + "A",
1210 aaa[:1*initialBufSize-1] + "A",
1211 aaa[:1*initialBufSize+0] + "A",
1212 aaa[:1*initialBufSize+1] + "A",
1214 testtext.Run(t, fmt.Sprint("dst buffer test using lower/", i), func(t *testing.T) {
1215 got, _, _ := String(lowerCaseASCII{}, s)
1216 if want := strings.ToLower(s); got != want {
1217 t.Errorf("got %s (%d); want %s (%d)", got, len(got), want, len(want))
1222 // Overrun the internal source buffer.
1223 for i, s := range []string{
1224 aaa[:1*initialBufSize-1],
1225 aaa[:1*initialBufSize+0],
1226 aaa[:1*initialBufSize+1],
1227 aaa[:2*initialBufSize+1],
1228 aaa[:2*initialBufSize+0],
1229 aaa[:2*initialBufSize+1],
1231 testtext.Run(t, fmt.Sprint("src buffer test using rleEncode/", i), func(t *testing.T) {
1232 got, _, _ := String(rleEncode{}, s)
1233 if want := fmt.Sprintf("%da", len(s)); got != want {
1234 t.Errorf("got %s (%d); want %s (%d)", got, len(got), want, len(want))
1239 // Test allocations for non-changing strings.
1240 // Note we still need to allocate a single buffer.
1241 for i, s := range []string{
1244 aaa[:initialBufSize-1],
1245 aaa[:initialBufSize+0],
1246 aaa[:initialBufSize+1],
1247 aaa[:10*initialBufSize],
1249 testtext.Run(t, fmt.Sprint("alloc/", i), func(t *testing.T) {
1250 if n := testtext.AllocsPerRun(5, func() { String(&lowerCaseASCIILookahead{}, s) }); n > 1 {
1251 t.Errorf("#allocs was %f; want 1", n)
1257 // TestBytesAllocation tests that buffer growth stays limited with the trickler
1258 // transformer, which behaves oddly but within spec. In case buffer growth is
1259 // not correctly handled, the test will either panic with a failed allocation or
1260 // thrash. To ensure the tests terminate under the last condition, we time out
1261 // after some sufficiently long period of time.
1262 func TestBytesAllocation(t *testing.T) {
1263 done := make(chan bool)
1265 in := bytes.Repeat([]byte{'a'}, 1000)
1266 tr := trickler(make([]byte, 1))
1272 case <-time.After(3 * time.Second):
1273 t.Error("time out, likely due to excessive allocation")
1277 // TestStringAllocation tests that buffer growth stays limited with the trickler
1278 // transformer, which behaves oddly but within spec. In case buffer growth is
1279 // not correctly handled, the test will either panic with a failed allocation or
1280 // thrash. To ensure the tests terminate under the last condition, we time out
1281 // after some sufficiently long period of time.
1282 func TestStringAllocation(t *testing.T) {
1283 done := make(chan bool)
1285 tr := trickler(make([]byte, 1))
1286 String(&tr, aaa[:1000])
1291 case <-time.After(3 * time.Second):
1292 t.Error("time out, likely due to excessive allocation")
1296 func BenchmarkStringLowerEmpty(b *testing.B) {
1297 for i := 0; i < b.N; i++ {
1298 String(&lowerCaseASCIILookahead{}, "")
1302 func BenchmarkStringLowerIdentical(b *testing.B) {
1303 for i := 0; i < b.N; i++ {
1304 String(&lowerCaseASCIILookahead{}, aaa[:4096])
1308 func BenchmarkStringLowerChanged(b *testing.B) {
1309 for i := 0; i < b.N; i++ {
1310 String(&lowerCaseASCIILookahead{}, AAA[:4096])
1315 aaa = strings.Repeat("a", 4096)
1316 AAA = strings.Repeat("A", 4096)