~vtuson/scopecreator/twitter-template

« back to all changes in this revision

Viewing changes to src/go/src/code.google.com/p/go.text/transform/transform_test.go

  • Committer: Victor Palau
  • Date: 2015-03-11 14:24:42 UTC
  • Revision ID: vtuson@gmail.com-20150311142442-f2pxp111c8ynv232
public release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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{}
 
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{}
 
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{}
 
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{}
 
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
        // allowStutter means that "xxxxxxxx" can be encoded as "5x3x"
 
108
        // instead of always as "8x".
 
109
        allowStutter bool
 
110
}
 
111
 
 
112
func (e rleEncode) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
 
113
        for len(src) > 0 {
 
114
                n, c0 := len(src), src[0]
 
115
                for i, c := range src[1:] {
 
116
                        if c != c0 {
 
117
                                n = i + 1
 
118
                                break
 
119
                        }
 
120
                }
 
121
                if n == len(src) && !atEOF && !e.allowStutter {
 
122
                        return nDst, nSrc, ErrShortSrc
 
123
                }
 
124
                s := strconv.Itoa(n)
 
125
                if len(s) >= len(dst) {
 
126
                        return nDst, nSrc, ErrShortDst
 
127
                }
 
128
                copy(dst, s)
 
129
                dst[len(s)] = c0
 
130
                dst, src = dst[len(s)+1:], src[n:]
 
131
                nDst, nSrc = nDst+len(s)+1, nSrc+n
 
132
        }
 
133
        return nDst, nSrc, nil
 
134
}
 
135
 
 
136
// trickler consumes all input bytes, but writes a single byte at a time to dst.
 
137
type trickler []byte
 
138
 
 
139
func (t *trickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
 
140
        *t = append(*t, src...)
 
141
        if len(*t) == 0 {
 
142
                return 0, 0, nil
 
143
        }
 
144
        if len(dst) == 0 {
 
145
                return 0, len(src), ErrShortDst
 
146
        }
 
147
        dst[0] = (*t)[0]
 
148
        *t = (*t)[1:]
 
149
        if len(*t) > 0 {
 
150
                err = ErrShortDst
 
151
        }
 
152
        return 1, len(src), err
 
153
}
 
154
 
 
155
// delayedTrickler is like trickler, but delays writing output to dst. This is
 
156
// highly unlikely to be relevant in practice, but it seems like a good idea
 
157
// to have some tolerance as long as progress can be detected.
 
158
type delayedTrickler []byte
 
159
 
 
160
func (t *delayedTrickler) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
 
161
        if len(*t) > 0 && len(dst) > 0 {
 
162
                dst[0] = (*t)[0]
 
163
                *t = (*t)[1:]
 
164
                nDst = 1
 
165
        }
 
166
        *t = append(*t, src...)
 
167
        if len(*t) > 0 {
 
168
                err = ErrShortDst
 
169
        }
 
170
        return nDst, len(src), err
 
171
}
 
172
 
 
173
type testCase struct {
 
174
        desc     string
 
175
        t        Transformer
 
176
        src      string
 
177
        dstSize  int
 
178
        srcSize  int
 
179
        ioSize   int
 
180
        wantStr  string
 
181
        wantErr  error
 
182
        wantIter int // number of iterations taken; 0 means we don't care.
 
183
}
 
184
 
 
185
func (t testCase) String() string {
 
186
        return tstr(t.t) + "; " + t.desc
 
187
}
 
188
 
 
189
func tstr(t Transformer) string {
 
190
        if stringer, ok := t.(fmt.Stringer); ok {
 
191
                return stringer.String()
 
192
        }
 
193
        s := fmt.Sprintf("%T", t)
 
194
        return s[1+strings.Index(s, "."):]
 
195
}
 
196
 
 
197
func (c chain) String() string {
 
198
        buf := &bytes.Buffer{}
 
199
        buf.WriteString("Chain(")
 
200
        for i, l := range c.link[:len(c.link)-1] {
 
201
                if i != 0 {
 
202
                        fmt.Fprint(buf, ", ")
 
203
                }
 
204
                buf.WriteString(tstr(l.t))
 
205
        }
 
206
        buf.WriteString(")")
 
207
        return buf.String()
 
208
}
 
