tailscale/util/nocasemaps/nocase_test.go
Joe Tsai 8f86d4f8b9 all: use slices.Collect with maps.Keys instead of xmaps.Keys
In Go 1.23, the standard maps.Keys helper was altered relative to xmaps.Keys
to return and iterator, which can be used with slices.Collect.

Also, Go 1.21 added the clear built-in, which replaces xmaps.Clear,
and is semantically more correct with respect to NaNs.

Updates #8632
Updates #12912
Updates #cleanup

Signed-off-by: Joe Tsai <joetsai@digital-static.net>
2024-10-11 11:41:09 -07:00

160 lines
4.7 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package nocasemaps
import (
"strings"
"testing"
qt "github.com/frankban/quicktest"
)
func pair[A, B any](a A, b B) (out struct {
A A
B B
}) {
out.A = a
out.B = b
return out
}
func Test(t *testing.T) {
c := qt.New(t)
m := make(map[string]int)
Set(m, "hello", 1)
c.Assert(m, qt.DeepEquals, map[string]int{"hello": 1})
Set(m, "HeLlO", 2)
c.Assert(m, qt.DeepEquals, map[string]int{"hello": 2})
c.Assert(Get(m, "hello"), qt.Equals, 2)
c.Assert(pair(GetOk(m, "hello")), qt.Equals, pair(2, true))
c.Assert(Get(m, "HeLlO"), qt.Equals, 2)
c.Assert(pair(GetOk(m, "HeLlO")), qt.Equals, pair(2, true))
c.Assert(Get(m, "HELLO"), qt.Equals, 2)
c.Assert(pair(GetOk(m, "HELLO")), qt.Equals, pair(2, true))
c.Assert(Get(m, "missing"), qt.Equals, 0)
c.Assert(pair(GetOk(m, "missing")), qt.Equals, pair(0, false))
Set(m, "foo", 3)
Set(m, "BAR", 4)
Set(m, "bAz", 5)
c.Assert(m, qt.DeepEquals, map[string]int{"hello": 2, "foo": 3, "bar": 4, "baz": 5})
Delete(m, "foo")
c.Assert(m, qt.DeepEquals, map[string]int{"hello": 2, "bar": 4, "baz": 5})
Delete(m, "bar")
c.Assert(m, qt.DeepEquals, map[string]int{"hello": 2, "baz": 5})
Delete(m, "BAZ")
c.Assert(m, qt.DeepEquals, map[string]int{"hello": 2})
// test cases for AppendSliceElem with int slices
appendTestInt := make(map[string][]int)
Set(appendTestInt, "firsT", []int{7})
c.Assert(appendTestInt, qt.DeepEquals, map[string][]int{"first": {7}})
AppendSliceElem(appendTestInt, "firsT", 77)
c.Assert(appendTestInt, qt.DeepEquals, map[string][]int{"first": {7, 77}})
Set(appendTestInt, "SeCOnd", []int{56})
c.Assert(appendTestInt, qt.DeepEquals, map[string][]int{"first": {7, 77}, "second": {56}})
AppendSliceElem(appendTestInt, "seCOnd", 563, 23)
c.Assert(appendTestInt, qt.DeepEquals, map[string][]int{"first": {7, 77}, "second": {56, 563, 23}})
// test cases for AppendSliceElem with string slices
appendTestString := make(map[string][]string)
Set(appendTestString, "firsTSTRING", []string{"hi"})
c.Assert(appendTestString, qt.DeepEquals, map[string][]string{"firststring": {"hi"}})
AppendSliceElem(appendTestString, "firsTSTRING", "hello", "bye")
c.Assert(appendTestString, qt.DeepEquals, map[string][]string{"firststring": {"hi", "hello", "bye"}})
}
var lowerTests = []struct{ in, want string }{
{"", ""},
{"abc", "abc"},
{"AbC123", "abc123"},
{"azAZ09_", "azaz09_"},
{"longStrinGwitHmixofsmaLLandcAps", "longstringwithmixofsmallandcaps"},
{"renan bastos 93 AOSDAJDJAIDJAIDAJIaidsjjaidijadsjiadjiOOKKO", "renan bastos 93 aosdajdjaidjaidajiaidsjjaidijadsjiadjiookko"},
{"LONG\u2C6FSTRING\u2C6FWITH\u2C6FNONASCII\u2C6FCHARS", "long\u0250string\u0250with\u0250nonascii\u0250chars"},
{"\u2C6D\u2C6D\u2C6D\u2C6D\u2C6D", "\u0251\u0251\u0251\u0251\u0251"}, // shrinks one byte per char
{"A\u0080\U0010FFFF", "a\u0080\U0010FFFF"}, // test utf8.RuneSelf and utf8.MaxRune
}
func TestAppendToLower(t *testing.T) {
for _, tt := range lowerTests {
got := string(appendToLower(nil, tt.in))
if got != tt.want {
t.Errorf("appendToLower(%q) = %q, want %q", tt.in, got, tt.want)
}
}
}
func FuzzAppendToLower(f *testing.F) {
for _, tt := range lowerTests {
f.Add(tt.in)
}
f.Fuzz(func(t *testing.T, in string) {
got := string(appendToLower(nil, in))
want := strings.ToLower(in)
if got != want {
t.Errorf("appendToLower(%q) = %q, want %q", in, got, want)
}
})
}
var (
testLower = "production-server"
testUpper = "PRODUCTION-SERVER"
testMap = make(map[string]int)
testValue = 5
testSink int
)
func Benchmark(b *testing.B) {
for i, key := range []string{testLower, testUpper} {
b.Run([]string{"Lower", "Upper"}[i], func(b *testing.B) {
b.Run("Get", func(b *testing.B) {
b.Run("Naive", func(b *testing.B) {
b.ReportAllocs()
for range b.N {
testSink = testMap[strings.ToLower(key)]
}
})
b.Run("NoCase", func(b *testing.B) {
b.ReportAllocs()
for range b.N {
testSink = Get(testMap, key)
}
})
})
b.Run("Set", func(b *testing.B) {
b.Run("Naive", func(b *testing.B) {
b.ReportAllocs()
testMap[strings.ToLower(key)] = testValue
for range b.N {
testMap[strings.ToLower(key)] = testValue
}
clear(testMap)
})
b.Run("NoCase", func(b *testing.B) {
b.ReportAllocs()
Set(testMap, key, testValue)
for range b.N {
Set(testMap, key, testValue)
}
clear(testMap)
})
})
b.Run("Delete", func(b *testing.B) {
b.Run("Naive", func(b *testing.B) {
b.ReportAllocs()
for range b.N {
delete(testMap, strings.ToLower(key))
}
})
b.Run("NoCase", func(b *testing.B) {
b.ReportAllocs()
for range b.N {
Delete(testMap, key)
}
})
})
})
}
}