Files
warmbly/internal/pkg/casefold/casefold_test.go
T

76 lines
2.4 KiB
Go

package casefold
import (
"strings"
"testing"
"unicode/utf8"
)
// Everything here rests on Index folding exactly the way strings.ToLower does:
// callers search one string and count against the other. Invalid
// UTF-8 is in the table because a range loop and strings.Map both turn a bad
// byte into U+FFFD, which is three bytes where the original was one.
func TestIndexMatchesStringsToLower(t *testing.T) {
cases := []string{
"", "plain ascii text", "FREE CASH", "İİİ free", "KKK cash", "café",
"🎉 free", "Straße", "Džungla", "fi ligature", "\xff\xfe bad bytes",
"mixed İ \xc3\x28 free", strings.Repeat("İ", 40) + " free",
}
for _, s := range cases {
lower, offsets := Index(s)
if want := strings.ToLower(s); lower != want {
t.Errorf("Index(%q) folded to %q, want %q", s, lower, want)
}
if offsets == nil {
continue
}
if len(offsets) != len(lower)+1 {
t.Errorf("Index(%q): %d offsets for %d bytes", s, len(offsets), len(lower))
continue
}
// Every offset must land on a rune boundary in the original, or a
// slice taken at it cuts a rune in half.
for i, at := range offsets {
if at < 0 || at > len(s) {
t.Fatalf("Index(%q): offset %d out of range at %d", s, at, i)
}
if at < len(s) && !utf8.RuneStart(s[at]) {
t.Errorf("Index(%q): offset %d at index %d is mid-rune", s, at, i)
}
}
if offsets[len(offsets)-1] != len(s) {
t.Errorf("Index(%q): last offset %d, want %d", s, offsets[len(offsets)-1], len(s))
}
}
}
// Origin must say "cannot quote this" rather than pointing at position zero,
// which would silently quote the start of the text for a match that moved out
// of range.
func TestOriginRefusesAnOffsetOutsideTheMap(t *testing.T) {
_, offsets := Index("İ free")
if got := Origin(offsets, len(offsets)); got != -1 {
t.Errorf("Origin past the end = %d, want -1", got)
}
if got := Origin(offsets, -1); got != -1 {
t.Errorf("Origin of a negative offset = %d, want -1", got)
}
if got := Origin(nil, -1); got != -1 {
t.Errorf("Origin of a negative offset with no map = %d, want -1", got)
}
}
// ASCII text needs no map, and the offsets pass straight through.
func TestIndexSkipsTheMapForASCII(t *testing.T) {
lower, offsets := Index("FREE Cash")
if lower != "free cash" {
t.Errorf("folded to %q", lower)
}
if offsets != nil {
t.Errorf("built a map for ASCII text: %v", offsets)
}
if got := Origin(offsets, 5); got != 5 {
t.Errorf("Origin = %d, want the offset unchanged", got)
}
}