209
 
 
210
var testCases = []testCase{
 
211
        {
 
212
                desc:    "empty",
 
213
                t:       lowerCaseASCII{},
 
214
                src:     "",
 
215
                dstSize: 100,
 
216
                srcSize: 100,
 
217
                wantStr: "",
 
218
        },
 
219
 
 
220
        {
 
221
                desc:    "basic",
 
222
                t:       lowerCaseASCII{},
 
223
                src:     "Hello WORLD.",
 
224
                dstSize: 100,
 
225
                srcSize: 100,
 
226
                wantStr: "hello world.",
 
227
        },
 
228
 
 
229
        {
 
230
                desc:    "small dst",
 
231
                t:       lowerCaseASCII{},
 
232
                src:     "Hello WORLD.",
 
233
                dstSize: 3,
 
234
                srcSize: 100,
 
235
                wantStr: "hello world.",
 
236
        },
 
237
 
 
238
        {
 
239
                desc:    "small src",
 
240
                t:       lowerCaseASCII{},
 
241
                src:     "Hello WORLD.",
 
242
                dstSize: 100,
 
243
                srcSize: 4,
 
244
                wantStr: "hello world.",
 
245
        },
 
246
 
 
247
        {
 
248
                desc:    "small buffers",
 
249
                t:       lowerCaseASCII{},
 
250
                src:     "Hello WORLD.",
 
251
                dstSize: 3,
 
252
                srcSize: 4,
 
253
                wantStr: "hello world.",
 
254
        },
 
255
 
 
256
        {
 
257
                desc:    "very small buffers",
 
258
                t:       lowerCaseASCII{},
 
259
                src:     "Hello WORLD.",
 
260
                dstSize: 1,
 
261
                srcSize: 1,
 
262
                wantStr: "hello world.",
 
263
        },
 
264
 
 
265
        {
 
266
                desc:    "basic",
 
267
                t:       dontMentionX{},
 
268
                src:     "The First Rule of Transform Club: don't mention Mister X, ever.",
 
269
                dstSize: 100,
 
270
                srcSize: 100,
 
271
                wantStr: "The First Rule of Transform Club: don't mention Mister ",
 
272
                wantErr: errYouMentionedX,
 
273
        },
 
274
 
 
275
        {
 
276
                desc:    "small buffers",
 
277
                t:       dontMentionX{},
 
278
                src:     "The First Rule of Transform Club: don't mention Mister X, ever.",
 
279
                dstSize: 10,
 
280
                srcSize: 10,
 
281
                wantStr: "The First Rule of Transform Club: don't mention Mister ",
 
282
                wantErr: errYouMentionedX,
 
283
        },
 
284
 
 
285
        {
 
286
                desc:    "very small buffers",
 
287
                t:       dontMentionX{},
 
288
                src:     "The First Rule of Transform Club: don't mention Mister X, ever.",
 
289
                dstSize: 1,
 
290
                srcSize: 1,
 
291
                wantStr: "The First Rule of Transform Club: don't mention Mister ",
 
292
                wantErr: errYouMentionedX,
 
293
        },
 
294
 
 
295
        {
 
296
                desc:    "only transform at EOF",
 
297
                t:       doublerAtEOF{},
 
298
                src:     "this",
 
299
                dstSize: 100,
 
300
                srcSize: 100,
 
301
                wantStr: "tthhiiss",
 
302
        },
 
303
 
 
304
        {
 
305
                desc:    "basic",
 
306
                t:       rleDecode{},
 
307
                src:     "1a2b3c10d11e0f1g",
 
308
                dstSize: 100,
 
309
                srcSize: 100,
 
310
                wantStr: "abbcccddddddddddeeeeeeeeeeeg",
 
311
        },
 
312
 
 
313
        {
 
314
                desc:    "long",
 
315
                t:       rleDecode{},
 
316
                src:     "12a23b34c45d56e99z",
 
317
                dstSize: 100,
 
318
                srcSize: 100,
 
319
                wantStr: strings.Repeat("a", 12) +
 
320
                        strings.Repeat("b", 23) +
 
321
                        strings.Repeat("c", 34) +
 
322
                        strings.Repeat("d", 45) +
 
323
                        strings.Repeat("e", 56) +
 
324
                        strings.Repeat("z", 99),
 
325
        },
 
326
 
 
327
        {
 
328
                desc:    "tight buffers",
 
329
                t:       rleDecode{},
 
330
                src:     "1a2b3c10d11e0f1g",
 
331
                dstSize: 11,
 
332
                srcSize: 3,
 
333
                wantStr: "abbcccddddddddddeeeeeeeeeeeg",
 
334
        },
 
335
 
 
336
        {
 
337
                desc:    "short dst",
 
338
                t:       rleDecode{},
 
339
                src:     "1a2b3c10d11e0f1g",
 
340
                dstSize: 10,
 
341
                srcSize: 3,
 
342
                wantStr: "abbcccdddddddddd",
 
343
                wantErr: ErrShortDst,
 
344
        },
 
345
 
 
346
        {
 
347
                desc:    "short src",
 
348
                t:       rleDecode{},
 
349
                src:     "1a2b3c10d11e0f1g",
 
350
                dstSize: 11,
 
351
                srcSize: 2,
 
352
                ioSize:  2,
 
353
                wantStr: "abbccc",
 
354
                wantErr: ErrShortSrc,
 
355
        },
 
356
 
 
357
        {
 
358
                desc:    "basic",
 
359
                t:       rleEncode{},
 
360
                src:     "abbcccddddddddddeeeeeeeeeeeg",
 
361
                dstSize: 100,
 
362
                srcSize: 100,
 
363
                wantStr: "1a2b3c10d11e1g",
 
364
        },
 
365
 
 
366
        {
 
367
                desc: "long",
 
368
                t:    rleEncode{},
 
369
                src: strings.Repeat("a", 12) +
 
370
                        strings.Repeat("b", 23) +
 
371
                        strings.Repeat("c", 34) +
 
372
                        strings.Repeat("d", 45) +
 
373
                        strings.Repeat("e", 56) +
 
374
                        strings.Repeat("z", 99),
 
375
                dstSize: 100,
 
376
                srcSize: 100,
 
377
                wantStr: "12a23b34c45d56e99z",
 
378
        },
 
379
 
 
380
        {
 
381
                desc:    "tight buffers",
 
382
                t:       rleEncode{},
 
383
                src:     "abbcccddddddddddeeeeeeeeeeeg",
 
384
                dstSize: 3,
 
385
                srcSize: 12,
 
386
                wantStr: "1a2b3c10d11e1g",
 
387
        },
 
388
 
 
389
        {
 
390
                desc:    "short dst",
 
391
                t:       rleEncode{},
 
392
                src:     "abbcccddddddddddeeeeeeeeeeeg",
 
393
                dstSize: 2,
 
394
                srcSize: 12,
 
395
                wantStr: "1a2b3c",
 
396
                wantErr: ErrShortDst,
 
397
        },
 
398
 
 
399
        {
 
400
                desc:    "short src",
 
401
                t:       rleEncode{},
 
402
                src:     "abbcccddddddddddeeeeeeeeeeeg",
 
403
                dstSize: 3,
 
404
                srcSize: 11,
 
405
                ioSize:  11,
 
406
                wantStr: "1a2b3c10d",
 
407
                wantErr: ErrShortSrc,
 
408
        },
 
409
 
 
410
        {
 
411
                desc:    "allowStutter = false",
 
412
                t:       rleEncode{allowStutter: false},
 
413
                src:     "aaaabbbbbbbbccccddddd",
 
414
                dstSize: 10,
 
415
                srcSize: 10,
 
416
                wantStr: "4a8b4c5d",
 
417
        },
 
418
 
 
419
        {
 
420
                desc:    "allowStutter = true",
 
421
                t:       rleEncode{allowStutter: true},
 
422
                src:     "aaaabbbbbbbbccccddddd",
 
423
                dstSize: 10,
 
424
                srcSize: 10,
 
425
                ioSize:  10,
 
426
                wantStr: "4a6b2b4c4d1d",
 
427
        },
 
428
 
 
429
        {
 
430
                desc:    "trickler",
 
431
                t:       &trickler{},
 
432
                src:     "abcdefghijklm",
 
433
                dstSize: 3,
 
434
                srcSize: 15,
 
435
                wantStr: "abcdefghijklm",
 
436
        },
 
437
 
 
438
        {
 
439
                desc:    "delayedTrickler",
 
440
                t:       &delayedTrickler{},
 
441
                src:     "abcdefghijklm",
 
442
                dstSize: 3,
 
443
                srcSize: 15,
 
444
                wantStr: "abcdefghijklm",
 
445
        },
 
446
}
 
