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  }