1 // Copyright 2015 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.
12 "golang.org/x/text/internal/testtext"
13 "golang.org/x/text/transform"
16 type transformTest struct {
22 out string // result string of first call to Transform
23 outFull string // transform of entire input string
28 t transform.SpanningTransformer
33 func (tt *transformTest) check(t *testing.T, i int) {
37 dst := make([]byte, tt.szDst)
39 nDst, nSrc, err := tt.t.Transform(dst, src, tt.atEOF)
41 t.Errorf("%d:%s:error: got %v; want %v", i, tt.desc, err, tt.err)
43 if got := string(dst[:nDst]); got != tt.out {
44 t.Errorf("%d:%s:out: got %q; want %q", i, tt.desc, got, tt.out)
47 // Calls tt.t.Transform for the remainder of the input. We use this to test
48 // the nSrc return value.
49 out := make([]byte, large)
50 n := copy(out, dst[:nDst])
51 nDst, _, _ = tt.t.Transform(out[n:], src[nSrc:], true)
52 if got, want := string(out[:n+nDst]), tt.outFull; got != want {
53 t.Errorf("%d:%s:outFull: got %q; want %q", i, tt.desc, got, want)
58 for ; p < len(tt.in) && p < len(tt.outFull) && tt.in[p] == tt.outFull[p]; p++ {
63 if n, err = tt.t.Span([]byte(tt.in), tt.atEOF); n != p || err != tt.errSpan {
64 t.Errorf("%d:%s:span: got %d, %v; want %d, %v", i, tt.desc, n, err, p, tt.errSpan)
68 func idem(r rune) rune { return r }
70 func TestMap(t *testing.T) {
71 runes := []rune{'a', 'ç', '中', '\U00012345', 'a'}
72 // Default mapper used for this test.
73 rotate := Map(func(r rune) rune {
74 for i, m := range runes {
82 for i, tt := range []transformTest{{
105 err: transform.ErrShortDst,
106 errSpan: transform.ErrEndOfSpan,
109 desc: "short dst ascii, no change",
115 err: transform.ErrShortDst,
118 desc: "short dst writing error",
124 err: transform.ErrShortDst,
125 errSpan: transform.ErrEndOfSpan,
128 desc: "short dst writing incomplete rune",
134 err: transform.ErrShortDst,
135 errSpan: transform.ErrEndOfSpan,
138 desc: "short dst, longer",
144 err: transform.ErrShortDst,
147 desc: "short dst, single",
153 err: transform.ErrShortDst,
156 desc: "short dst, longer, writing error",
161 outFull: "\ufffdHello\ufffd",
162 err: transform.ErrShortDst,
163 errSpan: transform.ErrEndOfSpan,
172 err: transform.ErrShortSrc,
173 errSpan: transform.ErrEndOfSpan,
176 desc: "invalid input, atEOF",
182 errSpan: transform.ErrEndOfSpan,
185 desc: "invalid input, !atEOF",
191 errSpan: transform.ErrEndOfSpan,
194 desc: "incomplete rune !atEOF",
200 err: transform.ErrShortSrc,
201 errSpan: transform.ErrShortSrc,
204 desc: "invalid input, incomplete rune atEOF",
210 errSpan: transform.ErrEndOfSpan,
213 desc: "misc correct",
216 in: "a\U00012345 ç!",
219 errSpan: transform.ErrEndOfSpan,
222 desc: "misc correct and invalid",
225 in: "Hello\x80 w\x80orl\xc0d!\xc0",
226 out: "Hello\ufffd w\ufffdorl\ufffdd!\ufffd",
227 outFull: "Hello\ufffd w\ufffdorl\ufffdd!\ufffd",
228 errSpan: transform.ErrEndOfSpan,
231 desc: "misc correct and invalid, short src",
234 in: "Hello\x80 w\x80orl\xc0d!\xc2",
235 out: "Hello\ufffd w\ufffdorl\ufffdd!",
236 outFull: "Hello\ufffd w\ufffdorl\ufffdd!\ufffd",
237 err: transform.ErrShortSrc,
238 errSpan: transform.ErrEndOfSpan,
241 desc: "misc correct and invalid, short src, replacing RuneError",
244 in: "Hel\ufffdlo\x80 w\x80orl\xc0d!\xc2",
245 out: "Hel?lo? w?orl?d!",
246 outFull: "Hel?lo? w?orl?d!?",
247 errSpan: transform.ErrEndOfSpan,
248 err: transform.ErrShortSrc,
249 t: Map(func(r rune) rune {
250 if r == utf8.RuneError {
260 func TestRemove(t *testing.T) {
261 remove := Remove(Predicate(func(r rune) bool {
262 return strings.ContainsRune("aeiou\u0300\uFF24\U00012345", r)
265 for i, tt := range []transformTest{
280 errSpan: transform.ErrEndOfSpan,
289 errSpan: transform.ErrEndOfSpan,
298 errSpan: transform.ErrEndOfSpan,
307 errSpan: transform.ErrEndOfSpan,
316 errSpan: transform.ErrEndOfSpan,
325 err: transform.ErrShortDst,
326 errSpan: transform.ErrEndOfSpan,
335 err: transform.ErrShortDst,
336 errSpan: transform.ErrEndOfSpan,
345 err: transform.ErrShortSrc,
346 errSpan: transform.ErrEndOfSpan,
355 errSpan: transform.ErrEndOfSpan,
364 errSpan: transform.ErrEndOfSpan,
373 errSpan: transform.ErrEndOfSpan,
382 err: transform.ErrShortSrc,
383 errSpan: transform.ErrShortSrc,
389 in: "Hello \U00012345world!",
391 outFull: "Hll wrld!",
392 errSpan: transform.ErrEndOfSpan,
398 in: "Hello\x80 w\x80orl\xc0d!\xc0",
399 out: "Hll\ufffd w\ufffdrl\ufffdd!\ufffd",
400 outFull: "Hll\ufffd w\ufffdrl\ufffdd!\ufffd",
401 errSpan: transform.ErrEndOfSpan,
407 in: "Hello\x80 w\x80orl\xc0d!\xc2",
408 out: "Hll\ufffd w\ufffdrl\ufffdd!",
409 outFull: "Hll\ufffd w\ufffdrl\ufffdd!\ufffd",
410 err: transform.ErrShortSrc,
411 errSpan: transform.ErrEndOfSpan,
417 in: "Hel\ufffdlo\x80 w\x80orl\xc0d!\xc2",
419 outFull: "Hello world!",
420 err: transform.ErrShortSrc,
421 errSpan: transform.ErrEndOfSpan,
422 t: Remove(Predicate(func(r rune) bool { return r == utf8.RuneError })),
430 err: transform.ErrShortDst,
431 errSpan: transform.ErrEndOfSpan,
440 err: transform.ErrShortDst,
441 errSpan: transform.ErrEndOfSpan,
447 in: "\x80Hello\uFF24\x80",
449 outFull: "\ufffdHll\ufffd",
450 err: transform.ErrShortDst,
451 errSpan: transform.ErrEndOfSpan,
466 func TestReplaceIllFormed(t *testing.T) {
467 replace := ReplaceIllFormed()
469 for i, tt := range []transformTest{
484 err: transform.ErrShortDst,
493 err: transform.ErrShortDst,
494 errSpan: transform.ErrEndOfSpan,
503 err: transform.ErrShortDst,
504 errSpan: transform.ErrEndOfSpan,
513 errSpan: transform.ErrEndOfSpan,
522 errSpan: transform.ErrEndOfSpan,
531 errSpan: transform.ErrEndOfSpan,
540 err: transform.ErrShortSrc,
541 errSpan: transform.ErrShortSrc,
549 outFull: "Hello world!",
555 in: "Hello\x80 w\x80orl\xc2d!\xc2",
556 out: "Hello\ufffd w\ufffdorl\ufffdd!\ufffd",
557 outFull: "Hello\ufffd w\ufffdorl\ufffdd!\ufffd",
558 errSpan: transform.ErrEndOfSpan,
564 in: "Hello\x80 w\x80orl\xc2d!\xc2",
565 out: "Hello\ufffd w\ufffdorl\ufffdd!",
566 outFull: "Hello\ufffd w\ufffdorl\ufffdd!\ufffd",
567 err: transform.ErrShortSrc,
568 errSpan: transform.ErrEndOfSpan,
576 outFull: "\ufffdHello\ufffd",
577 err: transform.ErrShortDst,
578 errSpan: transform.ErrEndOfSpan,
584 in: "\ufffdHello\ufffd",
586 outFull: "\ufffdHello\ufffd",
587 err: transform.ErrShortDst,
595 func TestMapAlloc(t *testing.T) {
596 if n := testtext.AllocsPerRun(3, func() {
597 Map(idem).Transform(nil, nil, false)
599 t.Errorf("got %f; want 0", n)
603 func rmNop(r rune) bool { return false }
605 func TestRemoveAlloc(t *testing.T) {
606 if n := testtext.AllocsPerRun(3, func() {
607 Remove(Predicate(rmNop)).Transform(nil, nil, false)
609 t.Errorf("got %f; want 0", n)
613 func TestReplaceIllFormedAlloc(t *testing.T) {
614 if n := testtext.AllocsPerRun(3, func() {
615 ReplaceIllFormed().Transform(nil, nil, false)
617 t.Errorf("got %f; want 0", n)
621 func doBench(b *testing.B, t Transformer) {
622 for _, bc := range []struct{ name, data string }{
623 {"ascii", testtext.ASCII},
624 {"3byte", testtext.ThreeByteUTF8},
626 dst := make([]byte, 2*len(bc.data))
627 src := []byte(bc.data)
629 testtext.Bench(b, bc.name+"/transform", func(b *testing.B) {
630 b.SetBytes(int64(len(src)))
631 for i := 0; i < b.N; i++ {
632 t.Transform(dst, src, true)
637 testtext.Bench(b, bc.name+"/span", func(b *testing.B) {
638 b.SetBytes(int64(len(src)))
639 for i := 0; i < b.N; i++ {
646 func BenchmarkRemove(b *testing.B) {
647 doBench(b, Remove(Predicate(func(r rune) bool { return r == 'e' })))
650 func BenchmarkMapAll(b *testing.B) {
651 doBench(b, Map(func(r rune) rune { return 'a' }))
654 func BenchmarkMapNone(b *testing.B) {
655 doBench(b, Map(func(r rune) rune { return r }))
658 func BenchmarkReplaceIllFormed(b *testing.B) {
659 doBench(b, ReplaceIllFormed())
663 input = strings.Repeat("Thé qüick brøwn føx jumps øver the lazy døg. ", 100)