447
 
 
448
func TestReader(t *testing.T) {
 
449
        for _, tc := range testCases {
 
450
                reset(tc.t)
 
451
                r := NewReader(strings.NewReader(tc.src), tc.t)
 
452
                // Differently sized dst and src buffers are not part of the
 
453
                // exported API. We override them manually.
 
454
                r.dst = make([]byte, tc.dstSize)
 
455
                r.src = make([]byte, tc.srcSize)
 
456
                got, err := ioutil.ReadAll(r)
 
457
                str := string(got)
 
458
                if str != tc.wantStr || err != tc.wantErr {
 
459
                        t.Errorf("%s:\ngot  %q, %v\nwant %q, %v", tc, str, err, tc.wantStr, tc.wantErr)
 
460
                }
 
461
        }
 
462
}
 
463
 
 
464
func reset(t Transformer) {
 
465
        var dst [128]byte
 
466
        for err := ErrShortDst; err != nil; {
 
467
                _, _, err = t.Transform(dst[:], nil, true)
 
468
        }
 
469
}
 
470
 
 
471
func TestWriter(t *testing.T) {
 
472
        tests := append(testCases, chainTests()...)
 
473
        for _, tc := range tests {
 
474
                sizes := []int{1, 2, 3, 4, 5, 10, 100, 1000}
 
475
                if tc.ioSize > 0 {
 
476
                        sizes = []int{tc.ioSize}
 
477
                }
 
478
                for _, sz := range sizes {
 
479
                        bb := &bytes.Buffer{}
 
480
                        reset(tc.t)
 
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
        reset(tc.t)
 
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
                reset(tt.t)
 
959
                if tt.wantErr == ErrShortDst || tt.wantErr == ErrShortSrc {
 
960
                        // The result string will be different.
 
961
                        continue
 
962
                }
 
963
                got, n, err := f(tt.t, tt.src)
 
964
                if tt.wantErr != err {
 
965
                        t.Errorf("%s:error: got %v; want %v", tt.desc, err, tt.wantErr)
 
966
                }
 
967
                if got, want := err == nil, n == len(tt.src); got != want {
 
968
                        t.Errorf("%s:n: got %v; want %v", tt.desc, got, want)
 
969
                }
 
970
                if got != tt.wantStr {
 
971
                        t.Errorf("%s:string: got %q; want %q", tt.desc, got, tt.wantStr)
 
972
                }
 
973
        }
 
974
}
 
975
 
 
976
func TestBytes(t *testing.T) {
 
977
        testString(t, func(z Transformer, s string) (string, int, error) {
 
978
                b, n, err := Bytes(z, []byte(s))
 
979
                return string(b), n, err
 
980
        })
 
981
}
 
982
 
 
983
func TestString(t *testing.T) {
 
984
        testString(t, String)
 
985
 
 
986
        // Overrun the internal destination buffer.
 
987
        for i, s := range []string{
 
988
                strings.Repeat("a", initialBufSize-1),
 
989
                strings.Repeat("a", initialBufSize+0),
 
990
                strings.Repeat("a", initialBufSize+1),
 
991
                strings.Repeat("A", initialBufSize-1),
 
992
                strings.Repeat("A", initialBufSize+0),
 
993
                strings.Repeat("A", initialBufSize+1),
 
994
                strings.Repeat("A", 2*initialBufSize-1),
 
995
                strings.Repeat("A", 2*initialBufSize+0),
 
996
                strings.Repeat("A", 2*initialBufSize+1),
 
997
                strings.Repeat("a", initialBufSize-2) + "A",
 
998
                strings.Repeat("a", initialBufSize-1) + "A",
 
999
                strings.Repeat("a", initialBufSize+0) + "A",
 
1000
                strings.Repeat("a", initialBufSize+1) + "A",
 
1001
        } {
 
1002
                got, _, _ := String(lowerCaseASCII{}, s)
 
1003
                if want := strings.ToLower(s); got != want {
 
1004
                        t.Errorf("%d:dst buffer test: got %s (%d); want %s (%d)", i, got, len(got), want, len(want))
 
1005
                }
 
1006
        }
 
1007
 
 
1008
        // Overrun the internal source buffer.
 
1009
        for i, s := range []string{
 
1010
                strings.Repeat("a", initialBufSize-1),
 
1011
                strings.Repeat("a", initialBufSize+0),
 
1012
                strings.Repeat("a", initialBufSize+1),
 
1013
                strings.Repeat("a", 2*initialBufSize+1),
 
1014
                strings.Repeat("a", 2*initialBufSize+0),
 
1015
                strings.Repeat("a", 2*initialBufSize+1),
 
1016
        } {
 
1017
                got, _, _ := String(rleEncode{}, s)
 
1018
                if want := fmt.Sprintf("%da", len(s)); got != want {
 
1019
                        t.Errorf("%d:src buffer test: got %s (%d); want %s (%d)", i, got, len(got), want, len(want))
 
1020
                }
 
1021
        }
 
1022
 
 
1023
        // Test allocations for non-changing strings.
 
1024
        // Note we still need to allocate a single buffer.
 
1025
        for i, s := range []string{
 
1026
                "",
 
1027
                "123",
 
1028
                "123456789",
 
1029
                strings.Repeat("a", initialBufSize),
 
1030
                strings.Repeat("a", 10*initialBufSize),
 
1031
        } {
 
1032
                if n := testing.AllocsPerRun(5, func() { String(&lowerCaseASCII{}, s) }); n > 1 {
 
1033
                        t.Errorf("%d: #allocs was %f; want 1", i, n)
 
1034
                }
 
1035
        }
 
1036
}
 
1037
 
 
1038
// TestBytesAllocation tests that buffer growth stays limited with the trickler
 
1039
// transformer, which behaves oddly but within spec. In case buffer growth is
 
1040
// not correctly handled, the test will either panic with a failed allocation or
 
1041
// thrash. To ensure the tests terminate under the last condition, we time out
 
1042
// after some sufficiently long period of time.
 
1043
func TestBytesAllocation(t *testing.T) {
 
1044
        done := make(chan bool)
 
1045
        go func() {
 
1046
                in := bytes.Repeat([]byte{'a'}, 1000)
 
1047
                tr := trickler(make([]byte, 1))
 
1048
                Bytes(&tr, in)
 
1049
                done <- true
 
1050
        }()
 
1051
        select {
 
1052
        case <-done:
 
1053
        case <-time.After(3 * time.Second):
 
1054
                t.Error("time out, likely due to excessive allocation")
 
1055
        }
 
1056
}
 
1057
 
 
1058
// TestStringAllocation tests that buffer growth stays limited with the trickler
 
1059
// transformer, which behaves oddly but within spec. In case buffer growth is
 
1060
// not correctly handled, the test will either panic with a failed allocation or
 
1061
// thrash. To ensure the tests terminate under the last condition, we time out
 
1062
// after some sufficiently long period of time.
 
1063
func TestStringAllocation(t *testing.T) {
 
1064
        done := make(chan bool)
 
1065
        go func() {
 
1066
                in := strings.Repeat("a", 1000)
 
1067
                tr := trickler(make([]byte, 1))
 
1068
                String(&tr, in)
 
1069
                done <- true
 
1070
        }()
 
1071
        select {
 
1072
        case <-done:
 
1073
        case <-time.After(3 * time.Second):
 
1074
                t.Error("time out, likely due to excessive allocation")
 
1075
        }
 
1076
}
 
1077
 
 
1078
func BenchmarkStringLower(b *testing.B) {
 
1079
        in := strings.Repeat("a", 4096)
 
1080
        for i := 0; i < b.N; i++ {
 
1081
                String(&lowerCaseASCII{}, in)
 
1082
        }
 
1083
}