package ascii import ( "bytes" "fmt" "strings" "testing" "unicode/utf8" ) var testStrings = [...]string{ "", "a", "ab", "abc", "abcd", "hello", "Hello World!", "Hello\"World!", "Hello\\World!", "Hello\nWorld!", "Hello\rWorld!", "Hello\tWorld!", "Hello\bWorld!", "Hello\fWorld!", "H~llo World!", "H~llo", "你好", "~", "\x80", "\x7F", "\xFF", "\x1fxxx", "\x1fxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "a string of 16B.", "an invalid string of 32B. \x00......", "some kind of long string with only ascii characters.", "some kind of long string with a non-ascii character at the end.\xff", strings.Repeat("1234567890", 1000), } var testStringsUTF8 []string func init() { for _, test := range testStrings { if utf8.ValidString(test) { testStringsUTF8 = append(testStringsUTF8, test) } } } func testString(s string, f func(byte) bool) bool { for i := range s { if !f(s[i]) { return false } } return true } func testValid(s string) bool { return testString(s, ValidByte) } func testValidPrint(s string) bool { return testString(s, ValidPrintByte) } func TestValid(t *testing.T) { testValidationFunction(t, testValid, ValidString) } func TestValidPrint(t *testing.T) { testValidationFunction(t, testValidPrint, ValidPrintString) } func testValidationFunction(t *testing.T, reference, function func(string) bool) { for _, test := range testStrings { t.Run(limit(test), func(t *testing.T) { expect := reference(test) if valid := function(test); expect != valid { t.Errorf("expected %t but got %t", expect, valid) } }) } } func BenchmarkValid(b *testing.B) { benchmarkValidationFunction(b, ValidString) } func BenchmarkValidPrint(b *testing.B) { benchmarkValidationFunction(b, ValidPrintString) } func benchmarkValidationFunction(b *testing.B, function func(string) bool) { for _, test := range testStrings { b.Run(limit(test), func(b *testing.B) { for i := 0; i < b.N; i++ { _ = function(test) } b.SetBytes(int64(len(test))) }) } } func limit(s string) string { if len(s) > 17 { return s[:17] + "..." } return s } func TestHasPrefixFold(t *testing.T) { for _, test := range testStringsUTF8 { t.Run(limit(test), func(t *testing.T) { prefix := test if len(prefix) > 0 { prefix = prefix[:len(prefix)/2] } upper := strings.ToUpper(prefix) lower := strings.ToLower(prefix) if !HasPrefixFoldString(test, prefix) { t.Errorf("%q does not match %q", test, prefix) } if !HasPrefixFoldString(test, upper) { t.Errorf("%q does not match %q", test, upper) } if !HasPrefixFoldString(test, lower) { t.Errorf("%q does not match %q", test, lower) } }) } } func TestHasSuffixFold(t *testing.T) { for _, test := range testStringsUTF8 { t.Run(limit(test), func(t *testing.T) { suffix := test if len(suffix) > 0 { suffix = suffix[len(suffix)/2:] } upper := strings.ToUpper(suffix) lower := strings.ToLower(suffix) if !HasSuffixFoldString(test, suffix) { t.Errorf("%q does not match %q", test, suffix) } if !HasSuffixFoldString(test, upper) { t.Errorf("%q does not match %q", test, upper) } if !HasSuffixFoldString(test, lower) { t.Errorf("%q does not match %q", test, lower) } }) } } func TestEqualFoldASCII(t *testing.T) { pairs := [...][2]byte{ {0, ' '}, {'@', '`'}, {'[', '{'}, {'_', 127}, } for _, pair := range pairs { t.Run(fmt.Sprintf("0x%02x=0x%02x", pair[0], pair[1]), func(t *testing.T) { for i := 1; i <= 256; i++ { a := bytes.Repeat([]byte{'x'}, i) b := bytes.Repeat([]byte{'X'}, i) if !EqualFold(a, b) { t.Errorf("%q does not match %q", a, b) break } a[0] = pair[0] b[0] = pair[1] if EqualFold(a, b) { t.Errorf("%q matches %q", a, b) break } } }) } } func TestEqualFold(t *testing.T) { // Only test valid UTF-8 otherwise ToUpper/ToLower will convert invalid // characters to UTF-8 placeholders, which breaks the case-insensitive // equality. for _, test := range testStringsUTF8 { t.Run(limit(test), func(t *testing.T) { upper := strings.ToUpper(test) lower := strings.ToLower(test) if !EqualFoldString(test, test) { t.Errorf("%q does not match %q", test, test) } if !EqualFoldString(test, upper) { t.Errorf("%q does not match %q", test, upper) } if !EqualFoldString(test, lower) { t.Errorf("%q does not match %q", test, lower) } if len(test) > 1 { reverse := make([]byte, len(test)) for i := range reverse { reverse[i] = test[len(test)-(i+1)] } if EqualFoldString(test, string(reverse)) { t.Errorf("%q matches %q", test, reverse) } } }) } } func BenchmarkEqualFold(b *testing.B) { for _, test := range testStringsUTF8 { b.Run(limit(test), func(b *testing.B) { other := test + "_" // not the same pointer for i := 0; i < b.N; i++ { _ = EqualFoldString(test, other[:len(test)]) // same length } b.SetBytes(int64(len(test))) }) } }