// 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 rune) rune { switch r { case 'a': return 'A' case 'b': return 'B' } return r } for i := 0; i < b.N; i++ { Map(fn, str) } }