Source file
src/strings/strings_test.go
1
2
3
4
5 package strings_test
6
7 import (
8 "bytes"
9 "fmt"
10 "io"
11 "math/rand"
12 "reflect"
13 "strconv"
14 . "strings"
15 "testing"
16 "unicode"
17 "unicode/utf8"
18 "unsafe"
19 )
20
21 func eq(a, b []string) bool {
22 if len(a) != len(b) {
23 return false
24 }
25 for i := 0; i < len(a); i++ {
26 if a[i] != b[i] {
27 return false
28 }
29 }
30 return true
31 }
32
33 var abcd = "abcd"
34 var faces = "☺☻☹"
35 var commas = "1,2,3,4"
36 var dots = "1....2....3....4"
37
38 type IndexTest struct {
39 s string
40 sep string
41 out int
42 }
43
44 var indexTests = []IndexTest{
45 {"", "", 0},
46 {"", "a", -1},
47 {"", "foo", -1},
48 {"fo", "foo", -1},
49 {"foo", "foo", 0},
50 {"oofofoofooo", "f", 2},
51 {"oofofoofooo", "foo", 4},
52 {"barfoobarfoo", "foo", 3},
53 {"foo", "", 0},
54 {"foo", "o", 1},
55 {"abcABCabc", "A", 3},
56
57 {"", "a", -1},
58 {"x", "a", -1},
59 {"x", "x", 0},
60 {"abc", "a", 0},
61 {"abc", "b", 1},
62 {"abc", "c", 2},
63 {"abc", "x", -1},
64
65 {"", "ab", -1},
66 {"bc", "ab", -1},
67 {"ab", "ab", 0},
68 {"xab", "ab", 1},
69 {"xab"[:2], "ab", -1},
70 {"", "abc", -1},
71 {"xbc", "abc", -1},
72 {"abc", "abc", 0},
73 {"xabc", "abc", 1},
74 {"xabc"[:3], "abc", -1},
75 {"xabxc", "abc", -1},
76 {"", "abcd", -1},
77 {"xbcd", "abcd", -1},
78 {"abcd", "abcd", 0},
79 {"xabcd", "abcd", 1},
80 {"xyabcd"[:5], "abcd", -1},
81 {"xbcqq", "abcqq", -1},
82 {"abcqq", "abcqq", 0},
83 {"xabcqq", "abcqq", 1},
84 {"xyabcqq"[:6], "abcqq", -1},
85 {"xabxcqq", "abcqq", -1},
86 {"xabcqxq", "abcqq", -1},
87 {"", "01234567", -1},
88 {"32145678", "01234567", -1},
89 {"01234567", "01234567", 0},
90 {"x01234567", "01234567", 1},
91 {"x0123456x01234567", "01234567", 9},
92 {"xx01234567"[:9], "01234567", -1},
93 {"", "0123456789", -1},
94 {"3214567844", "0123456789", -1},
95 {"0123456789", "0123456789", 0},
96 {"x0123456789", "0123456789", 1},
97 {"x012345678x0123456789", "0123456789", 11},
98 {"xyz0123456789"[:12], "0123456789", -1},
99 {"x01234567x89", "0123456789", -1},
100 {"", "0123456789012345", -1},
101 {"3214567889012345", "0123456789012345", -1},
102 {"0123456789012345", "0123456789012345", 0},
103 {"x0123456789012345", "0123456789012345", 1},
104 {"x012345678901234x0123456789012345", "0123456789012345", 17},
105 {"", "01234567890123456789", -1},
106 {"32145678890123456789", "01234567890123456789", -1},
107 {"01234567890123456789", "01234567890123456789", 0},
108 {"x01234567890123456789", "01234567890123456789", 1},
109 {"x0123456789012345678x01234567890123456789", "01234567890123456789", 21},
110 {"xyz01234567890123456789"[:22], "01234567890123456789", -1},
111 {"", "0123456789012345678901234567890", -1},
112 {"321456788901234567890123456789012345678911", "0123456789012345678901234567890", -1},
113 {"0123456789012345678901234567890", "0123456789012345678901234567890", 0},
114 {"x0123456789012345678901234567890", "0123456789012345678901234567890", 1},
115 {"x012345678901234567890123456789x0123456789012345678901234567890", "0123456789012345678901234567890", 32},
116 {"xyz0123456789012345678901234567890"[:33], "0123456789012345678901234567890", -1},
117 {"", "01234567890123456789012345678901", -1},
118 {"32145678890123456789012345678901234567890211", "01234567890123456789012345678901", -1},
119 {"01234567890123456789012345678901", "01234567890123456789012345678901", 0},
120 {"x01234567890123456789012345678901", "01234567890123456789012345678901", 1},
121 {"x0123456789012345678901234567890x01234567890123456789012345678901", "01234567890123456789012345678901", 33},
122 {"xyz01234567890123456789012345678901"[:34], "01234567890123456789012345678901", -1},
123 {"xxxxxx012345678901234567890123456789012345678901234567890123456789012", "012345678901234567890123456789012345678901234567890123456789012", 6},
124 {"", "0123456789012345678901234567890123456789", -1},
125 {"xx012345678901234567890123456789012345678901234567890123456789012", "0123456789012345678901234567890123456789", 2},
126 {"xx012345678901234567890123456789012345678901234567890123456789012"[:41], "0123456789012345678901234567890123456789", -1},
127 {"xx012345678901234567890123456789012345678901234567890123456789012", "0123456789012345678901234567890123456xxx", -1},
128 {"xx0123456789012345678901234567890123456789012345678901234567890120123456789012345678901234567890123456xxx", "0123456789012345678901234567890123456xxx", 65},
129
130 {"oxoxoxoxoxoxoxoxoxoxoxoy", "oy", 22},
131 {"oxoxoxoxoxoxoxoxoxoxoxox", "oy", -1},
132 }
133
134 var lastIndexTests = []IndexTest{
135 {"", "", 0},
136 {"", "a", -1},
137 {"", "foo", -1},
138 {"fo", "foo", -1},
139 {"foo", "foo", 0},
140 {"foo", "f", 0},
141 {"oofofoofooo", "f", 7},
142 {"oofofoofooo", "foo", 7},
143 {"barfoobarfoo", "foo", 9},
144 {"foo", "", 3},
145 {"foo", "o", 2},
146 {"abcABCabc", "A", 3},
147 {"abcABCabc", "a", 6},
148 }
149
150 var indexAnyTests = []IndexTest{
151 {"", "", -1},
152 {"", "a", -1},
153 {"", "abc", -1},
154 {"a", "", -1},
155 {"a", "a", 0},
156 {"\x80", "\xffb", 0},
157 {"aaa", "a", 0},
158 {"abc", "xyz", -1},
159 {"abc", "xcz", 2},
160 {"ab☺c", "x☺yz", 2},
161 {"a☺b☻c☹d", "cx", len("a☺b☻")},
162 {"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
163 {"aRegExp*", ".(|)*+?^$[]", 7},
164 {dots + dots + dots, " ", -1},
165 {"012abcba210", "\xffb", 4},
166 {"012\x80bcb\x80210", "\xffb", 3},
167 {"0123456\xcf\x80abc", "\xcfb\x80", 10},
168 }
169
170 var lastIndexAnyTests = []IndexTest{
171 {"", "", -1},
172 {"", "a", -1},
173 {"", "abc", -1},
174 {"a", "", -1},
175 {"a", "a", 0},
176 {"\x80", "\xffb", 0},
177 {"aaa", "a", 2},
178 {"abc", "xyz", -1},
179 {"abc", "ab", 1},
180 {"ab☺c", "x☺yz", 2},
181 {"a☺b☻c☹d", "cx", len("a☺b☻")},
182 {"a☺b☻c☹d", "uvw☻xyz", len("a☺b")},
183 {"a.RegExp*", ".(|)*+?^$[]", 8},
184 {dots + dots + dots, " ", -1},
185 {"012abcba210", "\xffb", 6},
186 {"012\x80bcb\x80210", "\xffb", 7},
187 {"0123456\xcf\x80abc", "\xcfb\x80", 10},
188 }
189
190
191
192 func runIndexTests(t *testing.T, f func(s, sep string) int, funcName string, testCases []IndexTest) {
193 for _, test := range testCases {
194 actual := f(test.s, test.sep)
195 if actual != test.out {
196 t.Errorf("%s(%q,%q) = %v; want %v", funcName, test.s, test.sep, actual, test.out)
197 }
198 }
199 }
200
201 func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) }
202 func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) }
203 func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) }
204 func TestLastIndexAny(t *testing.T) {
205 runIndexTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests)
206 }
207
208 func TestIndexByte(t *testing.T) {
209 for _, tt := range indexTests {
210 if len(tt.sep) != 1 {
211 continue
212 }
213 pos := IndexByte(tt.s, tt.sep[0])
214 if pos != tt.out {
215 t.Errorf(`IndexByte(%q, %q) = %v; want %v`, tt.s, tt.sep[0], pos, tt.out)
216 }
217 }
218 }
219
220 func TestLastIndexByte(t *testing.T) {
221 testCases := []IndexTest{
222 {"", "q", -1},
223 {"abcdef", "q", -1},
224 {"abcdefabcdef", "a", len("abcdef")},
225 {"abcdefabcdef", "f", len("abcdefabcde")},
226 {"zabcdefabcdef", "z", 0},
227 {"a☺b☻c☹d", "b", len("a☺")},
228 }
229 for _, test := range testCases {
230 actual := LastIndexByte(test.s, test.sep[0])
231 if actual != test.out {
232 t.Errorf("LastIndexByte(%q,%c) = %v; want %v", test.s, test.sep[0], actual, test.out)
233 }
234 }
235 }
236
237 func simpleIndex(s, sep string) int {
238 n := len(sep)
239 for i := n; i <= len(s); i++ {
240 if s[i-n:i] == sep {
241 return i - n
242 }
243 }
244 return -1
245 }
246
247 func TestIndexRandom(t *testing.T) {
248 const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
249 for times := 0; times < 10; times++ {
250 for strLen := 5 + rand.Intn(5); strLen < 140; strLen += 10 {
251 s1 := make([]byte, strLen)
252 for i := range s1 {
253 s1[i] = chars[rand.Intn(len(chars))]
254 }
255 s := string(s1)
256 for i := 0; i < 50; i++ {
257 begin := rand.Intn(len(s) + 1)
258 end := begin + rand.Intn(len(s)+1-begin)
259 sep := s[begin:end]
260 if i%4 == 0 {
261 pos := rand.Intn(len(sep) + 1)
262 sep = sep[:pos] + "A" + sep[pos:]
263 }
264 want := simpleIndex(s, sep)
265 res := Index(s, sep)
266 if res != want {
267 t.Errorf("Index(%s,%s) = %d; want %d", s, sep, res, want)
268 }
269 }
270 }
271 }
272 }
273
274 func TestIndexRune(t *testing.T) {
275 tests := []struct {
276 in string
277 rune rune
278 want int
279 }{
280 {"", 'a', -1},
281 {"", '☺', -1},
282 {"foo", '☹', -1},
283 {"foo", 'o', 1},
284 {"foo☺bar", '☺', 3},
285 {"foo☺☻☹bar", '☹', 9},
286 {"a A x", 'A', 2},
287 {"some_text=some_value", '=', 9},
288 {"☺a", 'a', 3},
289 {"a☻☺b", '☺', 4},
290
291
292 {"�", '�', 0},
293 {"\xff", '�', 0},
294 {"☻x�", '�', len("☻x")},
295 {"☻x\xe2\x98", '�', len("☻x")},
296 {"☻x\xe2\x98�", '�', len("☻x")},
297 {"☻x\xe2\x98x", '�', len("☻x")},
298
299
300 {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", -1, -1},
301 {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", 0xD800, -1},
302 {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", utf8.MaxRune + 1, -1},
303 }
304 for _, tt := range tests {
305 if got := IndexRune(tt.in, tt.rune); got != tt.want {
306 t.Errorf("IndexRune(%q, %d) = %v; want %v", tt.in, tt.rune, got, tt.want)
307 }
308 }
309
310 haystack := "test世界"
311 allocs := testing.AllocsPerRun(1000, func() {
312 if i := IndexRune(haystack, 's'); i != 2 {
313 t.Fatalf("'s' at %d; want 2", i)
314 }
315 if i := IndexRune(haystack, '世'); i != 4 {
316 t.Fatalf("'世' at %d; want 4", i)
317 }
318 })
319 if allocs != 0 && testing.CoverMode() == "" {
320 t.Errorf("expected no allocations, got %f", allocs)
321 }
322 }
323
324 const benchmarkString = "some_text=some☺value"
325
326 func BenchmarkIndexRune(b *testing.B) {
327 if got := IndexRune(benchmarkString, '☺'); got != 14 {
328 b.Fatalf("wrong index: expected 14, got=%d", got)
329 }
330 for i := 0; i < b.N; i++ {
331 IndexRune(benchmarkString, '☺')
332 }
333 }
334
335 var benchmarkLongString = Repeat(" ", 100) + benchmarkString
336
337 func BenchmarkIndexRuneLongString(b *testing.B) {
338 if got := IndexRune(benchmarkLongString, '☺'); got != 114 {
339 b.Fatalf("wrong index: expected 114, got=%d", got)
340 }
341 for i := 0; i < b.N; i++ {
342 IndexRune(benchmarkLongString, '☺')
343 }
344 }
345
346 func BenchmarkIndexRuneFastPath(b *testing.B) {
347 if got := IndexRune(benchmarkString, 'v'); got != 17 {
348 b.Fatalf("wrong index: expected 17, got=%d", got)
349 }
350 for i := 0; i < b.N; i++ {
351 IndexRune(benchmarkString, 'v')
352 }
353 }
354
355 func BenchmarkIndex(b *testing.B) {
356 if got := Index(benchmarkString, "v"); got != 17 {
357 b.Fatalf("wrong index: expected 17, got=%d", got)
358 }
359 for i := 0; i < b.N; i++ {
360 Index(benchmarkString, "v")
361 }
362 }
363
364 func BenchmarkLastIndex(b *testing.B) {
365 if got := Index(benchmarkString, "v"); got != 17 {
366 b.Fatalf("wrong index: expected 17, got=%d", got)
367 }
368 for i := 0; i < b.N; i++ {
369 LastIndex(benchmarkString, "v")
370 }
371 }
372
373 func BenchmarkIndexByte(b *testing.B) {
374 if got := IndexByte(benchmarkString, 'v'); got != 17 {
375 b.Fatalf("wrong index: expected 17, got=%d", got)
376 }
377 for i := 0; i < b.N; i++ {
378 IndexByte(benchmarkString, 'v')
379 }
380 }
381
382 type SplitTest struct {
383 s string
384 sep string
385 n int
386 a []string
387 }
388
389 var splittests = []SplitTest{
390 {"", "", -1, []string{}},
391 {abcd, "", 2, []string{"a", "bcd"}},
392 {abcd, "", 4, []string{"a", "b", "c", "d"}},
393 {abcd, "", -1, []string{"a", "b", "c", "d"}},
394 {faces, "", -1, []string{"☺", "☻", "☹"}},
395 {faces, "", 3, []string{"☺", "☻", "☹"}},
396 {faces, "", 17, []string{"☺", "☻", "☹"}},
397 {"☺�☹", "", -1, []string{"☺", "�", "☹"}},
398 {abcd, "a", 0, nil},
399 {abcd, "a", -1, []string{"", "bcd"}},
400 {abcd, "z", -1, []string{"abcd"}},
401 {commas, ",", -1, []string{"1", "2", "3", "4"}},
402 {dots, "...", -1, []string{"1", ".2", ".3", ".4"}},
403 {faces, "☹", -1, []string{"☺☻", ""}},
404 {faces, "~", -1, []string{faces}},
405 {"1 2 3 4", " ", 3, []string{"1", "2", "3 4"}},
406 {"1 2", " ", 3, []string{"1", "2"}},
407 }
408
409 func TestSplit(t *testing.T) {
410 for _, tt := range splittests {
411 a := SplitN(tt.s, tt.sep, tt.n)
412 if !eq(a, tt.a) {
413 t.Errorf("Split(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, a, tt.a)
414 continue
415 }
416 if tt.n == 0 {
417 continue
418 }
419 s := Join(a, tt.sep)
420 if s != tt.s {
421 t.Errorf("Join(Split(%q, %q, %d), %q) = %q", tt.s, tt.sep, tt.n, tt.sep, s)
422 }
423 if tt.n < 0 {
424 b := Split(tt.s, tt.sep)
425 if !reflect.DeepEqual(a, b) {
426 t.Errorf("Split disagrees with SplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
427 }
428 }
429 }
430 }
431
432 var splitaftertests = []SplitTest{
433 {abcd, "a", -1, []string{"a", "bcd"}},
434 {abcd, "z", -1, []string{"abcd"}},
435 {abcd, "", -1, []string{"a", "b", "c", "d"}},
436 {commas, ",", -1, []string{"1,", "2,", "3,", "4"}},
437 {dots, "...", -1, []string{"1...", ".2...", ".3...", ".4"}},
438 {faces, "☹", -1, []string{"☺☻☹", ""}},
439 {faces, "~", -1, []string{faces}},
440 {faces, "", -1, []string{"☺", "☻", "☹"}},
441 {"1 2 3 4", " ", 3, []string{"1 ", "2 ", "3 4"}},
442 {"1 2 3", " ", 3, []string{"1 ", "2 ", "3"}},
443 {"1 2", " ", 3, []string{"1 ", "2"}},
444 {"123", "", 2, []string{"1", "23"}},
445 {"123", "", 17, []string{"1", "2", "3"}},
446 }
447
448 func TestSplitAfter(t *testing.T) {
449 for _, tt := range splitaftertests {
450 a := SplitAfterN(tt.s, tt.sep, tt.n)
451 if !eq(a, tt.a) {
452 t.Errorf(`Split(%q, %q, %d) = %v; want %v`, tt.s, tt.sep, tt.n, a, tt.a)
453 continue
454 }
455 s := Join(a, "")
456 if s != tt.s {
457 t.Errorf(`Join(Split(%q, %q, %d), %q) = %q`, tt.s, tt.sep, tt.n, tt.sep, s)
458 }
459 if tt.n < 0 {
460 b := SplitAfter(tt.s, tt.sep)
461 if !reflect.DeepEqual(a, b) {
462 t.Errorf("SplitAfter disagrees with SplitAfterN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
463 }
464 }
465 }
466 }
467
468 type FieldsTest struct {
469 s string
470 a []string
471 }
472
473 var fieldstests = []FieldsTest{
474 {"", []string{}},
475 {" ", []string{}},
476 {" \t ", []string{}},
477 {"\u2000", []string{}},
478 {" abc ", []string{"abc"}},
479 {"1 2 3 4", []string{"1", "2", "3", "4"}},
480 {"1 2 3 4", []string{"1", "2", "3", "4"}},
481 {"1\t\t2\t\t3\t4", []string{"1", "2", "3", "4"}},
482 {"1\u20002\u20013\u20024", []string{"1", "2", "3", "4"}},
483 {"\u2000\u2001\u2002", []string{}},
484 {"\n™\t™\n", []string{"™", "™"}},
485 {"\n\u20001™2\u2000 \u2001 ™", []string{"1™2", "™"}},
486 {"\n1\uFFFD \uFFFD2\u20003\uFFFD4", []string{"1\uFFFD", "\uFFFD2", "3\uFFFD4"}},
487 {"1\xFF\u2000\xFF2\xFF \xFF", []string{"1\xFF", "\xFF2\xFF", "\xFF"}},
488 {faces, []string{faces}},
489 }
490
491 func TestFields(t *testing.T) {
492 for _, tt := range fieldstests {
493 a := Fields(tt.s)
494 if !eq(a, tt.a) {
495 t.Errorf("Fields(%q) = %v; want %v", tt.s, a, tt.a)
496 continue
497 }
498 }
499 }
500
501 var FieldsFuncTests = []FieldsTest{
502 {"", []string{}},
503 {"XX", []string{}},
504 {"XXhiXXX", []string{"hi"}},
505 {"aXXbXXXcX", []string{"a", "b", "c"}},
506 }
507
508 func TestFieldsFunc(t *testing.T) {
509 for _, tt := range fieldstests {
510 a := FieldsFunc(tt.s, unicode.IsSpace)
511 if !eq(a, tt.a) {
512 t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a)
513 continue
514 }
515 }
516 pred := func(c rune) bool { return c == 'X' }
517 for _, tt := range FieldsFuncTests {
518 a := FieldsFunc(tt.s, pred)
519 if !eq(a, tt.a) {
520 t.Errorf("FieldsFunc(%q) = %v, want %v", tt.s, a, tt.a)
521 }
522 }
523 }
524
525
526 type StringTest struct {
527 in, out string
528 }
529
530
531
532 func runStringTests(t *testing.T, f func(string) string, funcName string, testCases []StringTest) {
533 for _, tc := range testCases {
534 actual := f(tc.in)
535 if actual != tc.out {
536 t.Errorf("%s(%q) = %q; want %q", funcName, tc.in, actual, tc.out)
537 }
538 }
539 }
540
541 var upperTests = []StringTest{
542 {"", ""},
543 {"ONLYUPPER", "ONLYUPPER"},
544 {"abc", "ABC"},
545 {"AbC123", "ABC123"},
546 {"azAZ09_", "AZAZ09_"},
547 {"longStrinGwitHmixofsmaLLandcAps", "LONGSTRINGWITHMIXOFSMALLANDCAPS"},
548 {"long\u0250string\u0250with\u0250nonascii\u2C6Fchars", "LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS"},
549 {"\u0250\u0250\u0250\u0250\u0250", "\u2C6F\u2C6F\u2C6F\u2C6F\u2C6F"},
550 {"a\u0080\U0010FFFF", "A\u0080\U0010FFFF"},
551 }
552
553 var lowerTests = []StringTest{
554 {"", ""},
555 {"abc", "abc"},
556 {"AbC123", "abc123"},
557 {"azAZ09_", "azaz09_"},
558 {"longStrinGwitHmixofsmaLLandcAps", "longstringwithmixofsmallandcaps"},
559 {"LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS", "long\u0250string\u0250with\u0250nonascii\u0250chars"},
560 {"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"},
561 {"A\u0080\U0010FFFF", "a\u0080\U0010FFFF"},
562 }
563
564 const space = "\t\v\r\f\n\u0085\u00a0\u2000\u3000"
565
566 var trimSpaceTests = []StringTest{
567 {"", ""},
568 {"abc", "abc"},
569 {space + "abc" + space, "abc"},
570 {" ", ""},
571 {" \t\r\n \t\t\r\r\n\n ", ""},
572 {" \t\r\n x\t\t\r\r\n\n ", "x"},
573 {" \u2000\t\r\n x\t\t\r\r\ny\n \u3000", "x\t\t\r\r\ny"},
574 {"1 \t\r\n2", "1 \t\r\n2"},
575 {" x\x80", "x\x80"},
576 {" x\xc0", "x\xc0"},
577 {"x \xc0\xc0 ", "x \xc0\xc0"},
578 {"x \xc0", "x \xc0"},
579 {"x \xc0 ", "x \xc0"},
580 {"x \xc0\xc0 ", "x \xc0\xc0"},
581 {"x ☺\xc0\xc0 ", "x ☺\xc0\xc0"},
582 {"x ☺ ", "x ☺"},
583 }
584
585 func tenRunes(ch rune) string {
586 r := make([]rune, 10)
587 for i := range r {
588 r[i] = ch
589 }
590 return string(r)
591 }
592
593
594 func rot13(r rune) rune {
595 step := rune(13)
596 if r >= 'a' && r <= 'z' {
597 return ((r - 'a' + step) % 26) + 'a'
598 }
599 if r >= 'A' && r <= 'Z' {
600 return ((r - 'A' + step) % 26) + 'A'
601 }
602 return r
603 }
604
605 func TestMap(t *testing.T) {
606
607 a := tenRunes('a')
608
609 maxRune := func(rune) rune { return unicode.MaxRune }
610 m := Map(maxRune, a)
611 expect := tenRunes(unicode.MaxRune)
612 if m != expect {
613 t.Errorf("growing: expected %q got %q", expect, m)
614 }
615
616
617 minRune := func(rune) rune { return 'a' }
618 m = Map(minRune, tenRunes(unicode.MaxRune))
619 expect = a
620 if m != expect {
621 t.Errorf("shrinking: expected %q got %q", expect, m)
622 }
623
624
625 m = Map(rot13, "a to zed")
626 expect = "n gb mrq"
627 if m != expect {
628 t.Errorf("rot13: expected %q got %q", expect, m)
629 }
630
631
632 m = Map(rot13, Map(rot13, "a to zed"))
633 expect = "a to zed"
634 if m != expect {
635 t.Errorf("rot13: expected %q got %q", expect, m)
636 }
637
638
639 dropNotLatin := func(r rune) rune {
640 if unicode.Is(unicode.Latin, r) {
641 return r
642 }
643 return -1
644 }
645 m = Map(dropNotLatin, "Hello, 세계")
646 expect = "Hello"
647 if m != expect {
648 t.Errorf("drop: expected %q got %q", expect, m)
649 }
650
651
652 identity := func(r rune) rune {
653 return r
654 }
655 orig := "Input string that we expect not to be copied."
656 m = Map(identity, orig)
657 if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data !=
658 (*reflect.StringHeader)(unsafe.Pointer(&m)).Data {
659 t.Error("unexpected copy during identity map")
660 }
661
662
663 replaceNotLatin := func(r rune) rune {
664 if unicode.Is(unicode.Latin, r) {
665 return r
666 }
667 return utf8.RuneError
668 }
669 m = Map(replaceNotLatin, "Hello\255World")
670 expect = "Hello\uFFFDWorld"
671 if m != expect {
672 t.Errorf("replace invalid sequence: expected %q got %q", expect, m)
673 }
674
675
676 encode := func(r rune) rune {
677 switch r {
678 case utf8.RuneSelf:
679 return unicode.MaxRune
680 case unicode.MaxRune:
681 return utf8.RuneSelf
682 }
683 return r
684 }
685 s := string(rune(utf8.RuneSelf)) + string(utf8.MaxRune)
686 r := string(utf8.MaxRune) + string(rune(utf8.RuneSelf))
687 m = Map(encode, s)
688 if m != r {
689 t.Errorf("encoding not handled correctly: expected %q got %q", r, m)
690 }
691 m = Map(encode, r)
692 if m != s {
693 t.Errorf("encoding not handled correctly: expected %q got %q", s, m)
694 }
695
696
697 trimSpaces := func(r rune) rune {
698 if unicode.IsSpace(r) {
699 return -1
700 }
701 return r
702 }
703 m = Map(trimSpaces, " abc 123 ")
704 expect = "abc123"
705 if m != expect {
706 t.Errorf("trimSpaces: expected %q got %q", expect, m)
707 }
708 }
709
710 func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
711
712 func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) }
713
714 var toValidUTF8Tests = []struct {
715 in string
716 repl string
717 out string
718 }{
719 {"", "\uFFFD", ""},
720 {"abc", "\uFFFD", "abc"},
721 {"\uFDDD", "\uFFFD", "\uFDDD"},
722 {"a\xffb", "\uFFFD", "a\uFFFDb"},
723 {"a\xffb\uFFFD", "X", "aXb\uFFFD"},
724 {"a☺\xffb☺\xC0\xAFc☺\xff", "", "a☺b☺c☺"},
725 {"a☺\xffb☺\xC0\xAFc☺\xff", "日本語", "a☺日本語b☺日本語c☺日本語"},
726 {"\xC0\xAF", "\uFFFD", "\uFFFD"},
727 {"\xE0\x80\xAF", "\uFFFD", "\uFFFD"},
728 {"\xed\xa0\x80", "abc", "abc"},
729 {"\xed\xbf\xbf", "\uFFFD", "\uFFFD"},
730 {"\xF0\x80\x80\xaf", "☺", "☺"},
731 {"\xF8\x80\x80\x80\xAF", "\uFFFD", "\uFFFD"},
732 {"\xFC\x80\x80\x80\x80\xAF", "\uFFFD", "\uFFFD"},
733 }
734
735 func TestToValidUTF8(t *testing.T) {
736 for _, tc := range toValidUTF8Tests {
737 got := ToValidUTF8(tc.in, tc.repl)
738 if got != tc.out {
739 t.Errorf("ToValidUTF8(%q, %q) = %q; want %q", tc.in, tc.repl, got, tc.out)
740 }
741 }
742 }
743
744 func BenchmarkToUpper(b *testing.B) {
745 for _, tc := range upperTests {
746 b.Run(tc.in, func(b *testing.B) {
747 for i := 0; i < b.N; i++ {
748 actual := ToUpper(tc.in)
749 if actual != tc.out {
750 b.Errorf("ToUpper(%q) = %q; want %q", tc.in, actual, tc.out)
751 }
752 }
753 })
754 }
755 }
756
757 func BenchmarkToLower(b *testing.B) {
758 for _, tc := range lowerTests {
759 b.Run(tc.in, func(b *testing.B) {
760 for i := 0; i < b.N; i++ {
761 actual := ToLower(tc.in)
762 if actual != tc.out {
763 b.Errorf("ToLower(%q) = %q; want %q", tc.in, actual, tc.out)
764 }
765 }
766 })
767 }
768 }
769
770 func BenchmarkMapNoChanges(b *testing.B) {
771 identity := func(r rune) rune {
772 return r
773 }
774 for i := 0; i < b.N; i++ {
775 Map(identity, "Some string that won't be modified.")
776 }
777 }
778
779 func TestSpecialCase(t *testing.T) {
780 lower := "abcçdefgğhıijklmnoöprsştuüvyz"
781 upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ"
782 u := ToUpperSpecial(unicode.TurkishCase, upper)
783 if u != upper {
784 t.Errorf("Upper(upper) is %s not %s", u, upper)
785 }
786 u = ToUpperSpecial(unicode.TurkishCase, lower)
787 if u != upper {
788 t.Errorf("Upper(lower) is %s not %s", u, upper)
789 }
790 l := ToLowerSpecial(unicode.TurkishCase, lower)
791 if l != lower {
792 t.Errorf("Lower(lower) is %s not %s", l, lower)
793 }
794 l = ToLowerSpecial(unicode.TurkishCase, upper)
795 if l != lower {
796 t.Errorf("Lower(upper) is %s not %s", l, lower)
797 }
798 }
799
800 func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) }
801
802 var trimTests = []struct {
803 f string
804 in, arg, out string
805 }{
806 {"Trim", "abba", "a", "bb"},
807 {"Trim", "abba", "ab", ""},
808 {"TrimLeft", "abba", "ab", ""},
809 {"TrimRight", "abba", "ab", ""},
810 {"TrimLeft", "abba", "a", "bba"},
811 {"TrimLeft", "abba", "b", "abba"},
812 {"TrimRight", "abba", "a", "abb"},
813 {"TrimRight", "abba", "b", "abba"},
814 {"Trim", "<tag>", "<>", "tag"},
815 {"Trim", "* listitem", " *", "listitem"},
816 {"Trim", `"quote"`, `"`, "quote"},
817 {"Trim", "\u2C6F\u2C6F\u0250\u0250\u2C6F\u2C6F", "\u2C6F", "\u0250\u0250"},
818 {"Trim", "\x80test\xff", "\xff", "test"},
819 {"Trim", " Ġ ", " ", "Ġ"},
820 {"Trim", " Ġİ0", "0 ", "Ġİ"},
821
822 {"Trim", "abba", "", "abba"},
823 {"Trim", "", "123", ""},
824 {"Trim", "", "", ""},
825 {"TrimLeft", "abba", "", "abba"},
826 {"TrimLeft", "", "123", ""},
827 {"TrimLeft", "", "", ""},
828 {"TrimRight", "abba", "", "abba"},
829 {"TrimRight", "", "123", ""},
830 {"TrimRight", "", "", ""},
831 {"TrimRight", "☺\xc0", "☺", "☺\xc0"},
832 {"TrimPrefix", "aabb", "a", "abb"},
833 {"TrimPrefix", "aabb", "b", "aabb"},
834 {"TrimSuffix", "aabb", "a", "aabb"},
835 {"TrimSuffix", "aabb", "b", "aab"},
836 }
837
838 func TestTrim(t *testing.T) {
839 for _, tc := range trimTests {
840 name := tc.f
841 var f func(string, string) string
842 switch name {
843 case "Trim":
844 f = Trim
845 case "TrimLeft":
846 f = TrimLeft
847 case "TrimRight":
848 f = TrimRight
849 case "TrimPrefix":
850 f = TrimPrefix
851 case "TrimSuffix":
852 f = TrimSuffix
853 default:
854 t.Errorf("Undefined trim function %s", name)
855 }
856 actual := f(tc.in, tc.arg)
857 if actual != tc.out {
858 t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
859 }
860 }
861 }
862
863 func BenchmarkTrim(b *testing.B) {
864 b.ReportAllocs()
865
866 for i := 0; i < b.N; i++ {
867 for _, tc := range trimTests {
868 name := tc.f
869 var f func(string, string) string
870 switch name {
871 case "Trim":
872 f = Trim
873 case "TrimLeft":
874 f = TrimLeft
875 case "TrimRight":
876 f = TrimRight
877 case "TrimPrefix":
878 f = TrimPrefix
879 case "TrimSuffix":
880 f = TrimSuffix
881 default:
882 b.Errorf("Undefined trim function %s", name)
883 }
884 actual := f(tc.in, tc.arg)
885 if actual != tc.out {
886 b.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
887 }
888 }
889 }
890 }
891
892 func BenchmarkToValidUTF8(b *testing.B) {
893 tests := []struct {
894 name string
895 input string
896 }{
897 {"Valid", "typical"},
898 {"InvalidASCII", "foo\xffbar"},
899 {"InvalidNonASCII", "日本語\xff日本語"},
900 }
901 replacement := "\uFFFD"
902 b.ResetTimer()
903 for _, test := range tests {
904 b.Run(test.name, func(b *testing.B) {
905 for i := 0; i < b.N; i++ {
906 ToValidUTF8(test.input, replacement)
907 }
908 })
909 }
910 }
911
912 type predicate struct {
913 f func(rune) bool
914 name string
915 }
916
917 var isSpace = predicate{unicode.IsSpace, "IsSpace"}
918 var isDigit = predicate{unicode.IsDigit, "IsDigit"}
919 var isUpper = predicate{unicode.IsUpper, "IsUpper"}
920 var isValidRune = predicate{
921 func(r rune) bool {
922 return r != utf8.RuneError
923 },
924 "IsValidRune",
925 }
926
927 func not(p predicate) predicate {
928 return predicate{
929 func(r rune) bool {
930 return !p.f(r)
931 },
932 "not " + p.name,
933 }
934 }
935
936 var trimFuncTests = []struct {
937 f predicate
938 in string
939 trimOut string
940 leftOut string
941 rightOut string
942 }{
943 {isSpace, space + " hello " + space,
944 "hello",
945 "hello " + space,
946 space + " hello"},
947 {isDigit, "\u0e50\u0e5212hello34\u0e50\u0e51",
948 "hello",
949 "hello34\u0e50\u0e51",
950 "\u0e50\u0e5212hello"},
951 {isUpper, "\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F",
952 "hello",
953 "helloEF\u2C6F\u2C6FGH\u2C6F\u2C6F",
954 "\u2C6F\u2C6F\u2C6F\u2C6FABCDhello"},
955 {not(isSpace), "hello" + space + "hello",
956 space,
957 space + "hello",
958 "hello" + space},
959 {not(isDigit), "hello\u0e50\u0e521234\u0e50\u0e51helo",
960 "\u0e50\u0e521234\u0e50\u0e51",
961 "\u0e50\u0e521234\u0e50\u0e51helo",
962 "hello\u0e50\u0e521234\u0e50\u0e51"},
963 {isValidRune, "ab\xc0a\xc0cd",
964 "\xc0a\xc0",
965 "\xc0a\xc0cd",
966 "ab\xc0a\xc0"},
967 {not(isValidRune), "\xc0a\xc0",
968 "a",
969 "a\xc0",
970 "\xc0a"},
971 {isSpace, "",
972 "",
973 "",
974 ""},
975 {isSpace, " ",
976 "",
977 "",
978 ""},
979 }
980
981 func TestTrimFunc(t *testing.T) {
982 for _, tc := range trimFuncTests {
983 trimmers := []struct {
984 name string
985 trim func(s string, f func(r rune) bool) string
986 out string
987 }{
988 {"TrimFunc", TrimFunc, tc.trimOut},
989 {"TrimLeftFunc", TrimLeftFunc, tc.leftOut},
990 {"TrimRightFunc", TrimRightFunc, tc.rightOut},
991 }
992 for _, trimmer := range trimmers {
993 actual := trimmer.trim(tc.in, tc.f.f)
994 if actual != trimmer.out {
995 t.Errorf("%s(%q, %q) = %q; want %q", trimmer.name, tc.in, tc.f.name, actual, trimmer.out)
996 }
997 }
998 }
999 }
1000
1001 var indexFuncTests = []struct {
1002 in string
1003 f predicate
1004 first, last int
1005 }{
1006 {"", isValidRune, -1, -1},
1007 {"abc", isDigit, -1, -1},
1008 {"0123", isDigit, 0, 3},
1009 {"a1b", isDigit, 1, 1},
1010 {space, isSpace, 0, len(space) - 3},
1011 {"\u0e50\u0e5212hello34\u0e50\u0e51", isDigit, 0, 18},
1012 {"\u2C6F\u2C6F\u2C6F\u2C6FABCDhelloEF\u2C6F\u2C6FGH\u2C6F\u2C6F", isUpper, 0, 34},
1013 {"12\u0e50\u0e52hello34\u0e50\u0e51", not(isDigit), 8, 12},
1014
1015
1016 {"\x801", isDigit, 1, 1},
1017 {"\x80abc", isDigit, -1, -1},
1018 {"\xc0a\xc0", isValidRune, 1, 1},
1019 {"\xc0a\xc0", not(isValidRune), 0, 2},
1020 {"\xc0☺\xc0", not(isValidRune), 0, 4},
1021 {"\xc0☺\xc0\xc0", not(isValidRune), 0, 5},
1022 {"ab\xc0a\xc0cd", not(isValidRune), 2, 4},
1023 {"a\xe0\x80cd", not(isValidRune), 1, 2},
1024 {"\x80\x80\x80\x80", not(isValidRune), 0, 3},
1025 }
1026
1027 func TestIndexFunc(t *testing.T) {
1028 for _, tc := range indexFuncTests {
1029 first := IndexFunc(tc.in, tc.f.f)
1030 if first != tc.first {
1031 t.Errorf("IndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, first, tc.first)
1032 }
1033 last := LastIndexFunc(tc.in, tc.f.f)
1034 if last != tc.last {
1035 t.Errorf("LastIndexFunc(%q, %s) = %d; want %d", tc.in, tc.f.name, last, tc.last)
1036 }
1037 }
1038 }
1039
1040 func equal(m string, s1, s2 string, t *testing.T) bool {
1041 if s1 == s2 {
1042 return true
1043 }
1044 e1 := Split(s1, "")
1045 e2 := Split(s2, "")
1046 for i, c1 := range e1 {
1047 if i >= len(e2) {
1048 break
1049 }
1050 r1, _ := utf8.DecodeRuneInString(c1)
1051 r2, _ := utf8.DecodeRuneInString(e2[i])
1052 if r1 != r2 {
1053 t.Errorf("%s diff at %d: U+%04X U+%04X", m, i, r1, r2)
1054 }
1055 }
1056 return false
1057 }
1058
1059 func TestCaseConsistency(t *testing.T) {
1060
1061 numRunes := int(unicode.MaxRune + 1)
1062 if testing.Short() {
1063 numRunes = 1000
1064 }
1065 a := make([]rune, numRunes)
1066 for i := range a {
1067 a[i] = rune(i)
1068 }
1069 s := string(a)
1070
1071 upper := ToUpper(s)
1072 lower := ToLower(s)
1073
1074
1075 if n := utf8.RuneCountInString(upper); n != numRunes {
1076 t.Error("rune count wrong in upper:", n)
1077 }
1078 if n := utf8.RuneCountInString(lower); n != numRunes {
1079 t.Error("rune count wrong in lower:", n)
1080 }
1081 if !equal("ToUpper(upper)", ToUpper(upper), upper, t) {
1082 t.Error("ToUpper(upper) consistency fail")
1083 }
1084 if !equal("ToLower(lower)", ToLower(lower), lower, t) {
1085 t.Error("ToLower(lower) consistency fail")
1086 }
1087
1101 }
1102
1103 var RepeatTests = []struct {
1104 in, out string
1105 count int
1106 }{
1107 {"", "", 0},
1108 {"", "", 1},
1109 {"", "", 2},
1110 {"-", "", 0},
1111 {"-", "-", 1},
1112 {"-", "----------", 10},
1113 {"abc ", "abc abc abc ", 3},
1114 }
1115
1116 func TestRepeat(t *testing.T) {
1117 for _, tt := range RepeatTests {
1118 a := Repeat(tt.in, tt.count)
1119 if !equal("Repeat(s)", a, tt.out, t) {
1120 t.Errorf("Repeat(%v, %d) = %v; want %v", tt.in, tt.count, a, tt.out)
1121 continue
1122 }
1123 }
1124 }
1125
1126 func repeat(s string, count int) (err error) {
1127 defer func() {
1128 if r := recover(); r != nil {
1129 switch v := r.(type) {
1130 case error:
1131 err = v
1132 default:
1133 err = fmt.Errorf("%s", v)
1134 }
1135 }
1136 }()
1137
1138 Repeat(s, count)
1139
1140 return
1141 }
1142
1143
1144 func TestRepeatCatchesOverflow(t *testing.T) {
1145 tests := [...]struct {
1146 s string
1147 count int
1148 errStr string
1149 }{
1150 0: {"--", -2147483647, "negative"},
1151 1: {"", int(^uint(0) >> 1), ""},
1152 2: {"-", 10, ""},
1153 3: {"gopher", 0, ""},
1154 4: {"-", -1, "negative"},
1155 5: {"--", -102, "negative"},
1156 6: {string(make([]byte, 255)), int((^uint(0))/255 + 1), "overflow"},
1157 }
1158
1159 for i, tt := range tests {
1160 err := repeat(tt.s, tt.count)
1161 if tt.errStr == "" {
1162 if err != nil {
1163 t.Errorf("#%d panicked %v", i, err)
1164 }
1165 continue
1166 }
1167
1168 if err == nil || !Contains(err.Error(), tt.errStr) {
1169 t.Errorf("#%d expected %q got %q", i, tt.errStr, err)
1170 }
1171 }
1172 }
1173
1174 func runesEqual(a, b []rune) bool {
1175 if len(a) != len(b) {
1176 return false
1177 }
1178 for i, r := range a {
1179 if r != b[i] {
1180 return false
1181 }
1182 }
1183 return true
1184 }
1185
1186 var RunesTests = []struct {
1187 in string
1188 out []rune
1189 lossy bool
1190 }{
1191 {"", []rune{}, false},
1192 {" ", []rune{32}, false},
1193 {"ABC", []rune{65, 66, 67}, false},
1194 {"abc", []rune{97, 98, 99}, false},
1195 {"\u65e5\u672c\u8a9e", []rune{26085, 26412, 35486}, false},
1196 {"ab\x80c", []rune{97, 98, 0xFFFD, 99}, true},
1197 {"ab\xc0c", []rune{97, 98, 0xFFFD, 99}, true},
1198 }
1199
1200 func TestRunes(t *testing.T) {
1201 for _, tt := range RunesTests {
1202 a := []rune(tt.in)
1203 if !runesEqual(a, tt.out) {
1204 t.Errorf("[]rune(%q) = %v; want %v", tt.in, a, tt.out)
1205 continue
1206 }
1207 if !tt.lossy {
1208
1209 s := string(a)
1210 if s != tt.in {
1211 t.Errorf("string([]rune(%q)) = %x; want %x", tt.in, s, tt.in)
1212 }
1213 }
1214 }
1215 }
1216
1217 func TestReadByte(t *testing.T) {
1218 testStrings := []string{"", abcd, faces, commas}
1219 for _, s := range testStrings {
1220 reader := NewReader(s)
1221 if e := reader.UnreadByte(); e == nil {
1222 t.Errorf("Unreading %q at beginning: expected error", s)
1223 }
1224 var res bytes.Buffer
1225 for {
1226 b, e := reader.ReadByte()
1227 if e == io.EOF {
1228 break
1229 }
1230 if e != nil {
1231 t.Errorf("Reading %q: %s", s, e)
1232 break
1233 }
1234 res.WriteByte(b)
1235
1236 e = reader.UnreadByte()
1237 if e != nil {
1238 t.Errorf("Unreading %q: %s", s, e)
1239 break
1240 }
1241 b1, e := reader.ReadByte()
1242 if e != nil {
1243 t.Errorf("Reading %q after unreading: %s", s, e)
1244 break
1245 }
1246 if b1 != b {
1247 t.Errorf("Reading %q after unreading: want byte %q, got %q", s, b, b1)
1248 break
1249 }
1250 }
1251 if res.String() != s {
1252 t.Errorf("Reader(%q).ReadByte() produced %q", s, res.String())
1253 }
1254 }
1255 }
1256
1257 func TestReadRune(t *testing.T) {
1258 testStrings := []string{"", abcd, faces, commas}
1259 for _, s := range testStrings {
1260 reader := NewReader(s)
1261 if e := reader.UnreadRune(); e == nil {
1262 t.Errorf("Unreading %q at beginning: expected error", s)
1263 }
1264 res := ""
1265 for {
1266 r, z, e := reader.ReadRune()
1267 if e == io.EOF {
1268 break
1269 }
1270 if e != nil {
1271 t.Errorf("Reading %q: %s", s, e)
1272 break
1273 }
1274 res += string(r)
1275
1276 e = reader.UnreadRune()
1277 if e != nil {
1278 t.Errorf("Unreading %q: %s", s, e)
1279 break
1280 }
1281 r1, z1, e := reader.ReadRune()
1282 if e != nil {
1283 t.Errorf("Reading %q after unreading: %s", s, e)
1284 break
1285 }
1286 if r1 != r {
1287 t.Errorf("Reading %q after unreading: want rune %q, got %q", s, r, r1)
1288 break
1289 }
1290 if z1 != z {
1291 t.Errorf("Reading %q after unreading: want size %d, got %d", s, z, z1)
1292 break
1293 }
1294 }
1295 if res != s {
1296 t.Errorf("Reader(%q).ReadRune() produced %q", s, res)
1297 }
1298 }
1299 }
1300
1301 var UnreadRuneErrorTests = []struct {
1302 name string
1303 f func(*Reader)
1304 }{
1305 {"Read", func(r *Reader) { r.Read([]byte{0}) }},
1306 {"ReadByte", func(r *Reader) { r.ReadByte() }},
1307 {"UnreadRune", func(r *Reader) { r.UnreadRune() }},
1308 {"Seek", func(r *Reader) { r.Seek(0, io.SeekCurrent) }},
1309 {"WriteTo", func(r *Reader) { r.WriteTo(&bytes.Buffer{}) }},
1310 }
1311
1312 func TestUnreadRuneError(t *testing.T) {
1313 for _, tt := range UnreadRuneErrorTests {
1314 reader := NewReader("0123456789")
1315 if _, _, err := reader.ReadRune(); err != nil {
1316
1317 t.Fatal(err)
1318 }
1319 tt.f(reader)
1320 err := reader.UnreadRune()
1321 if err == nil {
1322 t.Errorf("Unreading after %s: expected error", tt.name)
1323 }
1324 }
1325 }
1326
1327 var ReplaceTests = []struct {
1328 in string
1329 old, new string
1330 n int
1331 out string
1332 }{
1333 {"hello", "l", "L", 0, "hello"},
1334 {"hello", "l", "L", -1, "heLLo"},
1335 {"hello", "x", "X", -1, "hello"},
1336 {"", "x", "X", -1, ""},
1337 {"radar", "r", "<r>", -1, "<r>ada<r>"},
1338 {"", "", "<>", -1, "<>"},
1339 {"banana", "a", "<>", -1, "b<>n<>n<>"},
1340 {"banana", "a", "<>", 1, "b<>nana"},
1341 {"banana", "a", "<>", 1000, "b<>n<>n<>"},
1342 {"banana", "an", "<>", -1, "b<><>a"},
1343 {"banana", "ana", "<>", -1, "b<>na"},
1344 {"banana", "", "<>", -1, "<>b<>a<>n<>a<>n<>a<>"},
1345 {"banana", "", "<>", 10, "<>b<>a<>n<>a<>n<>a<>"},
1346 {"banana", "", "<>", 6, "<>b<>a<>n<>a<>n<>a"},
1347 {"banana", "", "<>", 5, "<>b<>a<>n<>a<>na"},
1348 {"banana", "", "<>", 1, "<>banana"},
1349 {"banana", "a", "a", -1, "banana"},
1350 {"banana", "a", "a", 1, "banana"},
1351 {"☺☻☹", "", "<>", -1, "<>☺<>☻<>☹<>"},
1352 }
1353
1354 func TestReplace(t *testing.T) {
1355 for _, tt := range ReplaceTests {
1356 if s := Replace(tt.in, tt.old, tt.new, tt.n); s != tt.out {
1357 t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out)
1358 }
1359 if tt.n == -1 {
1360 s := ReplaceAll(tt.in, tt.old, tt.new)
1361 if s != tt.out {
1362 t.Errorf("ReplaceAll(%q, %q, %q) = %q, want %q", tt.in, tt.old, tt.new, s, tt.out)
1363 }
1364 }
1365 }
1366 }
1367
1368 var TitleTests = []struct {
1369 in, out string
1370 }{
1371 {"", ""},
1372 {"a", "A"},
1373 {" aaa aaa aaa ", " Aaa Aaa Aaa "},
1374 {" Aaa Aaa Aaa ", " Aaa Aaa Aaa "},
1375 {"123a456", "123a456"},
1376 {"double-blind", "Double-Blind"},
1377 {"ÿøû", "Ÿøû"},
1378 {"with_underscore", "With_underscore"},
1379 {"unicode \xe2\x80\xa8 line separator", "Unicode \xe2\x80\xa8 Line Separator"},
1380 }
1381
1382 func TestTitle(t *testing.T) {
1383 for _, tt := range TitleTests {
1384 if s := Title(tt.in); s != tt.out {
1385 t.Errorf("Title(%q) = %q, want %q", tt.in, s, tt.out)
1386 }
1387 }
1388 }
1389
1390 var ContainsTests = []struct {
1391 str, substr string
1392 expected bool
1393 }{
1394 {"abc", "bc", true},
1395 {"abc", "bcd", false},
1396 {"abc", "", true},
1397 {"", "a", false},
1398
1399
1400
1401 {"xxxxxx", "01", false},
1402 {"01xxxx", "01", true},
1403 {"xx01xx", "01", true},
1404 {"xxxx01", "01", true},
1405 {"01xxxxx"[1:], "01", false},
1406 {"xxxxx01"[:6], "01", false},
1407
1408 {"xxxxxxx", "012", false},
1409 {"012xxxx", "012", true},
1410 {"xx012xx", "012", true},
1411 {"xxxx012", "012", true},
1412 {"012xxxxx"[1:], "012", false},
1413 {"xxxxx012"[:7], "012", false},
1414
1415 {"xxxxxxxx", "0123", false},
1416 {"0123xxxx", "0123", true},
1417 {"xx0123xx", "0123", true},
1418 {"xxxx0123", "0123", true},
1419 {"0123xxxxx"[1:], "0123", false},
1420 {"xxxxx0123"[:8], "0123", false},
1421
1422 {"xxxxxxxxx", "01234", false},
1423 {"01234xxxx", "01234", true},
1424 {"xx01234xx", "01234", true},
1425 {"xxxx01234", "01234", true},
1426 {"01234xxxxx"[1:], "01234", false},
1427 {"xxxxx01234"[:9], "01234", false},
1428
1429 {"xxxxxxxxxxxx", "01234567", false},
1430 {"01234567xxxx", "01234567", true},
1431 {"xx01234567xx", "01234567", true},
1432 {"xxxx01234567", "01234567", true},
1433 {"01234567xxxxx"[1:], "01234567", false},
1434 {"xxxxx01234567"[:12], "01234567", false},
1435
1436 {"xxxxxxxxxxxxx", "012345678", false},
1437 {"012345678xxxx", "012345678", true},
1438 {"xx012345678xx", "012345678", true},
1439 {"xxxx012345678", "012345678", true},
1440 {"012345678xxxxx"[1:], "012345678", false},
1441 {"xxxxx012345678"[:13], "012345678", false},
1442
1443 {"xxxxxxxxxxxxxxxxxxxx", "0123456789ABCDEF", false},
1444 {"0123456789ABCDEFxxxx", "0123456789ABCDEF", true},
1445 {"xx0123456789ABCDEFxx", "0123456789ABCDEF", true},
1446 {"xxxx0123456789ABCDEF", "0123456789ABCDEF", true},
1447 {"0123456789ABCDEFxxxxx"[1:], "0123456789ABCDEF", false},
1448 {"xxxxx0123456789ABCDEF"[:20], "0123456789ABCDEF", false},
1449
1450 {"xxxxxxxxxxxxxxxxxxxxx", "0123456789ABCDEFG", false},
1451 {"0123456789ABCDEFGxxxx", "0123456789ABCDEFG", true},
1452 {"xx0123456789ABCDEFGxx", "0123456789ABCDEFG", true},
1453 {"xxxx0123456789ABCDEFG", "0123456789ABCDEFG", true},
1454 {"0123456789ABCDEFGxxxxx"[1:], "0123456789ABCDEFG", false},
1455 {"xxxxx0123456789ABCDEFG"[:21], "0123456789ABCDEFG", false},
1456
1457
1458 {"xx01x", "012", false},
1459 {"xx0123x", "01234", false},
1460 {"xx01234567x", "012345678", false},
1461 {"xx0123456789ABCDEFx", "0123456789ABCDEFG", false},
1462 }
1463
1464 func TestContains(t *testing.T) {
1465 for _, ct := range ContainsTests {
1466 if Contains(ct.str, ct.substr) != ct.expected {
1467 t.Errorf("Contains(%s, %s) = %v, want %v",
1468 ct.str, ct.substr, !ct.expected, ct.expected)
1469 }
1470 }
1471 }
1472
1473 var ContainsAnyTests = []struct {
1474 str, substr string
1475 expected bool
1476 }{
1477 {"", "", false},
1478 {"", "a", false},
1479 {"", "abc", false},
1480 {"a", "", false},
1481 {"a", "a", true},
1482 {"aaa", "a", true},
1483 {"abc", "xyz", false},
1484 {"abc", "xcz", true},
1485 {"a☺b☻c☹d", "uvw☻xyz", true},
1486 {"aRegExp*", ".(|)*+?^$[]", true},
1487 {dots + dots + dots, " ", false},
1488 }
1489
1490 func TestContainsAny(t *testing.T) {
1491 for _, ct := range ContainsAnyTests {
1492 if ContainsAny(ct.str, ct.substr) != ct.expected {
1493 t.Errorf("ContainsAny(%s, %s) = %v, want %v",
1494 ct.str, ct.substr, !ct.expected, ct.expected)
1495 }
1496 }
1497 }
1498
1499 var ContainsRuneTests = []struct {
1500 str string
1501 r rune
1502 expected bool
1503 }{
1504 {"", 'a', false},
1505 {"a", 'a', true},
1506 {"aaa", 'a', true},
1507 {"abc", 'y', false},
1508 {"abc", 'c', true},
1509 {"a☺b☻c☹d", 'x', false},
1510 {"a☺b☻c☹d", '☻', true},
1511 {"aRegExp*", '*', true},
1512 }
1513
1514 func TestContainsRune(t *testing.T) {
1515 for _, ct := range ContainsRuneTests {
1516 if ContainsRune(ct.str, ct.r) != ct.expected {
1517 t.Errorf("ContainsRune(%q, %q) = %v, want %v",
1518 ct.str, ct.r, !ct.expected, ct.expected)
1519 }
1520 }
1521 }
1522
1523 var EqualFoldTests = []struct {
1524 s, t string
1525 out bool
1526 }{
1527 {"abc", "abc", true},
1528 {"ABcd", "ABcd", true},
1529 {"123abc", "123ABC", true},
1530 {"αβδ", "ΑΒΔ", true},
1531 {"abc", "xyz", false},
1532 {"abc", "XYZ", false},
1533 {"abcdefghijk", "abcdefghijX", false},
1534 {"abcdefghijk", "abcdefghij\u212A", true},
1535 {"abcdefghijK", "abcdefghij\u212A", true},
1536 {"abcdefghijkz", "abcdefghij\u212Ay", false},
1537 {"abcdefghijKz", "abcdefghij\u212Ay", false},
1538 {"1", "2", false},
1539 {"utf-8", "US-ASCII", false},
1540 }
1541
1542 func TestEqualFold(t *testing.T) {
1543 for _, tt := range EqualFoldTests {
1544 if out := EqualFold(tt.s, tt.t); out != tt.out {
1545 t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out)
1546 }
1547 if out := EqualFold(tt.t, tt.s); out != tt.out {
1548 t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out)
1549 }
1550 }
1551 }
1552
1553 func BenchmarkEqualFold(b *testing.B) {
1554 for i := 0; i < b.N; i++ {
1555 for _, tt := range EqualFoldTests {
1556 if out := EqualFold(tt.s, tt.t); out != tt.out {
1557 b.Fatal("wrong result")
1558 }
1559 }
1560 }
1561 }
1562
1563 var CountTests = []struct {
1564 s, sep string
1565 num int
1566 }{
1567 {"", "", 1},
1568 {"", "notempty", 0},
1569 {"notempty", "", 9},
1570 {"smaller", "not smaller", 0},
1571 {"12345678987654321", "6", 2},
1572 {"611161116", "6", 3},
1573 {"notequal", "NotEqual", 0},
1574 {"equal", "equal", 1},
1575 {"abc1231231123q", "123", 3},
1576 {"11111", "11", 2},
1577 }
1578
1579 func TestCount(t *testing.T) {
1580 for _, tt := range CountTests {
1581 if num := Count(tt.s, tt.sep); num != tt.num {
1582 t.Errorf("Count(%q, %q) = %d, want %d", tt.s, tt.sep, num, tt.num)
1583 }
1584 }
1585 }
1586
1587 var cutTests = []struct {
1588 s, sep string
1589 before, after string
1590 found bool
1591 }{
1592 {"abc", "b", "a", "c", true},
1593 {"abc", "a", "", "bc", true},
1594 {"abc", "c", "ab", "", true},
1595 {"abc", "abc", "", "", true},
1596 {"abc", "", "", "abc", true},
1597 {"abc", "d", "abc", "", false},
1598 {"", "d", "", "", false},
1599 {"", "", "", "", true},
1600 }
1601
1602 func TestCut(t *testing.T) {
1603 for _, tt := range cutTests {
1604 if before, after, found := Cut(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found {
1605 t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
1606 }
1607 }
1608 }
1609
1610 func makeBenchInputHard() string {
1611 tokens := [...]string{
1612 "<a>", "<p>", "<b>", "<strong>",
1613 "</a>", "</p>", "</b>", "</strong>",
1614 "hello", "world",
1615 }
1616 x := make([]byte, 0, 1<<20)
1617 for {
1618 i := rand.Intn(len(tokens))
1619 if len(x)+len(tokens[i]) >= 1<<20 {
1620 break
1621 }
1622 x = append(x, tokens[i]...)
1623 }
1624 return string(x)
1625 }
1626
1627 var benchInputHard = makeBenchInputHard()
1628
1629 func benchmarkIndexHard(b *testing.B, sep string) {
1630 for i := 0; i < b.N; i++ {
1631 Index(benchInputHard, sep)
1632 }
1633 }
1634
1635 func benchmarkLastIndexHard(b *testing.B, sep string) {
1636 for i := 0; i < b.N; i++ {
1637 LastIndex(benchInputHard, sep)
1638 }
1639 }
1640
1641 func benchmarkCountHard(b *testing.B, sep string) {
1642 for i := 0; i < b.N; i++ {
1643 Count(benchInputHard, sep)
1644 }
1645 }
1646
1647 func BenchmarkIndexHard1(b *testing.B) { benchmarkIndexHard(b, "<>") }
1648 func BenchmarkIndexHard2(b *testing.B) { benchmarkIndexHard(b, "</pre>") }
1649 func BenchmarkIndexHard3(b *testing.B) { benchmarkIndexHard(b, "<b>hello world</b>") }
1650 func BenchmarkIndexHard4(b *testing.B) {
1651 benchmarkIndexHard(b, "<pre><b>hello</b><strong>world</strong></pre>")
1652 }
1653
1654 func BenchmarkLastIndexHard1(b *testing.B) { benchmarkLastIndexHard(b, "<>") }
1655 func BenchmarkLastIndexHard2(b *testing.B) { benchmarkLastIndexHard(b, "</pre>") }
1656 func BenchmarkLastIndexHard3(b *testing.B) { benchmarkLastIndexHard(b, "<b>hello world</b>") }
1657
1658 func BenchmarkCountHard1(b *testing.B) { benchmarkCountHard(b, "<>") }
1659 func BenchmarkCountHard2(b *testing.B) { benchmarkCountHard(b, "</pre>") }
1660 func BenchmarkCountHard3(b *testing.B) { benchmarkCountHard(b, "<b>hello world</b>") }
1661
1662 var benchInputTorture = Repeat("ABC", 1<<10) + "123" + Repeat("ABC", 1<<10)
1663 var benchNeedleTorture = Repeat("ABC", 1<<10+1)
1664
1665 func BenchmarkIndexTorture(b *testing.B) {
1666 for i := 0; i < b.N; i++ {
1667 Index(benchInputTorture, benchNeedleTorture)
1668 }
1669 }
1670
1671 func BenchmarkCountTorture(b *testing.B) {
1672 for i := 0; i < b.N; i++ {
1673 Count(benchInputTorture, benchNeedleTorture)
1674 }
1675 }
1676
1677 func BenchmarkCountTortureOverlapping(b *testing.B) {
1678 A := Repeat("ABC", 1<<20)
1679 B := Repeat("ABC", 1<<10)
1680 for i := 0; i < b.N; i++ {
1681 Count(A, B)
1682 }
1683 }
1684
1685 func BenchmarkCountByte(b *testing.B) {
1686 indexSizes := []int{10, 32, 4 << 10, 4 << 20, 64 << 20}
1687 benchStr := Repeat(benchmarkString,
1688 (indexSizes[len(indexSizes)-1]+len(benchmarkString)-1)/len(benchmarkString))
1689 benchFunc := func(b *testing.B, benchStr string) {
1690 b.SetBytes(int64(len(benchStr)))
1691 for i := 0; i < b.N; i++ {
1692 Count(benchStr, "=")
1693 }
1694 }
1695 for _, size := range indexSizes {
1696 b.Run(fmt.Sprintf("%d", size), func(b *testing.B) {
1697 benchFunc(b, benchStr[:size])
1698 })
1699 }
1700
1701 }
1702
1703 var makeFieldsInput = func() string {
1704 x := make([]byte, 1<<20)
1705
1706 for i := range x {
1707 switch rand.Intn(10) {
1708 case 0:
1709 x[i] = ' '
1710 case 1:
1711 if i > 0 && x[i-1] == 'x' {
1712 copy(x[i-1:], "χ")
1713 break
1714 }
1715 fallthrough
1716 default:
1717 x[i] = 'x'
1718 }
1719 }
1720 return string(x)
1721 }
1722
1723 var makeFieldsInputASCII = func() string {
1724 x := make([]byte, 1<<20)
1725
1726 for i := range x {
1727 if rand.Intn(10) == 0 {
1728 x[i] = ' '
1729 } else {
1730 x[i] = 'x'
1731 }
1732 }
1733 return string(x)
1734 }
1735
1736 var stringdata = []struct{ name, data string }{
1737 {"ASCII", makeFieldsInputASCII()},
1738 {"Mixed", makeFieldsInput()},
1739 }
1740
1741 func BenchmarkFields(b *testing.B) {
1742 for _, sd := range stringdata {
1743 b.Run(sd.name, func(b *testing.B) {
1744 for j := 1 << 4; j <= 1<<20; j <<= 4 {
1745 b.Run(fmt.Sprintf("%d", j), func(b *testing.B) {
1746 b.ReportAllocs()
1747 b.SetBytes(int64(j))
1748 data := sd.data[:j]
1749 for i := 0; i < b.N; i++ {
1750 Fields(data)
1751 }
1752 })
1753 }
1754 })
1755 }
1756 }
1757
1758 func BenchmarkFieldsFunc(b *testing.B) {
1759 for _, sd := range stringdata {
1760 b.Run(sd.name, func(b *testing.B) {
1761 for j := 1 << 4; j <= 1<<20; j <<= 4 {
1762 b.Run(fmt.Sprintf("%d", j), func(b *testing.B) {
1763 b.ReportAllocs()
1764 b.SetBytes(int64(j))
1765 data := sd.data[:j]
1766 for i := 0; i < b.N; i++ {
1767 FieldsFunc(data, unicode.IsSpace)
1768 }
1769 })
1770 }
1771 })
1772 }
1773 }
1774
1775 func BenchmarkSplitEmptySeparator(b *testing.B) {
1776 for i := 0; i < b.N; i++ {
1777 Split(benchInputHard, "")
1778 }
1779 }
1780
1781 func BenchmarkSplitSingleByteSeparator(b *testing.B) {
1782 for i := 0; i < b.N; i++ {
1783 Split(benchInputHard, "/")
1784 }
1785 }
1786
1787 func BenchmarkSplitMultiByteSeparator(b *testing.B) {
1788 for i := 0; i < b.N; i++ {
1789 Split(benchInputHard, "hello")
1790 }
1791 }
1792
1793 func BenchmarkSplitNSingleByteSeparator(b *testing.B) {
1794 for i := 0; i < b.N; i++ {
1795 SplitN(benchInputHard, "/", 10)
1796 }
1797 }
1798
1799 func BenchmarkSplitNMultiByteSeparator(b *testing.B) {
1800 for i := 0; i < b.N; i++ {
1801 SplitN(benchInputHard, "hello", 10)
1802 }
1803 }
1804
1805 func BenchmarkRepeat(b *testing.B) {
1806 s := "0123456789"
1807 for _, n := range []int{5, 10} {
1808 for _, c := range []int{1, 2, 6} {
1809 b.Run(fmt.Sprintf("%dx%d", n, c), func(b *testing.B) {
1810 for i := 0; i < b.N; i++ {
1811 Repeat(s[:n], c)
1812 }
1813 })
1814 }
1815 }
1816 }
1817
1818 func BenchmarkIndexAnyASCII(b *testing.B) {
1819 x := Repeat("#", 2048)
1820 cs := "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"
1821 for k := 1; k <= 2048; k <<= 4 {
1822 for j := 1; j <= 64; j <<= 1 {
1823 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
1824 for i := 0; i < b.N; i++ {
1825 IndexAny(x[:k], cs[:j])
1826 }
1827 })
1828 }
1829 }
1830 }
1831
1832 func BenchmarkIndexAnyUTF8(b *testing.B) {
1833 x := Repeat("#", 2048)
1834 cs := "你好世界, hello world. 你好世界, hello world. 你好世界, hello world."
1835 for k := 1; k <= 2048; k <<= 4 {
1836 for j := 1; j <= 64; j <<= 1 {
1837 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
1838 for i := 0; i < b.N; i++ {
1839 IndexAny(x[:k], cs[:j])
1840 }
1841 })
1842 }
1843 }
1844 }
1845
1846 func BenchmarkLastIndexAnyASCII(b *testing.B) {
1847 x := Repeat("#", 2048)
1848 cs := "0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz"
1849 for k := 1; k <= 2048; k <<= 4 {
1850 for j := 1; j <= 64; j <<= 1 {
1851 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
1852 for i := 0; i < b.N; i++ {
1853 LastIndexAny(x[:k], cs[:j])
1854 }
1855 })
1856 }
1857 }
1858 }
1859
1860 func BenchmarkLastIndexAnyUTF8(b *testing.B) {
1861 x := Repeat("#", 2048)
1862 cs := "你好世界, hello world. 你好世界, hello world. 你好世界, hello world."
1863 for k := 1; k <= 2048; k <<= 4 {
1864 for j := 1; j <= 64; j <<= 1 {
1865 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
1866 for i := 0; i < b.N; i++ {
1867 LastIndexAny(x[:k], cs[:j])
1868 }
1869 })
1870 }
1871 }
1872 }
1873
1874 func BenchmarkTrimASCII(b *testing.B) {
1875 cs := "0123456789abcdef"
1876 for k := 1; k <= 4096; k <<= 4 {
1877 for j := 1; j <= 16; j <<= 1 {
1878 b.Run(fmt.Sprintf("%d:%d", k, j), func(b *testing.B) {
1879 x := Repeat(cs[:j], k)
1880 for i := 0; i < b.N; i++ {
1881 Trim(x[:k], cs[:j])
1882 }
1883 })
1884 }
1885 }
1886 }
1887
1888 func BenchmarkTrimByte(b *testing.B) {
1889 x := " the quick brown fox "
1890 for i := 0; i < b.N; i++ {
1891 Trim(x, " ")
1892 }
1893 }
1894
1895 func BenchmarkIndexPeriodic(b *testing.B) {
1896 key := "aa"
1897 for _, skip := range [...]int{2, 4, 8, 16, 32, 64} {
1898 b.Run(fmt.Sprintf("IndexPeriodic%d", skip), func(b *testing.B) {
1899 s := Repeat("a"+Repeat(" ", skip-1), 1<<16/skip)
1900 for i := 0; i < b.N; i++ {
1901 Index(s, key)
1902 }
1903 })
1904 }
1905 }
1906
1907 func BenchmarkJoin(b *testing.B) {
1908 vals := []string{"red", "yellow", "pink", "green", "purple", "orange", "blue"}
1909 for l := 0; l <= len(vals); l++ {
1910 b.Run(strconv.Itoa(l), func(b *testing.B) {
1911 b.ReportAllocs()
1912 vals := vals[:l]
1913 for i := 0; i < b.N; i++ {
1914 Join(vals, " and ")
1915 }
1916 })
1917 }
1918 }
1919
1920 func BenchmarkTrimSpace(b *testing.B) {
1921 tests := []struct{ name, input string }{
1922 {"NoTrim", "typical"},
1923 {"ASCII", " foo bar "},
1924 {"SomeNonASCII", " \u2000\t\r\n x\t\t\r\r\ny\n \u3000 "},
1925 {"JustNonASCII", "\u2000\u2000\u2000☺☺☺☺\u3000\u3000\u3000"},
1926 }
1927 for _, test := range tests {
1928 b.Run(test.name, func(b *testing.B) {
1929 for i := 0; i < b.N; i++ {
1930 TrimSpace(test.input)
1931 }
1932 })
1933 }
1934 }
1935
1936 var stringSink string
1937
1938 func BenchmarkReplaceAll(b *testing.B) {
1939 b.ReportAllocs()
1940 for i := 0; i < b.N; i++ {
1941 stringSink = ReplaceAll("banana", "a", "<>")
1942 }
1943 }
1944
View as plain text