github.com/segmentio/encoding@v0.4.0/ascii/ascii_test.go (about) 1 package ascii 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 "testing" 8 "unicode/utf8" 9 ) 10 11 var testStrings = [...]string{ 12 "", 13 "a", 14 "ab", 15 "abc", 16 "abcd", 17 "hello", 18 "Hello World!", 19 "Hello\"World!", 20 "Hello\\World!", 21 "Hello\nWorld!", 22 "Hello\rWorld!", 23 "Hello\tWorld!", 24 "Hello\bWorld!", 25 "Hello\fWorld!", 26 "H~llo World!", 27 "H~llo", 28 "你好", 29 "~", 30 "\x80", 31 "\x7F", 32 "\xFF", 33 "\x1fxxx", 34 "\x1fxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", 35 "a string of 16B.", 36 "an invalid string of 32B. \x00......", 37 "some kind of long string with only ascii characters.", 38 "some kind of long string with a non-ascii character at the end.\xff", 39 strings.Repeat("1234567890", 1000), 40 } 41 42 var testStringsUTF8 []string 43 44 func init() { 45 for _, test := range testStrings { 46 if utf8.ValidString(test) { 47 testStringsUTF8 = append(testStringsUTF8, test) 48 } 49 } 50 } 51 52 func testString(s string, f func(byte) bool) bool { 53 for i := range s { 54 if !f(s[i]) { 55 return false 56 } 57 } 58 return true 59 } 60 61 func testValid(s string) bool { 62 return testString(s, ValidByte) 63 } 64 65 func testValidPrint(s string) bool { 66 return testString(s, ValidPrintByte) 67 } 68 69 func TestValid(t *testing.T) { 70 testValidationFunction(t, testValid, ValidString) 71 } 72 73 func TestValidPrint(t *testing.T) { 74 testValidationFunction(t, testValidPrint, ValidPrintString) 75 } 76 77 func testValidationFunction(t *testing.T, reference, function func(string) bool) { 78 for _, test := range testStrings { 79 t.Run(limit(test), func(t *testing.T) { 80 expect := reference(test) 81 82 if valid := function(test); expect != valid { 83 t.Errorf("expected %t but got %t", expect, valid) 84 } 85 }) 86 } 87 } 88 89 func BenchmarkValid(b *testing.B) { 90 benchmarkValidationFunction(b, ValidString) 91 } 92 93 func BenchmarkValidPrint(b *testing.B) { 94 benchmarkValidationFunction(b, ValidPrintString) 95 } 96 97 func benchmarkValidationFunction(b *testing.B, function func(string) bool) { 98 for _, test := range testStrings { 99 b.Run(limit(test), func(b *testing.B) { 100 for i := 0; i < b.N; i++ { 101 _ = function(test) 102 } 103 b.SetBytes(int64(len(test))) 104 }) 105 } 106 } 107 108 func limit(s string) string { 109 if len(s) > 17 { 110 return s[:17] + "..." 111 } 112 return s 113 } 114 115 func TestHasPrefixFold(t *testing.T) { 116 for _, test := range testStringsUTF8 { 117 t.Run(limit(test), func(t *testing.T) { 118 prefix := test 119 if len(prefix) > 0 { 120 prefix = prefix[:len(prefix)/2] 121 } 122 upper := strings.ToUpper(prefix) 123 lower := strings.ToLower(prefix) 124 125 if !HasPrefixFoldString(test, prefix) { 126 t.Errorf("%q does not match %q", test, prefix) 127 } 128 129 if !HasPrefixFoldString(test, upper) { 130 t.Errorf("%q does not match %q", test, upper) 131 } 132 133 if !HasPrefixFoldString(test, lower) { 134 t.Errorf("%q does not match %q", test, lower) 135 } 136 }) 137 } 138 } 139 140 func TestHasSuffixFold(t *testing.T) { 141 for _, test := range testStringsUTF8 { 142 t.Run(limit(test), func(t *testing.T) { 143 suffix := test 144 if len(suffix) > 0 { 145 suffix = suffix[len(suffix)/2:] 146 } 147 upper := strings.ToUpper(suffix) 148 lower := strings.ToLower(suffix) 149 150 if !HasSuffixFoldString(test, suffix) { 151 t.Errorf("%q does not match %q", test, suffix) 152 } 153 154 if !HasSuffixFoldString(test, upper) { 155 t.Errorf("%q does not match %q", test, upper) 156 } 157 158 if !HasSuffixFoldString(test, lower) { 159 t.Errorf("%q does not match %q", test, lower) 160 } 161 }) 162 } 163 } 164 165 func TestEqualFoldASCII(t *testing.T) { 166 pairs := [...][2]byte{ 167 {0, ' '}, 168 {'@', '`'}, 169 {'[', '{'}, 170 {'_', 127}, 171 } 172 173 for _, pair := range pairs { 174 t.Run(fmt.Sprintf("0x%02x=0x%02x", pair[0], pair[1]), func(t *testing.T) { 175 for i := 1; i <= 256; i++ { 176 a := bytes.Repeat([]byte{'x'}, i) 177 b := bytes.Repeat([]byte{'X'}, i) 178 179 if !EqualFold(a, b) { 180 t.Errorf("%q does not match %q", a, b) 181 break 182 } 183 184 a[0] = pair[0] 185 b[0] = pair[1] 186 187 if EqualFold(a, b) { 188 t.Errorf("%q matches %q", a, b) 189 break 190 } 191 } 192 }) 193 } 194 } 195 196 func TestEqualFold(t *testing.T) { 197 // Only test valid UTF-8 otherwise ToUpper/ToLower will convert invalid 198 // characters to UTF-8 placeholders, which breaks the case-insensitive 199 // equality. 200 for _, test := range testStringsUTF8 { 201 t.Run(limit(test), func(t *testing.T) { 202 upper := strings.ToUpper(test) 203 lower := strings.ToLower(test) 204 205 if !EqualFoldString(test, test) { 206 t.Errorf("%q does not match %q", test, test) 207 } 208 209 if !EqualFoldString(test, upper) { 210 t.Errorf("%q does not match %q", test, upper) 211 } 212 213 if !EqualFoldString(test, lower) { 214 t.Errorf("%q does not match %q", test, lower) 215 } 216 217 if len(test) > 1 { 218 reverse := make([]byte, len(test)) 219 for i := range reverse { 220 reverse[i] = test[len(test)-(i+1)] 221 } 222 223 if EqualFoldString(test, string(reverse)) { 224 t.Errorf("%q matches %q", test, reverse) 225 } 226 } 227 }) 228 } 229 } 230 231 func BenchmarkEqualFold(b *testing.B) { 232 for _, test := range testStringsUTF8 { 233 b.Run(limit(test), func(b *testing.B) { 234 other := test + "_" // not the same pointer 235 236 for i := 0; i < b.N; i++ { 237 _ = EqualFoldString(test, other[:len(test)]) // same length 238 } 239 240 b.SetBytes(int64(len(test))) 241 }) 242 } 243 }