github.com/go-enjin/golang-org-x-text@v0.12.1-enjin.2/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"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  	"time"
    16  	"unicode/utf8"
    17  
    18  	"github.com/go-enjin/golang-org-x-text/internal/testtext"
    19  )
    20  
    21  type lowerCaseASCII struct{ NopResetter }
    22  
    23  func (lowerCaseASCII) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    24  	n := len(src)
    25  	if n > len(dst) {
    26  		n, err = len(dst), ErrShortDst
    27  	}
    28  	for i, c := range src[:n] {
    29  		if 'A' <= c && c <= 'Z' {
    30  			c += 'a' - 'A'
    31  		}
    32  		dst[i] = c
    33  	}
    34  	return n, n, err
    35  }
    36  
    37  // lowerCaseASCIILookahead lowercases the string and reports ErrShortSrc as long
    38  // as the input is not atEOF.
    39  type lowerCaseASCIILookahead struct{ NopResetter }
    40  
    41  func (lowerCaseASCIILookahead) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    42  	n := len(src)
    43  	if n > len(dst) {
    44  		n, err = len(dst), ErrShortDst
    45  	}
    46  	for i, c := range src[:n] {
    47  		if 'A' <= c && c <= 'Z' {
    48  			c += 'a' - 'A'
    49  		}
    50  		dst[i] = c
    51  	}
    52  	if !atEOF {
    53  		err = ErrShortSrc
    54  	}
    55  	return n, n, err
    56  }
    57  
    58  var errYouMentionedX = errors.New("you mentioned X")
    59  
    60  type dontMentionX struct{ NopResetter }
    61  
    62  func (dontMentionX) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    63  	n := len(src)
    64  	if n > len(dst) {
    65  		n, err = len(dst), ErrShortDst
    66  	}
    67  	for i, c := range src[:n] {
    68  		if c == 'X' {
    69  			return i, i, errYouMentionedX
    70  		}
    71  		dst[i] = c
    72  	}
    73  	return n, n, err
    74  }
    75  
    76  var errAtEnd = errors.New("error after all text")
    77  
    78  type errorAtEnd struct{ NopResetter }
    79  
    80  func (errorAtEnd) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    81  	n := copy(dst, src)
    82  	if n < len(src) {
    83  		return n, n, ErrShortDst
    84  	}
    85  	if atEOF {
    86  		return n, n, errAtEnd
    87  	}
    88  	return n, n, nil
    89  }
    90  
    91  type replaceWithConstant struct {
    92  	replacement string
    93  	written     int
    94  }
    95  
    96  func (t *replaceWithConstant) Reset() {
    97  	t.written = 0
    98  }
    99  
   100  func (t *replaceWithConstant) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   101  	if atEOF {
   102  		nDst = copy(dst, t.replacement[t.written:])
   103  		t.written += nDst
   104  		if t.written < len(t.replacement) {
   105  			err = ErrShortDst
   106  		}
   107  	}
   108  	return nDst, len(src), err
   109  }
   110  
   111  type addAnXAtTheEnd struct{ NopResetter }
   112  
   113  func (addAnXAtTheEnd) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   114  	n := copy(dst, src)
   115  	if n < len(src) {
   116  		return n, n, ErrShortDst
   117  	}
   118  	if !atEOF {
   119  		return n, n, nil
   120  	}
   121  	if len(dst) == n {
   122  		return n, n, ErrShortDst
   123  	}
   124  	dst[n] = 'X'
   125  	return n + 1, n, nil
   126  }
   127  
   128  // doublerAtEOF is a strange Transformer that transforms "this" to "tthhiiss",
   129  // but only if atEOF is true.
   130  type doublerAtEOF struct{ NopResetter }
   131  
   132  func (doublerAtEOF) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   133  	if !atEOF {
   134  		return 0, 0, ErrShortSrc
   135  	}
   136  	for i, c := range src {
   137  		if 2*i+2 >= len(dst) {
   138  			return 2 * i, i, ErrShortDst
   139  		}
   140  		dst[2*i+0] = c
   141  		dst[2*i+1] = c
   142  	}
   143  	return 2 * len(src), len(src), nil
   144  }
   145  
   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.
   148  
   149  type rleDecode struct{ NopResetter }
   150  
   151  func (rleDecode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   152  loop:
   153  	for len(src) > 0 {
   154  		n := 0
   155  		for i, c := range src {
   156  			if '0' <= c && c <= '9' {
   157  				n = 10*n + int(c-'0')
   158  				continue
   159  			}
   160  			if i == 0 {
   161  				return nDst, nSrc, errors.New("rleDecode: bad input")
   162  			}
   163  			if n > len(dst) {
   164  				return nDst, nSrc, ErrShortDst
   165  			}
   166  			for j := 0; j < n; j++ {
   167  				dst[j] = c
   168  			}
   169  			dst, src = dst[n:], src[i+1:]
   170  			nDst, nSrc = nDst+n, nSrc+i+1
   171  			continue loop
   172  		}
   173  		if atEOF {
   174  			return nDst, nSrc, errors.New("rleDecode: bad input")
   175  		}
   176  		return nDst, nSrc, ErrShortSrc
   177  	}
   178  	return nDst, nSrc, nil
   179  }
   180  
   181  type rleEncode struct {
   182  	NopResetter
   183  
   184  	// allowStutter means that "xxxxxxxx" can be encoded as "5x3x"
   185  	// instead of always as "8x".
   186  	allowStutter bool
   187  }
   188  
   189  func (e rleEncode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   190  	for len(src) > 0 {
   191  		n, c0 := len(src), src[0]
   192  		for i, c := range src[1:] {
   193  			if c != c0 {
   194  				n = i + 1
   195  				break
   196  			}
   197  		}
   198  		if n == len(src) && !atEOF && !e.allowStutter {
   199  			return nDst, nSrc, ErrShortSrc
   200  		}
   201  		s := strconv.Itoa(n)
   202  		if len(s) >= len(dst) {
   203  			return nDst, nSrc, ErrShortDst
   204  		}
   205  		copy(dst, s)
   206  		dst[len(s)] = c0
   207  		dst, src = dst[len(s)+1:], src[n:]
   208  		nDst, nSrc = nDst+len(s)+1, nSrc+n
   209  	}
   210  	return nDst, nSrc, nil
   211  }
   212  
   213  // trickler consumes all input bytes, but writes a single byte at a time to dst.
   214  type trickler []byte
   215  
   216  func (t *trickler) Reset() {
   217  	*t = nil
   218  }
   219  
   220  func (t *trickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   221  	*t = append(*t, src...)
   222  	if len(*t) == 0 {
   223  		return 0, 0, nil
   224  	}
   225  	if len(dst) == 0 {
   226  		return 0, len(src), ErrShortDst
   227  	}
   228  	dst[0] = (*t)[0]
   229  	*t = (*t)[1:]
   230  	if len(*t) > 0 {
   231  		err = ErrShortDst
   232  	}
   233  	return 1, len(src), err
   234  }
   235  
   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
   240  
   241  func (t *delayedTrickler) Reset() {
   242  	*t = nil
   243  }
   244  
   245  func (t *delayedTrickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
   246  	if len(*t) > 0 && len(dst) > 0 {
   247  		dst[0] = (*t)[0]
   248  		*t = (*t)[1:]
   249  		nDst = 1
   250  	}
   251  	*t = append(*t, src...)
   252  	if len(*t) > 0 {
   253  		err = ErrShortDst
   254  	}
   255  	return nDst, len(src), err
   256  }
   257  
   258  type testCase struct {
   259  	desc     string
   260  	t        Transformer
   261  	src      string
   262  	dstSize  int
   263  	srcSize  int
   264  	ioSize   int
   265  	wantStr  string
   266  	wantErr  error
   267  	wantIter int // number of iterations taken; 0 means we don't care.
   268  }
   269  
   270  func (t testCase) String() string {
   271  	return tstr(t.t) + "; " + t.desc
   272  }
   273  
   274  func tstr(t Transformer) string {
   275  	if stringer, ok := t.(fmt.Stringer); ok {
   276  		return stringer.String()
   277  	}
   278  	s := fmt.Sprintf("%T", t)
   279  	return s[1+strings.Index(s, "."):]
   280  }
   281  
   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] {
   286  		if i != 0 {
   287  			fmt.Fprint(buf, ", ")
   288  		}
   289  		buf.WriteString(tstr(l.t))
   290  	}
   291  	buf.WriteString(")")
   292  	return buf.String()
   293  }
   294  
   295  var testCases = []testCase{
   296  	{
   297  		desc:    "empty",
   298  		t:       lowerCaseASCII{},
   299  		src:     "",
   300  		dstSize: 100,
   301  		srcSize: 100,
   302  		wantStr: "",
   303  	},
   304  
   305  	{
   306  		desc:    "basic",
   307  		t:       lowerCaseASCII{},
   308  		src:     "Hello WORLD.",
   309  		dstSize: 100,
   310  		srcSize: 100,
   311  		wantStr: "hello world.",
   312  	},
   313  
   314  	{
   315  		desc:    "small dst",
   316  		t:       lowerCaseASCII{},
   317  		src:     "Hello WORLD.",
   318  		dstSize: 3,
   319  		srcSize: 100,
   320  		wantStr: "hello world.",
   321  	},
   322  
   323  	{
   324  		desc:    "small src",
   325  		t:       lowerCaseASCII{},
   326  		src:     "Hello WORLD.",
   327  		dstSize: 100,
   328  		srcSize: 4,
   329  		wantStr: "hello world.",
   330  	},
   331  
   332  	{
   333  		desc:    "small buffers",
   334  		t:       lowerCaseASCII{},
   335  		src:     "Hello WORLD.",
   336  		dstSize: 3,
   337  		srcSize: 4,
   338  		wantStr: "hello world.",
   339  	},
   340  
   341  	{
   342  		desc:    "very small buffers",
   343  		t:       lowerCaseASCII{},
   344  		src:     "Hello WORLD.",
   345  		dstSize: 1,
   346  		srcSize: 1,
   347  		wantStr: "hello world.",
   348  	},
   349  
   350  	{
   351  		desc:    "small dst with lookahead",
   352  		t:       lowerCaseASCIILookahead{},
   353  		src:     "Hello WORLD.",
   354  		dstSize: 3,
   355  		srcSize: 100,
   356  		wantStr: "hello world.",
   357  	},
   358  
   359  	{
   360  		desc:    "small src with lookahead",
   361  		t:       lowerCaseASCIILookahead{},
   362  		src:     "Hello WORLD.",
   363  		dstSize: 100,
   364  		srcSize: 4,
   365  		wantStr: "hello world.",
   366  	},
   367  
   368  	{
   369  		desc:    "small buffers with lookahead",
   370  		t:       lowerCaseASCIILookahead{},
   371  		src:     "Hello WORLD.",
   372  		dstSize: 3,
   373  		srcSize: 4,
   374  		wantStr: "hello world.",
   375  	},
   376  
   377  	{
   378  		desc:    "very small buffers with lookahead",
   379  		t:       lowerCaseASCIILookahead{},
   380  		src:     "Hello WORLD.",
   381  		dstSize: 1,
   382  		srcSize: 2,
   383  		wantStr: "hello world.",
   384  	},
   385  
   386  	{
   387  		desc:    "user error",
   388  		t:       dontMentionX{},
   389  		src:     "The First Rule of Transform Club: don't mention Mister X, ever.",
   390  		dstSize: 100,
   391  		srcSize: 100,
   392  		wantStr: "The First Rule of Transform Club: don't mention Mister ",
   393  		wantErr: errYouMentionedX,
   394  	},
   395  
   396  	{
   397  		desc:    "user error at end",
   398  		t:       errorAtEnd{},
   399  		src:     "All goes well until it doesn't.",
   400  		dstSize: 100,
   401  		srcSize: 100,
   402  		wantStr: "All goes well until it doesn't.",
   403  		wantErr: errAtEnd,
   404  	},
   405  
   406  	{
   407  		desc:    "user error at end, incremental",
   408  		t:       errorAtEnd{},
   409  		src:     "All goes well until it doesn't.",
   410  		dstSize: 10,
   411  		srcSize: 10,
   412  		wantStr: "All goes well until it doesn't.",
   413  		wantErr: errAtEnd,
   414  	},
   415  
   416  	{
   417  		desc:    "replace entire non-empty string with one byte",
   418  		t:       &replaceWithConstant{replacement: "X"},
   419  		src:     "none of this will be copied",
   420  		dstSize: 1,
   421  		srcSize: 10,
   422  		wantStr: "X",
   423  	},
   424  
   425  	{
   426  		desc:    "replace entire empty string with one byte",
   427  		t:       &replaceWithConstant{replacement: "X"},
   428  		src:     "",
   429  		dstSize: 1,
   430  		srcSize: 10,
   431  		wantStr: "X",
   432  	},
   433  
   434  	{
   435  		desc:    "replace entire empty string with seven bytes",
   436  		t:       &replaceWithConstant{replacement: "ABCDEFG"},
   437  		src:     "",
   438  		dstSize: 3,
   439  		srcSize: 10,
   440  		wantStr: "ABCDEFG",
   441  	},
   442  
   443  	{
   444  		desc:    "add an X (initialBufSize-1)",
   445  		t:       addAnXAtTheEnd{},
   446  		src:     aaa[:initialBufSize-1],
   447  		dstSize: 10,
   448  		srcSize: 10,
   449  		wantStr: aaa[:initialBufSize-1] + "X",
   450  	},
   451  
   452  	{
   453  		desc:    "add an X (initialBufSize+0)",
   454  		t:       addAnXAtTheEnd{},
   455  		src:     aaa[:initialBufSize+0],
   456  		dstSize: 10,
   457  		srcSize: 10,
   458  		wantStr: aaa[:initialBufSize+0] + "X",
   459  	},
   460  
   461  	{
   462  		desc:    "add an X (initialBufSize+1)",
   463  		t:       addAnXAtTheEnd{},
   464  		src:     aaa[:initialBufSize+1],
   465  		dstSize: 10,
   466  		srcSize: 10,
   467  		wantStr: aaa[:initialBufSize+1] + "X",
   468  	},
   469  
   470  	{
   471  		desc:    "small buffers",
   472  		t:       dontMentionX{},
   473  		src:     "The First Rule of Transform Club: don't mention Mister X, ever.",
   474  		dstSize: 10,
   475  		srcSize: 10,
   476  		wantStr: "The First Rule of Transform Club: don't mention Mister ",
   477  		wantErr: errYouMentionedX,
   478  	},
   479  
   480  	{
   481  		desc:    "very small buffers",
   482  		t:       dontMentionX{},
   483  		src:     "The First Rule of Transform Club: don't mention Mister X, ever.",
   484  		dstSize: 1,
   485  		srcSize: 1,
   486  		wantStr: "The First Rule of Transform Club: don't mention Mister ",
   487  		wantErr: errYouMentionedX,
   488  	},
   489  
   490  	{
   491  		desc:    "only transform at EOF",
   492  		t:       doublerAtEOF{},
   493  		src:     "this",
   494  		dstSize: 100,
   495  		srcSize: 100,
   496  		wantStr: "tthhiiss",
   497  	},
   498  
   499  	{
   500  		desc:    "basic",
   501  		t:       rleDecode{},
   502  		src:     "1a2b3c10d11e0f1g",
   503  		dstSize: 100,
   504  		srcSize: 100,
   505  		wantStr: "abbcccddddddddddeeeeeeeeeeeg",
   506  	},
   507  
   508  	{
   509  		desc:    "long",
   510  		t:       rleDecode{},
   511  		src:     "12a23b34c45d56e99z",
   512  		dstSize: 100,
   513  		srcSize: 100,
   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),
   520  	},
   521  
   522  	{
   523  		desc:    "tight buffers",
   524  		t:       rleDecode{},
   525  		src:     "1a2b3c10d11e0f1g",
   526  		dstSize: 11,
   527  		srcSize: 3,
   528  		wantStr: "abbcccddddddddddeeeeeeeeeeeg",
   529  	},
   530  
   531  	{
   532  		desc:    "short dst",
   533  		t:       rleDecode{},
   534  		src:     "1a2b3c10d11e0f1g",
   535  		dstSize: 10,
   536  		srcSize: 3,
   537  		wantStr: "abbcccdddddddddd",
   538  		wantErr: ErrShortDst,
   539  	},
   540  
   541  	{
   542  		desc:    "short src",
   543  		t:       rleDecode{},
   544  		src:     "1a2b3c10d11e0f1g",
   545  		dstSize: 11,
   546  		srcSize: 2,
   547  		ioSize:  2,
   548  		wantStr: "abbccc",
   549  		wantErr: ErrShortSrc,
   550  	},
   551  
   552  	{
   553  		desc:    "basic",
   554  		t:       rleEncode{},
   555  		src:     "abbcccddddddddddeeeeeeeeeeeg",
   556  		dstSize: 100,
   557  		srcSize: 100,
   558  		wantStr: "1a2b3c10d11e1g",
   559  	},
   560  
   561  	{
   562  		desc: "long",
   563  		t:    rleEncode{},
   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),
   570  		dstSize: 100,
   571  		srcSize: 100,
   572  		wantStr: "12a23b34c45d56e99z",
   573  	},
   574  
   575  	{
   576  		desc:    "tight buffers",
   577  		t:       rleEncode{},
   578  		src:     "abbcccddddddddddeeeeeeeeeeeg",
   579  		dstSize: 3,
   580  		srcSize: 12,
   581  		wantStr: "1a2b3c10d11e1g",
   582  	},
   583  
   584  	{
   585  		desc:    "short dst",
   586  		t:       rleEncode{},
   587  		src:     "abbcccddddddddddeeeeeeeeeeeg",
   588  		dstSize: 2,
   589  		srcSize: 12,
   590  		wantStr: "1a2b3c",
   591  		wantErr: ErrShortDst,
   592  	},
   593  
   594  	{
   595  		desc:    "short src",
   596  		t:       rleEncode{},
   597  		src:     "abbcccddddddddddeeeeeeeeeeeg",
   598  		dstSize: 3,
   599  		srcSize: 11,
   600  		ioSize:  11,
   601  		wantStr: "1a2b3c10d",
   602  		wantErr: ErrShortSrc,
   603  	},
   604  
   605  	{
   606  		desc:    "allowStutter = false",
   607  		t:       rleEncode{allowStutter: false},
   608  		src:     "aaaabbbbbbbbccccddddd",
   609  		dstSize: 10,
   610  		srcSize: 10,
   611  		wantStr: "4a8b4c5d",
   612  	},
   613  
   614  	{
   615  		desc:    "allowStutter = true",
   616  		t:       rleEncode{allowStutter: true},
   617  		src:     "aaaabbbbbbbbccccddddd",
   618  		dstSize: 10,
   619  		srcSize: 10,
   620  		ioSize:  10,
   621  		wantStr: "4a6b2b4c4d1d",
   622  	},
   623  
   624  	{
   625  		desc:    "trickler",
   626  		t:       &trickler{},
   627  		src:     "abcdefghijklm",
   628  		dstSize: 3,
   629  		srcSize: 15,
   630  		wantStr: "abcdefghijklm",
   631  	},
   632  
   633  	{
   634  		desc:    "delayedTrickler",
   635  		t:       &delayedTrickler{},
   636  		src:     "abcdefghijklm",
   637  		dstSize: 3,
   638  		srcSize: 15,
   639  		wantStr: "abcdefghijklm",
   640  	},
   641  }
   642  
   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 := io.ReadAll(r)
   652  			str := string(got)
   653  			if str != tc.wantStr || err != tc.wantErr {
   654  				t.Errorf("\ngot  %q, %v\nwant %q, %v", str, err, tc.wantStr, tc.wantErr)
   655  			}
   656  		})
   657  	}
   658  }
   659  
   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}
   664  		if tc.ioSize > 0 {
   665  			sizes = []int{tc.ioSize}
   666  		}
   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)
   676  				var err error
   677  				for b := tc.src; len(b) > 0 && err == nil; {
   678  					n := copy(src, b)
   679  					b = b[n:]
   680  					m := 0
   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)
   684  					}
   685  				}
   686  				if err == nil {
   687  					err = w.Close()
   688  				}
   689  				str := bb.String()
   690  				if str != tc.wantStr || err != tc.wantErr {
   691  					t.Errorf("\ngot  %q, %v\nwant %q, %v", str, err, tc.wantStr, tc.wantErr)
   692  				}
   693  			})
   694  		}
   695  	}
   696  }
   697  
   698  func TestNop(t *testing.T) {
   699  	testCases := []struct {
   700  		str     string
   701  		dstSize int
   702  		err     error
   703  	}{
   704  		{"", 0, nil},
   705  		{"", 10, nil},
   706  		{"a", 0, ErrShortDst},
   707  		{"a", 1, nil},
   708  		{"a", 10, nil},
   709  	}
   710  	for i, tc := range testCases {
   711  		dst := make([]byte, tc.dstSize)
   712  		nDst, nSrc, err := Nop.Transform(dst, []byte(tc.str), true)
   713  		want := tc.str
   714  		if tc.dstSize < len(want) {
   715  			want = want[:tc.dstSize]
   716  		}
   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)
   719  		}
   720  	}
   721  }
   722  
   723  func TestDiscard(t *testing.T) {
   724  	testCases := []struct {
   725  		str     string
   726  		dstSize int
   727  	}{
   728  		{"", 0},
   729  		{"", 10},
   730  		{"a", 0},
   731  		{"ab", 10},
   732  	}
   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))
   737  		}
   738  	}
   739  }
   740  
   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 {
   744  	t := []Transformer{}
   745  	for i := 0; i < len(x); i += 2 {
   746  		t = append(t, x[i].(Transformer))
   747  	}
   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))
   751  	}
   752  	return c
   753  }
   754  
   755  func chainTests() []testCase {
   756  	return []testCase{
   757  		{
   758  			desc:     "nil error",
   759  			t:        mkChain(rleEncode{}, 100, lowerCaseASCII{}),
   760  			src:      "ABB",
   761  			dstSize:  100,
   762  			srcSize:  100,
   763  			wantStr:  "1a2b",
   764  			wantErr:  nil,
   765  			wantIter: 1,
   766  		},
   767  
   768  		{
   769  			desc:    "short dst buffer",
   770  			t:       mkChain(lowerCaseASCII{}, 3, rleDecode{}),
   771  			src:     "1a2b3c10d11e0f1g",
   772  			dstSize: 10,
   773  			srcSize: 3,
   774  			wantStr: "abbcccdddddddddd",
   775  			wantErr: ErrShortDst,
   776  		},
   777  
   778  		{
   779  			desc:    "short internal dst buffer",
   780  			t:       mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop),
   781  			src:     "1a2b3c10d11e0f1g",
   782  			dstSize: 100,
   783  			srcSize: 3,
   784  			wantStr: "abbcccdddddddddd",
   785  			wantErr: errShortInternal,
   786  		},
   787  
   788  		{
   789  			desc:    "short internal dst buffer from input",
   790  			t:       mkChain(rleDecode{}, 10, Nop),
   791  			src:     "1a2b3c10d11e0f1g",
   792  			dstSize: 100,
   793  			srcSize: 3,
   794  			wantStr: "abbcccdddddddddd",
   795  			wantErr: errShortInternal,
   796  		},
   797  
   798  		{
   799  			desc:    "empty short internal dst buffer",
   800  			t:       mkChain(lowerCaseASCII{}, 3, rleDecode{}, 10, Nop),
   801  			src:     "4a7b11e0f1g",
   802  			dstSize: 100,
   803  			srcSize: 3,
   804  			wantStr: "aaaabbbbbbb",
   805  			wantErr: errShortInternal,
   806  		},
   807  
   808  		{
   809  			desc:    "empty short internal dst buffer from input",
   810  			t:       mkChain(rleDecode{}, 10, Nop),
   811  			src:     "4a7b11e0f1g",
   812  			dstSize: 100,
   813  			srcSize: 3,
   814  			wantStr: "aaaabbbbbbb",
   815  			wantErr: errShortInternal,
   816  		},
   817  
   818  		{
   819  			desc:     "short internal src buffer after full dst buffer",
   820  			t:        mkChain(Nop, 5, rleEncode{}, 10, Nop),
   821  			src:      "cccccddddd",
   822  			dstSize:  100,
   823  			srcSize:  100,
   824  			wantStr:  "",
   825  			wantErr:  errShortInternal,
   826  			wantIter: 1,
   827  		},
   828  
   829  		{
   830  			desc:    "short internal src buffer after short dst buffer; test lastFull",
   831  			t:       mkChain(rleDecode{}, 5, rleEncode{}, 4, Nop),
   832  			src:     "2a1b4c6d",
   833  			dstSize: 100,
   834  			srcSize: 100,
   835  			wantStr: "2a1b",
   836  			wantErr: errShortInternal,
   837  		},
   838  
   839  		{
   840  			desc:     "short internal src buffer after successful complete fill",
   841  			t:        mkChain(Nop, 3, rleDecode{}),
   842  			src:      "123a4b",
   843  			dstSize:  4,
   844  			srcSize:  3,
   845  			wantStr:  "",
   846  			wantErr:  errShortInternal,
   847  			wantIter: 1,
   848  		},
   849  
   850  		{
   851  			desc:    "short internal src buffer after short dst buffer; test lastFull",
   852  			t:       mkChain(rleDecode{}, 5, rleEncode{}),
   853  			src:     "2a1b4c6d",
   854  			dstSize: 4,
   855  			srcSize: 100,
   856  			wantStr: "2a1b",
   857  			wantErr: errShortInternal,
   858  		},
   859  
   860  		{
   861  			desc:    "short src buffer",
   862  			t:       mkChain(rleEncode{}, 5, Nop),
   863  			src:     "abbcccddddeeeee",
   864  			dstSize: 4,
   865  			srcSize: 4,
   866  			ioSize:  4,
   867  			wantStr: "1a2b3c",
   868  			wantErr: ErrShortSrc,
   869  		},
   870  
   871  		{
   872  			desc:     "process all in one go",
   873  			t:        mkChain(rleEncode{}, 5, Nop),
   874  			src:      "abbcccddddeeeeeffffff",
   875  			dstSize:  100,
   876  			srcSize:  100,
   877  			wantStr:  "1a2b3c4d5e6f",
   878  			wantErr:  nil,
   879  			wantIter: 1,
   880  		},
   881  
   882  		{
   883  			desc:    "complete processing downstream after error",
   884  			t:       mkChain(dontMentionX{}, 2, rleDecode{}, 5, Nop),
   885  			src:     "3a4b5eX",
   886  			dstSize: 100,
   887  			srcSize: 100,
   888  			ioSize:  100,
   889  			wantStr: "aaabbbbeeeee",
   890  			wantErr: errYouMentionedX,
   891  		},
   892  
   893  		{
   894  			desc:    "return downstream fatal errors first (followed by short dst)",
   895  			t:       mkChain(dontMentionX{}, 8, rleDecode{}, 4, Nop),
   896  			src:     "3a4b5eX",
   897  			dstSize: 100,
   898  			srcSize: 100,
   899  			ioSize:  100,
   900  			wantStr: "aaabbbb",
   901  			wantErr: errShortInternal,
   902  		},
   903  
   904  		{
   905  			desc:    "return downstream fatal errors first (followed by short src)",
   906  			t:       mkChain(dontMentionX{}, 5, Nop, 1, rleDecode{}),
   907  			src:     "1a5bX",
   908  			dstSize: 100,
   909  			srcSize: 100,
   910  			ioSize:  100,
   911  			wantStr: "",
   912  			wantErr: errShortInternal,
   913  		},
   914  
   915  		{
   916  			desc:    "short internal",
   917  			t:       mkChain(Nop, 11, rleEncode{}, 3, Nop),
   918  			src:     "abbcccddddddddddeeeeeeeeeeeg",
   919  			dstSize: 3,
   920  			srcSize: 100,
   921  			wantStr: "1a2b3c10d",
   922  			wantErr: errShortInternal,
   923  		},
   924  	}
   925  }
   926  
   927  func doTransform(tc testCase) (res string, iter int, err error) {
   928  	tc.t.Reset()
   929  	dst := make([]byte, tc.dstSize)
   930  	out, in := make([]byte, 0, 2*len(tc.src)), []byte(tc.src)
   931  	for {
   932  		iter++
   933  		src, atEOF := in, true
   934  		if len(src) > tc.srcSize {
   935  			src, atEOF = src[:tc.srcSize], false
   936  		}
   937  		nDst, nSrc, err := tc.t.Transform(dst, src, atEOF)
   938  		out = append(out, dst[:nDst]...)
   939  		in = in[nSrc:]
   940  		switch {
   941  		case err == nil && len(in) != 0:
   942  		case err == ErrShortSrc && nSrc > 0:
   943  		case err == ErrShortDst && (nDst > 0 || nSrc > 0):
   944  		default:
   945  			return string(out), iter, err
   946  		}
   947  	}
   948  }
   949  
   950  func TestChain(t *testing.T) {
   951  	if c, ok := Chain().(nop); !ok {
   952  		t.Errorf("empty chain: %v; want Nop", c)
   953  	}
   954  
   955  	// Test Chain for a single Transformer.
   956  	for _, tc := range testCases {
   957  		tc.t = Chain(tc.t)
   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)
   961  		}
   962  	}
   963  
   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
   969  		}
   970  		if t.link[len(t.link)-2].t != tc.t && tc.wantErr == ErrShortDst {
   971  			tc.wantErr = errShortInternal
   972  		}
   973  		tc.t = t
   974  		tests = append(tests, tc)
   975  	}
   976  	for _, tc := range testCases {
   977  		for _, sz := range sizes {
   978  			tt := tc
   979  			tt.dstSize = sz
   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))
   986  			}
   987  		}
   988  	}
   989  	for _, tc := range testCases {
   990  		tt := tc
   991  		tt.dstSize = 1
   992  		tt.wantStr = ""
   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))
   996  	}
   997  	for _, tc := range testCases {
   998  		tt := tc
   999  		tt.dstSize = 100
  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))
  1016  		}
  1017  	}
  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)
  1023  		}
  1024  		break
  1025  	}
  1026  }
  1027  
  1028  func TestRemoveFunc(t *testing.T) {
  1029  	filter := RemoveFunc(func(r rune) bool {
  1030  		return strings.IndexRune("ab\u0300\u1234,", r) != -1
  1031  	})
  1032  	tests := []testCase{
  1033  		{
  1034  			src:     ",",
  1035  			wantStr: "",
  1036  		},
  1037  
  1038  		{
  1039  			src:     "c",
  1040  			wantStr: "c",
  1041  		},
  1042  
  1043  		{
  1044  			src:     "\u2345",
  1045  			wantStr: "\u2345",
  1046  		},
  1047  
  1048  		{
  1049  			src:     "tschüß",
  1050  			wantStr: "tschüß",
  1051  		},
  1052  
  1053  		{
  1054  			src:     ",до,свидания,",
  1055  			wantStr: "досвидания",
  1056  		},
  1057  
  1058  		{
  1059  			src:     "a\xbd\xb2=\xbc ⌘",
  1060  			wantStr: "\uFFFD\uFFFD=\uFFFD ⌘",
  1061  		},
  1062  
  1063  		{
  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",
  1068  		},
  1069  
  1070  		{
  1071  			src:      "\xcc\u0300\x80",
  1072  			dstSize:  3,
  1073  			wantStr:  "\uFFFD\uFFFD",
  1074  			wantIter: 2,
  1075  		},
  1076  
  1077  		{
  1078  			// Test a long buffer greater than the internal buffer size
  1079  			src:      "hello\xcc\xcc\xccworld",
  1080  			srcSize:  13,
  1081  			wantStr:  "hello\uFFFD\uFFFD\uFFFDworld",
  1082  			wantIter: 1,
  1083  		},
  1084  
  1085  		{
  1086  			src:     "\u2345",
  1087  			dstSize: 2,
  1088  			wantStr: "",
  1089  			wantErr: ErrShortDst,
  1090  		},
  1091  
  1092  		{
  1093  			src:     "\xcc",
  1094  			dstSize: 2,
  1095  			wantStr: "",
  1096  			wantErr: ErrShortDst,
  1097  		},
  1098  
  1099  		{
  1100  			src:     "\u0300",
  1101  			dstSize: 2,
  1102  			srcSize: 1,
  1103  			wantStr: "",
  1104  			wantErr: ErrShortSrc,
  1105  		},
  1106  
  1107  		{
  1108  			t: RemoveFunc(func(r rune) bool {
  1109  				return r == utf8.RuneError
  1110  			}),
  1111  			src:     "\xcc\u0300\x80",
  1112  			wantStr: "\u0300",
  1113  		},
  1114  	}
  1115  
  1116  	for _, tc := range tests {
  1117  		tc.desc = tc.src
  1118  		if tc.t == nil {
  1119  			tc.t = filter
  1120  		}
  1121  		if tc.dstSize == 0 {
  1122  			tc.dstSize = 100
  1123  		}
  1124  		if tc.srcSize == 0 {
  1125  			tc.srcSize = 100
  1126  		}
  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)
  1131  		}
  1132  
  1133  		tc.src = str
  1134  		idem, _, _ := doTransform(tc)
  1135  		if str != idem {
  1136  			t.Errorf("%+q: found %+q; want %+q", tc.src, idem, str)
  1137  		}
  1138  	}
  1139  }
  1140  
  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.
  1146  			continue
  1147  		}
  1148  		if tt.wantErr == ErrShortDst || tt.wantErr == ErrShortSrc {
  1149  			// The result string will be different.
  1150  			continue
  1151  		}
  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)
  1156  			}
  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)
  1161  			}
  1162  			if got != tt.wantStr {
  1163  				t.Errorf("string: got %q; want %q", got, tt.wantStr)
  1164  			}
  1165  		})
  1166  	}
  1167  }
  1168  
  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
  1173  	})
  1174  }
  1175  
  1176  func TestAppend(t *testing.T) {
  1177  	// Create a bunch of subtests for different buffer sizes.
  1178  	testCases := [][]byte{
  1179  		nil,
  1180  		make([]byte, 0, 0),
  1181  		make([]byte, 0, 1),
  1182  		make([]byte, 1, 1),
  1183  		make([]byte, 1, 5),
  1184  		make([]byte, 100, 100),
  1185  		make([]byte, 100, 200),
  1186  	}
  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
  1191  		})
  1192  	}
  1193  }
  1194  
  1195  func TestString(t *testing.T) {
  1196  	testtext.Run(t, "transform", func(t *testing.T) { testString(t, String) })
  1197  
  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",
  1213  	} {
  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))
  1218  			}
  1219  		})
  1220  	}
  1221  
  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],
  1230  	} {
  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))
  1235  			}
  1236  		})
  1237  	}
  1238  
  1239  	// Test allocations for non-changing strings.
  1240  	// Note we still need to allocate a single buffer.
  1241  	for i, s := range []string{
  1242  		"",
  1243  		"123456789",
  1244  		aaa[:initialBufSize-1],
  1245  		aaa[:initialBufSize+0],
  1246  		aaa[:initialBufSize+1],
  1247  		aaa[:10*initialBufSize],
  1248  	} {
  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)
  1252  			}
  1253  		})
  1254  	}
  1255  }
  1256  
  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)
  1264  	go func() {
  1265  		in := bytes.Repeat([]byte{'a'}, 1000)
  1266  		tr := trickler(make([]byte, 1))
  1267  		Bytes(&tr, in)
  1268  		done <- true
  1269  	}()
  1270  	select {
  1271  	case <-done:
  1272  	case <-time.After(3 * time.Second):
  1273  		t.Error("time out, likely due to excessive allocation")
  1274  	}
  1275  }
  1276  
  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)
  1284  	go func() {
  1285  		tr := trickler(make([]byte, 1))
  1286  		String(&tr, aaa[:1000])
  1287  		done <- true
  1288  	}()
  1289  	select {
  1290  	case <-done:
  1291  	case <-time.After(3 * time.Second):
  1292  		t.Error("time out, likely due to excessive allocation")
  1293  	}
  1294  }
  1295  
  1296  func BenchmarkStringLowerEmpty(b *testing.B) {
  1297  	for i := 0; i < b.N; i++ {
  1298  		String(&lowerCaseASCIILookahead{}, "")
  1299  	}
  1300  }
  1301  
  1302  func BenchmarkStringLowerIdentical(b *testing.B) {
  1303  	for i := 0; i < b.N; i++ {
  1304  		String(&lowerCaseASCIILookahead{}, aaa[:4096])
  1305  	}
  1306  }
  1307  
  1308  func BenchmarkStringLowerChanged(b *testing.B) {
  1309  	for i := 0; i < b.N; i++ {
  1310  		String(&lowerCaseASCIILookahead{}, AAA[:4096])
  1311  	}
  1312  }
  1313  
  1314  var (
  1315  	aaa = strings.Repeat("a", 4096)
  1316  	AAA = strings.Repeat("A", 4096)
  1317  )
  1318  
  1319  type badTransformer struct{}
  1320  
  1321  func (bt badTransformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
  1322  	return 0, 0, ErrShortSrc
  1323  }
  1324  
  1325  func (bt badTransformer) Reset() {}
  1326  
  1327  func TestBadTransformer(t *testing.T) {
  1328  	bt := badTransformer{}
  1329  	if _, _, err := String(bt, "aaa"); err != ErrShortSrc {
  1330  		t.Errorf("String expected ErrShortSrc, got nil")
  1331  	}
  1332  	if _, _, err := Bytes(bt, []byte("aaa")); err != ErrShortSrc {
  1333  		t.Errorf("Bytes expected ErrShortSrc, got nil")
  1334  	}
  1335  	r := NewReader(bytes.NewReader([]byte("aaa")), bt)
  1336  	var bytes []byte
  1337  	if _, err := r.Read(bytes); err != ErrShortSrc {
  1338  		t.Errorf("NewReader Read expected ErrShortSrc, got nil")
  1339  	}
  1340  }