| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package strings_test |
| |
| import ( |
| "bytes" |
| "fmt" |
| "log" |
| . "strings" |
| "testing" |
| ) |
| |
| var _ = log.Printf |
| |
| type ReplacerTest struct { |
| r *Replacer |
| in string |
| out string |
| } |
| |
| var htmlEscaper = NewReplacer("&", "&", "<", "<", ">", ">", "\"", """) |
| |
| // The http package's old HTML escaping function. |
| func oldhtmlEscape(s string) string { |
| s = Replace(s, "&", "&", -1) |
| s = Replace(s, "<", "<", -1) |
| s = Replace(s, ">", ">", -1) |
| s = Replace(s, "\"", """, -1) |
| s = Replace(s, "'", "'", -1) |
| return s |
| } |
| |
| var replacer = NewReplacer("aaa", "3[aaa]", "aa", "2[aa]", "a", "1[a]", "i", "i", |
| "longerst", "most long", "longer", "medium", "long", "short", |
| "X", "Y", "Y", "Z") |
| |
| var capitalLetters = NewReplacer("a", "A", "b", "B") |
| |
| var blankToXReplacer = NewReplacer("", "X", "o", "O") |
| |
| var ReplacerTests = []ReplacerTest{ |
| // byte->string |
| {htmlEscaper, "No changes", "No changes"}, |
| {htmlEscaper, "I <3 escaping & stuff", "I <3 escaping & stuff"}, |
| {htmlEscaper, "&&&", "&&&"}, |
| |
| // generic |
| {replacer, "fooaaabar", "foo3[aaa]b1[a]r"}, |
| {replacer, "long, longerst, longer", "short, most long, medium"}, |
| {replacer, "XiX", "YiY"}, |
| |
| // byte->byte |
| {capitalLetters, "brad", "BrAd"}, |
| {capitalLetters, Repeat("a", (32<<10)+123), Repeat("A", (32<<10)+123)}, |
| |
| // hitting "" special case |
| {blankToXReplacer, "oo", "XOXOX"}, |
| } |
| |
| func TestReplacer(t *testing.T) { |
| for i, tt := range ReplacerTests { |
| if s := tt.r.Replace(tt.in); s != tt.out { |
| t.Errorf("%d. Replace(%q) = %q, want %q", i, tt.in, s, tt.out) |
| } |
| var buf bytes.Buffer |
| n, err := tt.r.WriteString(&buf, tt.in) |
| if err != nil { |
| t.Errorf("%d. WriteString: %v", i, err) |
| continue |
| } |
| got := buf.String() |
| if got != tt.out { |
| t.Errorf("%d. WriteString(%q) wrote %q, want %q", i, tt.in, got, tt.out) |
| continue |
| } |
| if n != len(tt.out) { |
| t.Errorf("%d. WriteString(%q) wrote correct string but reported %d bytes; want %d (%q)", |
| i, tt.in, n, len(tt.out), tt.out) |
| } |
| } |
| } |
| |
| // pickAlgorithmTest is a test that verifies that given input for a |
| // Replacer that we pick the correct algorithm. |
| type pickAlgorithmTest struct { |
| r *Replacer |
| want string // name of algorithm |
| } |
| |
| var pickAlgorithmTests = []pickAlgorithmTest{ |
| {capitalLetters, "*strings.byteReplacer"}, |
| {NewReplacer("12", "123"), "*strings.genericReplacer"}, |
| {NewReplacer("1", "12"), "*strings.byteStringReplacer"}, |
| {htmlEscaper, "*strings.byteStringReplacer"}, |
| } |
| |
| func TestPickAlgorithm(t *testing.T) { |
| for i, tt := range pickAlgorithmTests { |
| got := fmt.Sprintf("%T", tt.r.Replacer()) |
| if got != tt.want { |
| t.Errorf("%d. algorithm = %s, want %s", i, got, tt.want) |
| } |
| } |
| } |
| |
| func BenchmarkGenericMatch(b *testing.B) { |
| str := Repeat("A", 100) + Repeat("B", 100) |
| generic := NewReplacer("a", "A", "b", "B", "12", "123") // varying lengths forces generic |
| for i := 0; i < b.N; i++ { |
| generic.Replace(str) |
| } |
| } |
| |
| func BenchmarkByteByteNoMatch(b *testing.B) { |
| str := Repeat("A", 100) + Repeat("B", 100) |
| for i := 0; i < b.N; i++ { |
| capitalLetters.Replace(str) |
| } |
| } |
| |
| func BenchmarkByteByteMatch(b *testing.B) { |
| str := Repeat("a", 100) + Repeat("b", 100) |
| for i := 0; i < b.N; i++ { |
| capitalLetters.Replace(str) |
| } |
| } |
| |
| func BenchmarkByteStringMatch(b *testing.B) { |
| str := "<" + Repeat("a", 99) + Repeat("b", 99) + ">" |
| for i := 0; i < b.N; i++ { |
| htmlEscaper.Replace(str) |
| } |
| } |
| |
| func BenchmarkHTMLEscapeNew(b *testing.B) { |
| str := "I <3 to escape HTML & other text too." |
| for i := 0; i < b.N; i++ { |
| htmlEscaper.Replace(str) |
| } |
| } |
| |
| func BenchmarkHTMLEscapeOld(b *testing.B) { |
| str := "I <3 to escape HTML & other text too." |
| for i := 0; i < b.N; i++ { |
| oldhtmlEscape(str) |
| } |
| } |
| |
| // BenchmarkByteByteReplaces compares byteByteImpl against multiple Replaces. |
| func BenchmarkByteByteReplaces(b *testing.B) { |
| str := Repeat("a", 100) + Repeat("b", 100) |
| for i := 0; i < b.N; i++ { |
| Replace(Replace(str, "a", "A", -1), "b", "B", -1) |
| } |
| } |
| |
| // BenchmarkByteByteMap compares byteByteImpl against Map. |
| func BenchmarkByteByteMap(b *testing.B) { |
| str := Repeat("a", 100) + Repeat("b", 100) |
| fn := func(r int) int { |
| switch r { |
| case 'a': |
| return int('A') |
| case 'b': |
| return int('B') |
| } |
| return r |
| } |
| for i := 0; i < b.N; i++ { |
| Map(fn, str) |
| } |
| } |