Files
bl/common/utils/concurrent-swiss-map/concurrent_swiss_map_test.go

333 lines
6.3 KiB
Go

package csmap_test
import (
"strconv"
"sync"
"testing"
csmap "github.com/mhmtszr/concurrent-swiss-map"
)
func TestHas(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.Store(1, "test")
if !myMap.Has(1) {
t.Fatal("1 should exists")
}
}
func TestLoad(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.Store(1, "test")
v, ok := myMap.Load(1)
v2, ok2 := myMap.Load(2)
if v != "test" || !ok {
t.Fatal("1 should test")
}
if v2 != "" || ok2 {
t.Fatal("2 should not exist")
}
}
func TestDelete(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.Store(1, "test")
ok1 := myMap.Delete(20)
ok2 := myMap.Delete(1)
if myMap.Has(1) {
t.Fatal("1 should be deleted")
}
if ok1 {
t.Fatal("ok1 should be false")
}
if !ok2 {
t.Fatal("ok2 should be true")
}
}
func TestSetIfAbsent(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.SetIfAbsent(1, "test")
if !myMap.Has(1) {
t.Fatal("1 should be exist")
}
}
func TestSetIfPresent(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.SetIfPresent(1, "test")
if myMap.Has(1) {
t.Fatal("1 should be not exist")
}
myMap.Store(1, "test")
myMap.SetIfPresent(1, "new-test")
val, _ := myMap.Load(1)
if val != "new-test" {
t.Fatal("val should be new-test")
}
}
func TestSetIf(t *testing.T) {
myMap := csmap.New[int, string]()
valueA := "value a"
myMap.SetIf(1, func(previousVale string, previousFound bool) (value string, set bool) {
// operate like a SetIfAbsent...
if !previousFound {
return valueA, true
}
return "", false
})
value, _ := myMap.Load(1)
if value != valueA {
t.Fatal("value should value a")
}
myMap.SetIf(1, func(previousVale string, previousFound bool) (value string, set bool) {
// operate like a SetIfAbsent...
if !previousFound {
return "bad", true
}
return "", false
})
value, _ = myMap.Load(1)
if value != valueA {
t.Fatal("value should value a")
}
}
func TestDeleteIf(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.Store(1, "value b")
ok1 := myMap.DeleteIf(20, func(value string) bool {
t.Fatal("condition function should not have been called")
return false
})
if ok1 {
t.Fatal("ok1 should be false")
}
ok2 := myMap.DeleteIf(1, func(value string) bool {
if value != "value b" {
t.Fatal("condition function arg should be tests")
}
return false // don't delete
})
if ok2 {
t.Fatal("ok1 should be false")
}
ok3 := myMap.DeleteIf(1, func(value string) bool {
if value != "value b" {
t.Fatal("condition function arg should be tests")
}
return true // delete the entry
})
if !ok3 {
t.Fatal("ok2 should be true")
}
}
func TestCount(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.SetIfAbsent(1, "test")
myMap.SetIfAbsent(2, "test2")
if myMap.Count() != 2 {
t.Fatal("count should be 2")
}
}
func TestIsEmpty(t *testing.T) {
myMap := csmap.New[int, string]()
if !myMap.IsEmpty() {
t.Fatal("map should be empty")
}
}
func TestRangeStop(t *testing.T) {
myMap := csmap.New[int, string](
csmap.WithShardCount[int, string](1),
)
myMap.SetIfAbsent(1, "test")
myMap.SetIfAbsent(2, "test2")
myMap.SetIfAbsent(3, "test2")
total := 0
myMap.Range(func(key int, value string) (stop bool) {
total++
return true
})
if total != 1 {
t.Fatal("total should be 1")
}
}
func TestRange(t *testing.T) {
myMap := csmap.New[int, string]()
myMap.SetIfAbsent(1, "test")
myMap.SetIfAbsent(2, "test2")
total := 0
myMap.Range(func(key int, value string) (stop bool) {
total++
return
})
if total != 2 {
t.Fatal("total should be 2")
}
}
func TestCustomHasherWithRange(t *testing.T) {
myMap := csmap.New[int, string](
csmap.WithCustomHasher[int, string](func(key int) uint64 {
return 0
}),
)
myMap.SetIfAbsent(1, "test")
myMap.SetIfAbsent(2, "test2")
myMap.SetIfAbsent(3, "test2")
myMap.SetIfAbsent(4, "test2")
total := 0
myMap.Range(func(key int, value string) (stop bool) {
total++
return true
})
if total != 1 {
t.Fatal("total should be 1, because currently range stops current shard only.")
}
}
func TestDeleteFromRange(t *testing.T) {
myMap := csmap.New[string, int](
csmap.WithSize[string, int](1024),
)
myMap.Store("aaa", 10)
myMap.Store("aab", 11)
myMap.Store("aac", 15)
myMap.Store("aad", 124)
myMap.Store("aaf", 987)
myMap.Range(func(key string, value int) (stop bool) {
if value > 20 {
myMap.Delete(key)
}
return false
})
if myMap.Count() != 3 {
t.Fatal("total should be 3, because currently range deletes values that bigger than 20.")
}
}
func TestMarshal(t *testing.T) {
myMap := csmap.New[string, int](
csmap.WithSize[string, int](1024),
)
myMap.Store("aaa", 10)
myMap.Store("aab", 11)
b, _ := myMap.MarshalJSON()
newMap := csmap.New[string, int](
csmap.WithSize[string, int](1024),
)
_ = newMap.UnmarshalJSON(b)
if myMap.Count() != 2 || !myMap.Has("aaa") || !myMap.Has("aab") {
t.Fatal("count should be 2 after unmarshal")
}
}
func TestBasicConcurrentWriteDeleteCount(t *testing.T) {
myMap := csmap.New[int, string](
csmap.WithShardCount[int, string](32),
csmap.WithSize[int, string](1000),
)
var wg sync.WaitGroup
wg.Add(1000000)
for i := 0; i < 1000000; i++ {
i := i
go func() {
defer wg.Done()
myMap.Store(i, strconv.Itoa(i))
}()
}
wg.Wait()
wg.Add(1000000)
for i := 0; i < 1000000; i++ {
i := i
go func() {
defer wg.Done()
if !myMap.Has(i) {
t.Error(strconv.Itoa(i) + " should exist")
return
}
}()
}
wg.Wait()
wg.Add(1000000)
for i := 0; i < 1000000; i++ {
i := i
go func() {
defer wg.Done()
myMap.Delete(i)
}()
}
wg.Wait()
wg.Add(1000000)
for i := 0; i < 1000000; i++ {
i := i
go func() {
defer wg.Done()
if myMap.Has(i) {
t.Error(strconv.Itoa(i) + " should not exist")
return
}
}()
}
wg.Wait()
}
func TestClear(t *testing.T) {
myMap := csmap.New[int, string]()
loop := 10000
for i := 0; i < loop; i++ {
myMap.Store(i, "test")
}
myMap.Clear()
if !myMap.IsEmpty() {
t.Fatal("count should be true")
}
// store again
for i := 0; i < loop; i++ {
myMap.Store(i, "test")
}
// get again
for i := 0; i < loop; i++ {
val, ok := myMap.Load(i)
if ok != true {
t.Fatal("ok should be true")
}
if val != "test" {
t.Fatal("val should be test")
}
}
// check again
count := myMap.Count()
if count != loop {
t.Fatal("count should be 1000")
}
}