...

Source file src/golang.org/x/text/width/transform_test.go

Documentation: golang.org/x/text/width

     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.
     4  
     5  package width
     6  
     7  import (
     8  	"bytes"
     9  	"strings"
    10  	"testing"
    11  
    12  	"golang.org/x/text/internal/testtext"
    13  	"golang.org/x/text/transform"
    14  )
    15  
    16  func foldRune(r rune) (folded rune, ok bool) {
    17  	alt, ok := mapRunes[r]
    18  	if ok && alt.e&tagNeedsFold != 0 {
    19  		return alt.r, true
    20  	}
    21  	return r, false
    22  }
    23  
    24  func widenRune(r rune) (wide rune, ok bool) {
    25  	alt, ok := mapRunes[r]
    26  	if k := alt.e.kind(); k == EastAsianHalfwidth || k == EastAsianNarrow {
    27  		return alt.r, true
    28  	}
    29  	return r, false
    30  }
    31  
    32  func narrowRune(r rune) (narrow rune, ok bool) {
    33  	alt, ok := mapRunes[r]
    34  	if k := alt.e.kind(); k == EastAsianFullwidth || k == EastAsianWide || k == EastAsianAmbiguous {
    35  		return alt.r, true
    36  	}
    37  	return r, false
    38  }
    39  
    40  func TestFoldSingleRunes(t *testing.T) {
    41  	for r := rune(0); r < 0x1FFFF; r++ {
    42  		if loSurrogate <= r && r <= hiSurrogate {
    43  			continue
    44  		}
    45  		x, _ := foldRune(r)
    46  		want := string(x)
    47  		got := Fold.String(string(r))
    48  		if got != want {
    49  			t.Errorf("Fold().String(%U) = %+q; want %+q", r, got, want)
    50  		}
    51  	}
    52  }
    53  
    54  type transformTest struct {
    55  	desc    string
    56  	src     string
    57  	nBuf    int
    58  	nDst    int
    59  	atEOF   bool
    60  	dst     string
    61  	nSrc    int
    62  	err     error
    63  	nSpan   int
    64  	errSpan error
    65  }
    66  
    67  func (tc *transformTest) doTest(t *testing.T, tr Transformer) {
    68  	testtext.Run(t, tc.desc, func(t *testing.T) {
    69  		b := make([]byte, tc.nBuf)
    70  		nDst, nSrc, err := tr.Transform(b, []byte(tc.src), tc.atEOF)
    71  		if got := string(b[:nDst]); got != tc.dst[:nDst] {
    72  			t.Errorf("dst was %+q; want %+q", got, tc.dst)
    73  		}
    74  		if nDst != tc.nDst {
    75  			t.Errorf("nDst was %d; want %d", nDst, tc.nDst)
    76  		}
    77  		if nSrc != tc.nSrc {
    78  			t.Errorf("nSrc was %d; want %d", nSrc, tc.nSrc)
    79  		}
    80  		if err != tc.err {
    81  			t.Errorf("error was %v; want %v", err, tc.err)
    82  		}
    83  		if got := tr.String(tc.src); got != tc.dst {
    84  			t.Errorf("String(%q) = %q; want %q", tc.src, got, tc.dst)
    85  		}
    86  		n, err := tr.Span([]byte(tc.src), tc.atEOF)
    87  		if n != tc.nSpan || err != tc.errSpan {
    88  			t.Errorf("Span: got %d, %v; want %d, %v", n, err, tc.nSpan, tc.errSpan)
    89  		}
    90  	})
    91  }
    92  
    93  func TestFold(t *testing.T) {
    94  	for _, tc := range []transformTest{{
    95  		desc:    "empty",
    96  		src:     "",
    97  		nBuf:    10,
    98  		dst:     "",
    99  		nDst:    0,
   100  		nSrc:    0,
   101  		atEOF:   false,
   102  		err:     nil,
   103  		nSpan:   0,
   104  		errSpan: nil,
   105  	}, {
   106  		desc:    "short source 1",
   107  		src:     "a\xc2",
   108  		nBuf:    10,
   109  		dst:     "a\xc2",
   110  		nDst:    1,
   111  		nSrc:    1,
   112  		atEOF:   false,
   113  		err:     transform.ErrShortSrc,
   114  		nSpan:   1,
   115  		errSpan: transform.ErrShortSrc,
   116  	}, {
   117  		desc:    "short source 2",
   118  		src:     "a\xe0\x80",
   119  		nBuf:    10,
   120  		dst:     "a\xe0\x80",
   121  		nDst:    1,
   122  		nSrc:    1,
   123  		atEOF:   false,
   124  		err:     transform.ErrShortSrc,
   125  		nSpan:   1,
   126  		errSpan: transform.ErrShortSrc,
   127  	}, {
   128  		desc:    "incomplete but terminated source 1",
   129  		src:     "a\xc2",
   130  		nBuf:    10,
   131  		dst:     "a\xc2",
   132  		nDst:    2,
   133  		nSrc:    2,
   134  		atEOF:   true,
   135  		err:     nil,
   136  		nSpan:   2,
   137  		errSpan: nil,
   138  	}, {
   139  		desc:    "incomplete but terminated source 2",
   140  		src:     "a\xe0\x80",
   141  		nBuf:    10,
   142  		dst:     "a\xe0\x80",
   143  		nDst:    3,
   144  		nSrc:    3,
   145  		atEOF:   true,
   146  		err:     nil,
   147  		nSpan:   3,
   148  		errSpan: nil,
   149  	}, {
   150  		desc:    "exact fit dst",
   151  		src:     "a\uff01",
   152  		nBuf:    2,
   153  		dst:     "a!",
   154  		nDst:    2,
   155  		nSrc:    4,
   156  		atEOF:   false,
   157  		err:     nil,
   158  		nSpan:   1,
   159  		errSpan: transform.ErrEndOfSpan,
   160  	}, {
   161  		desc:    "exact fit dst and src ascii",
   162  		src:     "ab",
   163  		nBuf:    2,
   164  		dst:     "ab",
   165  		nDst:    2,
   166  		nSrc:    2,
   167  		atEOF:   true,
   168  		err:     nil,
   169  		nSpan:   2,
   170  		errSpan: nil,
   171  	}, {
   172  		desc:    "empty dst",
   173  		src:     "\u0300",
   174  		nBuf:    0,
   175  		dst:     "\u0300",
   176  		nDst:    0,
   177  		nSrc:    0,
   178  		atEOF:   true,
   179  		err:     transform.ErrShortDst,
   180  		nSpan:   2,
   181  		errSpan: nil,
   182  	}, {
   183  		desc:    "empty dst ascii",
   184  		src:     "a",
   185  		nBuf:    0,
   186  		dst:     "a",
   187  		nDst:    0,
   188  		nSrc:    0,
   189  		atEOF:   true,
   190  		err:     transform.ErrShortDst,
   191  		nSpan:   1,
   192  		errSpan: nil,
   193  	}, {
   194  		desc:    "short dst 1",
   195  		src:     "a\uffe0", // ¢
   196  		nBuf:    2,
   197  		dst:     "a\u00a2", // ¢
   198  		nDst:    1,
   199  		nSrc:    1,
   200  		atEOF:   false,
   201  		err:     transform.ErrShortDst,
   202  		nSpan:   1,
   203  		errSpan: transform.ErrEndOfSpan,
   204  	}, {
   205  		desc:    "short dst 2",
   206  		src:     "不夠",
   207  		nBuf:    3,
   208  		dst:     "不夠",
   209  		nDst:    3,
   210  		nSrc:    3,
   211  		atEOF:   true,
   212  		err:     transform.ErrShortDst,
   213  		nSpan:   6,
   214  		errSpan: nil,
   215  	}, {
   216  		desc:    "short dst fast path",
   217  		src:     "fast",
   218  		nDst:    3,
   219  		dst:     "fast",
   220  		nBuf:    3,
   221  		nSrc:    3,
   222  		atEOF:   true,
   223  		err:     transform.ErrShortDst,
   224  		nSpan:   4,
   225  		errSpan: nil,
   226  	}, {
   227  		desc:    "short dst larger buffer",
   228  		src:     "\uff21" + strings.Repeat("0", 127) + "B",
   229  		nBuf:    128,
   230  		dst:     "A" + strings.Repeat("0", 127) + "B",
   231  		nDst:    128,
   232  		nSrc:    130,
   233  		atEOF:   true,
   234  		err:     transform.ErrShortDst,
   235  		nSpan:   0,
   236  		errSpan: transform.ErrEndOfSpan,
   237  	}, {
   238  		desc:    "fast path alternation",
   239  		src:     "fast路徑fast路徑",
   240  		nBuf:    20,
   241  		dst:     "fast路徑fast路徑",
   242  		nDst:    20,
   243  		nSrc:    20,
   244  		atEOF:   true,
   245  		err:     nil,
   246  		nSpan:   20,
   247  		errSpan: nil,
   248  	}} {
   249  		tc.doTest(t, Fold)
   250  	}
   251  }
   252  
   253  func TestWidenSingleRunes(t *testing.T) {
   254  	for r := rune(0); r < 0x1FFFF; r++ {
   255  		if loSurrogate <= r && r <= hiSurrogate {
   256  			continue
   257  		}
   258  		alt, _ := widenRune(r)
   259  		want := string(alt)
   260  		got := Widen.String(string(r))
   261  		if got != want {
   262  			t.Errorf("Widen().String(%U) = %+q; want %+q", r, got, want)
   263  		}
   264  	}
   265  }
   266  
   267  func TestWiden(t *testing.T) {
   268  	for _, tc := range []transformTest{{
   269  		desc:    "empty",
   270  		src:     "",
   271  		nBuf:    10,
   272  		dst:     "",
   273  		nDst:    0,
   274  		nSrc:    0,
   275  		atEOF:   false,
   276  		err:     nil,
   277  		nSpan:   0,
   278  		errSpan: nil,
   279  	}, {
   280  		desc:    "short source 1",
   281  		src:     "a\xc2",
   282  		nBuf:    10,
   283  		dst:     "a\xc2",
   284  		nDst:    3,
   285  		nSrc:    1,
   286  		atEOF:   false,
   287  		err:     transform.ErrShortSrc,
   288  		nSpan:   0,
   289  		errSpan: transform.ErrEndOfSpan,
   290  	}, {
   291  		desc:    "short source 2",
   292  		src:     "a\xe0\x80",
   293  		nBuf:    10,
   294  		dst:     "a\xe0\x80",
   295  		nDst:    3,
   296  		nSrc:    1,
   297  		atEOF:   false,
   298  		err:     transform.ErrShortSrc,
   299  		nSpan:   0,
   300  		errSpan: transform.ErrEndOfSpan,
   301  	}, {
   302  		desc:    "incomplete but terminated source 1",
   303  		src:     "a\xc2",
   304  		nBuf:    10,
   305  		dst:     "a\xc2",
   306  		nDst:    4,
   307  		nSrc:    2,
   308  		atEOF:   true,
   309  		err:     nil,
   310  		nSpan:   0,
   311  		errSpan: transform.ErrEndOfSpan,
   312  	}, {
   313  		desc:    "incomplete but terminated source 2",
   314  		src:     "a\xe0\x80",
   315  		nBuf:    10,
   316  		dst:     "a\xe0\x80",
   317  		nDst:    5,
   318  		nSrc:    3,
   319  		atEOF:   true,
   320  		err:     nil,
   321  		nSpan:   0,
   322  		errSpan: transform.ErrEndOfSpan,
   323  	}, {
   324  		desc:    "short source 1 some span",
   325  		src:     "a\xc2",
   326  		nBuf:    10,
   327  		dst:     "a\xc2",
   328  		nDst:    3,
   329  		nSrc:    3,
   330  		atEOF:   false,
   331  		err:     transform.ErrShortSrc,
   332  		nSpan:   3,
   333  		errSpan: transform.ErrShortSrc,
   334  	}, {
   335  		desc:    "short source 2 some span",
   336  		src:     "a\xe0\x80",
   337  		nBuf:    10,
   338  		dst:     "a\xe0\x80",
   339  		nDst:    3,
   340  		nSrc:    3,
   341  		atEOF:   false,
   342  		err:     transform.ErrShortSrc,
   343  		nSpan:   3,
   344  		errSpan: transform.ErrShortSrc,
   345  	}, {
   346  		desc:    "incomplete but terminated source 1 some span",
   347  		src:     "a\xc2",
   348  		nBuf:    10,
   349  		dst:     "a\xc2",
   350  		nDst:    4,
   351  		nSrc:    4,
   352  		atEOF:   true,
   353  		err:     nil,
   354  		nSpan:   4,
   355  		errSpan: nil,
   356  	}, {
   357  		desc:    "incomplete but terminated source 2 some span",
   358  		src:     "a\xe0\x80",
   359  		nBuf:    10,
   360  		dst:     "a\xe0\x80",
   361  		nDst:    5,
   362  		nSrc:    5,
   363  		atEOF:   true,
   364  		err:     nil,
   365  		nSpan:   5,
   366  		errSpan: nil,
   367  	}, {
   368  		desc:    "exact fit dst",
   369  		src:     "a!",
   370  		nBuf:    6,
   371  		dst:     "a\uff01",
   372  		nDst:    6,
   373  		nSrc:    2,
   374  		atEOF:   false,
   375  		err:     nil,
   376  		nSpan:   0,
   377  		errSpan: transform.ErrEndOfSpan,
   378  	}, {
   379  		desc:    "empty dst",
   380  		src:     "\u0300",
   381  		nBuf:    0,
   382  		dst:     "\u0300",
   383  		nDst:    0,
   384  		nSrc:    0,
   385  		atEOF:   true,
   386  		err:     transform.ErrShortDst,
   387  		nSpan:   2,
   388  		errSpan: nil,
   389  	}, {
   390  		desc:    "empty dst ascii",
   391  		src:     "a",
   392  		nBuf:    0,
   393  		dst:     "a",
   394  		nDst:    0,
   395  		nSrc:    0,
   396  		atEOF:   true,
   397  		err:     transform.ErrShortDst,
   398  		nSpan:   0,
   399  		errSpan: transform.ErrEndOfSpan,
   400  	}, {
   401  		desc:    "short dst 1",
   402  		src:     "a\uffe0",
   403  		nBuf:    4,
   404  		dst:     "a\uffe0",
   405  		nDst:    3,
   406  		nSrc:    1,
   407  		atEOF:   false,
   408  		err:     transform.ErrShortDst,
   409  		nSpan:   0,
   410  		errSpan: transform.ErrEndOfSpan,
   411  	}, {
   412  		desc:    "short dst 2",
   413  		src:     "不夠",
   414  		nBuf:    3,
   415  		dst:     "不夠",
   416  		nDst:    3,
   417  		nSrc:    3,
   418  		atEOF:   true,
   419  		err:     transform.ErrShortDst,
   420  		nSpan:   6,
   421  		errSpan: nil,
   422  	}, {
   423  		desc:    "short dst ascii",
   424  		src:     "ascii",
   425  		nBuf:    3,
   426  		dst:     "ascii", // U+ff41, ...
   427  		nDst:    3,
   428  		nSrc:    1,
   429  		atEOF:   true,
   430  		err:     transform.ErrShortDst,
   431  		nSpan:   0,
   432  		errSpan: transform.ErrEndOfSpan,
   433  	}, {
   434  		desc:    "ambiguous",
   435  		src:     "\uffe9",
   436  		nBuf:    4,
   437  		dst:     "\u2190",
   438  		nDst:    3,
   439  		nSrc:    3,
   440  		atEOF:   false,
   441  		err:     nil,
   442  		nSpan:   0,
   443  		errSpan: transform.ErrEndOfSpan,
   444  	}} {
   445  		tc.doTest(t, Widen)
   446  	}
   447  }
   448  
   449  func TestNarrowSingleRunes(t *testing.T) {
   450  	for r := rune(0); r < 0x1FFFF; r++ {
   451  		if loSurrogate <= r && r <= hiSurrogate {
   452  			continue
   453  		}
   454  		alt, _ := narrowRune(r)
   455  		want := string(alt)
   456  		got := Narrow.String(string(r))
   457  		if got != want {
   458  			t.Errorf("Narrow().String(%U) = %+q; want %+q", r, got, want)
   459  		}
   460  	}
   461  }
   462  
   463  func TestNarrow(t *testing.T) {
   464  	for _, tc := range []transformTest{{
   465  		desc:    "empty",
   466  		src:     "",
   467  		nBuf:    10,
   468  		dst:     "",
   469  		nDst:    0,
   470  		nSrc:    0,
   471  		atEOF:   false,
   472  		err:     nil,
   473  		nSpan:   0,
   474  		errSpan: nil,
   475  	}, {
   476  		desc:    "short source 1",
   477  		src:     "a\xc2",
   478  		nBuf:    10,
   479  		dst:     "a\xc2",
   480  		nDst:    1,
   481  		nSrc:    1,
   482  		atEOF:   false,
   483  		err:     transform.ErrShortSrc,
   484  		nSpan:   1,
   485  		errSpan: transform.ErrShortSrc,
   486  	}, {
   487  		desc:    "short source 2",
   488  		src:     "a\xe0\x80",
   489  		nBuf:    10,
   490  		dst:     "a\xe0\x80",
   491  		nDst:    1,
   492  		nSrc:    3,
   493  		atEOF:   false,
   494  		err:     transform.ErrShortSrc,
   495  		nSpan:   0,
   496  		errSpan: transform.ErrEndOfSpan,
   497  	}, {
   498  		desc:    "incomplete but terminated source 1",
   499  		src:     "a\xc2",
   500  		nBuf:    10,
   501  		dst:     "a\xc2",
   502  		nDst:    2,
   503  		nSrc:    4,
   504  		atEOF:   true,
   505  		err:     nil,
   506  		nSpan:   0,
   507  		errSpan: transform.ErrEndOfSpan,
   508  	}, {
   509  		desc:    "incomplete but terminated source 2",
   510  		src:     "a\xe0\x80",
   511  		nBuf:    10,
   512  		dst:     "a\xe0\x80",
   513  		nDst:    3,
   514  		nSrc:    5,
   515  		atEOF:   true,
   516  		err:     nil,
   517  		nSpan:   0,
   518  		errSpan: transform.ErrEndOfSpan,
   519  	}, {
   520  		desc:    "exact fit dst",
   521  		src:     "a\uff01",
   522  		nBuf:    2,
   523  		dst:     "a!",
   524  		nDst:    2,
   525  		nSrc:    6,
   526  		atEOF:   false,
   527  		err:     nil,
   528  		nSpan:   0,
   529  		errSpan: transform.ErrEndOfSpan,
   530  	}, {
   531  		desc:    "exact fit dst some span",
   532  		src:     "a\uff01",
   533  		nBuf:    2,
   534  		dst:     "a!",
   535  		nDst:    2,
   536  		nSrc:    4,
   537  		atEOF:   false,
   538  		err:     nil,
   539  		nSpan:   1,
   540  		errSpan: transform.ErrEndOfSpan,
   541  	}, {
   542  		desc:    "empty dst",
   543  		src:     "\u0300",
   544  		nBuf:    0,
   545  		dst:     "\u0300",
   546  		nDst:    0,
   547  		nSrc:    0,
   548  		atEOF:   true,
   549  		err:     transform.ErrShortDst,
   550  		nSpan:   2,
   551  		errSpan: nil,
   552  	}, {
   553  		desc:    "empty dst ascii",
   554  		src:     "a",
   555  		nBuf:    0,
   556  		dst:     "a",
   557  		nDst:    0,
   558  		nSrc:    0,
   559  		atEOF:   true,
   560  		err:     transform.ErrShortDst,
   561  		nSpan:   1,
   562  		errSpan: nil,
   563  	}, {
   564  		desc:    "short dst 1",
   565  		src:     "a\uffe0", // ¢
   566  		nBuf:    2,
   567  		dst:     "a\u00a2", // ¢
   568  		nDst:    1,
   569  		nSrc:    3,
   570  		atEOF:   false,
   571  		err:     transform.ErrShortDst,
   572  		nSpan:   0,
   573  		errSpan: transform.ErrEndOfSpan,
   574  	}, {
   575  		desc:    "short dst 2",
   576  		src:     "不夠",
   577  		nBuf:    3,
   578  		dst:     "不夠",
   579  		nDst:    3,
   580  		nSrc:    3,
   581  		atEOF:   true,
   582  		err:     transform.ErrShortDst,
   583  		nSpan:   6,
   584  		errSpan: nil,
   585  	}, {
   586  		// Create a narrow variant of ambiguous runes, if they exist.
   587  		desc:    "ambiguous",
   588  		src:     "\u2190",
   589  		nBuf:    4,
   590  		dst:     "\uffe9",
   591  		nDst:    3,
   592  		nSrc:    3,
   593  		atEOF:   false,
   594  		err:     nil,
   595  		nSpan:   0,
   596  		errSpan: transform.ErrEndOfSpan,
   597  	}, {
   598  		desc:    "short dst fast path",
   599  		src:     "fast",
   600  		nBuf:    3,
   601  		dst:     "fast",
   602  		nDst:    3,
   603  		nSrc:    3,
   604  		atEOF:   true,
   605  		err:     transform.ErrShortDst,
   606  		nSpan:   4,
   607  		errSpan: nil,
   608  	}, {
   609  		desc:    "short dst larger buffer",
   610  		src:     "\uff21" + strings.Repeat("0", 127) + "B",
   611  		nBuf:    128,
   612  		dst:     "A" + strings.Repeat("0", 127) + "B",
   613  		nDst:    128,
   614  		nSrc:    130,
   615  		atEOF:   true,
   616  		err:     transform.ErrShortDst,
   617  		nSpan:   0,
   618  		errSpan: transform.ErrEndOfSpan,
   619  	}, {
   620  		desc:    "fast path alternation",
   621  		src:     "fast路徑fast路徑",
   622  		nBuf:    20,
   623  		dst:     "fast路徑fast路徑",
   624  		nDst:    20,
   625  		nSrc:    20,
   626  		atEOF:   true,
   627  		err:     nil,
   628  		nSpan:   20,
   629  		errSpan: nil,
   630  	}} {
   631  		tc.doTest(t, Narrow)
   632  	}
   633  }
   634  
   635  func bench(b *testing.B, t Transformer, s string) {
   636  	dst := make([]byte, 1024)
   637  	src := []byte(s)
   638  	b.SetBytes(int64(len(src)))
   639  	b.ResetTimer()
   640  	for i := 0; i < b.N; i++ {
   641  		t.Transform(dst, src, true)
   642  	}
   643  }
   644  
   645  func changingRunes(f func(r rune) (rune, bool)) string {
   646  	buf := &bytes.Buffer{}
   647  	for r := rune(0); r <= 0xFFFF; r++ {
   648  		if _, ok := foldRune(r); ok {
   649  			buf.WriteRune(r)
   650  		}
   651  	}
   652  	return buf.String()
   653  }
   654  
   655  func BenchmarkFoldASCII(b *testing.B) {
   656  	bench(b, Fold, testtext.ASCII)
   657  }
   658  
   659  func BenchmarkFoldCJK(b *testing.B) {
   660  	bench(b, Fold, testtext.CJK)
   661  }
   662  
   663  func BenchmarkFoldNonCanonical(b *testing.B) {
   664  	bench(b, Fold, changingRunes(foldRune))
   665  }
   666  
   667  func BenchmarkFoldOther(b *testing.B) {
   668  	bench(b, Fold, testtext.TwoByteUTF8+testtext.ThreeByteUTF8)
   669  }
   670  
   671  func BenchmarkWideASCII(b *testing.B) {
   672  	bench(b, Widen, testtext.ASCII)
   673  }
   674  
   675  func BenchmarkWideCJK(b *testing.B) {
   676  	bench(b, Widen, testtext.CJK)
   677  }
   678  
   679  func BenchmarkWideNonCanonical(b *testing.B) {
   680  	bench(b, Widen, changingRunes(widenRune))
   681  }
   682  
   683  func BenchmarkWideOther(b *testing.B) {
   684  	bench(b, Widen, testtext.TwoByteUTF8+testtext.ThreeByteUTF8)
   685  }
   686  
   687  func BenchmarkNarrowASCII(b *testing.B) {
   688  	bench(b, Narrow, testtext.ASCII)
   689  }
   690  
   691  func BenchmarkNarrowCJK(b *testing.B) {
   692  	bench(b, Narrow, testtext.CJK)
   693  }
   694  
   695  func BenchmarkNarrowNonCanonical(b *testing.B) {
   696  	bench(b, Narrow, changingRunes(narrowRune))
   697  }
   698  
   699  func BenchmarkNarrowOther(b *testing.B) {
   700  	bench(b, Narrow, testtext.TwoByteUTF8+testtext.ThreeByteUTF8)
   701  }
   702  

View as plain text