| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740 |
- package csync
- import (
- "encoding/json"
- "maps"
- "sync"
- "sync/atomic"
- "testing"
- "testing/synctest"
- "time"
- "github.com/stretchr/testify/require"
- )
- func TestNewMap(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- require.NotNil(t, m)
- require.NotNil(t, m.inner)
- require.Equal(t, 0, m.Len())
- }
- func TestNewMapFrom(t *testing.T) {
- t.Parallel()
- original := map[string]int{
- "key1": 1,
- "key2": 2,
- }
- m := NewMapFrom(original)
- require.NotNil(t, m)
- require.Equal(t, original, m.inner)
- require.Equal(t, 2, m.Len())
- value, ok := m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 1, value)
- }
- func TestNewLazyMap(t *testing.T) {
- t.Parallel()
- synctest.Test(t, func(t *testing.T) {
- t.Helper()
- waiter := sync.Mutex{}
- waiter.Lock()
- var loadCalled atomic.Bool
- loadFunc := func() map[string]int {
- waiter.Lock()
- defer waiter.Unlock()
- loadCalled.Store(true)
- return map[string]int{
- "key1": 1,
- "key2": 2,
- }
- }
- m := NewLazyMap(loadFunc)
- require.NotNil(t, m)
- waiter.Unlock() // Allow the load function to proceed
- time.Sleep(100 * time.Millisecond)
- require.True(t, loadCalled.Load())
- require.Equal(t, 2, m.Len())
- value, ok := m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 1, value)
- })
- }
- func TestMap_Reset(t *testing.T) {
- t.Parallel()
- m := NewMapFrom(map[string]int{
- "a": 10,
- })
- m.Reset(map[string]int{
- "b": 20,
- })
- value, ok := m.Get("b")
- require.True(t, ok)
- require.Equal(t, 20, value)
- require.Equal(t, 1, m.Len())
- }
- func TestMap_Set(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 42)
- value, ok := m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 42, value)
- require.Equal(t, 1, m.Len())
- m.Set("key1", 100)
- value, ok = m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 100, value)
- require.Equal(t, 1, m.Len())
- }
- func TestMap_GetOrSet(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- require.Equal(t, 42, m.GetOrSet("key1", func() int { return 42 }))
- require.Equal(t, 42, m.GetOrSet("key1", func() int { return 99999 }))
- require.Equal(t, 1, m.Len())
- }
- func TestMap_Get(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- value, ok := m.Get("nonexistent")
- require.False(t, ok)
- require.Equal(t, 0, value)
- m.Set("key1", 42)
- value, ok = m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 42, value)
- }
- func TestMap_Del(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 42)
- m.Set("key2", 100)
- require.Equal(t, 2, m.Len())
- m.Del("key1")
- _, ok := m.Get("key1")
- require.False(t, ok)
- require.Equal(t, 1, m.Len())
- value, ok := m.Get("key2")
- require.True(t, ok)
- require.Equal(t, 100, value)
- m.Del("nonexistent")
- require.Equal(t, 1, m.Len())
- }
- func TestMap_Len(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- require.Equal(t, 0, m.Len())
- m.Set("key1", 1)
- require.Equal(t, 1, m.Len())
- m.Set("key2", 2)
- require.Equal(t, 2, m.Len())
- m.Del("key1")
- require.Equal(t, 1, m.Len())
- m.Del("key2")
- require.Equal(t, 0, m.Len())
- }
- func TestMap_Take(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 42)
- m.Set("key2", 100)
- require.Equal(t, 2, m.Len())
- value, ok := m.Take("key1")
- require.True(t, ok)
- require.Equal(t, 42, value)
- require.Equal(t, 1, m.Len())
- _, exists := m.Get("key1")
- require.False(t, exists)
- value, ok = m.Get("key2")
- require.True(t, ok)
- require.Equal(t, 100, value)
- }
- func TestMap_Take_NonexistentKey(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 42)
- value, ok := m.Take("nonexistent")
- require.False(t, ok)
- require.Equal(t, 0, value)
- require.Equal(t, 1, m.Len())
- value, ok = m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 42, value)
- }
- func TestMap_Take_EmptyMap(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- value, ok := m.Take("key1")
- require.False(t, ok)
- require.Equal(t, 0, value)
- require.Equal(t, 0, m.Len())
- }
- func TestMap_Take_SameKeyTwice(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 42)
- value, ok := m.Take("key1")
- require.True(t, ok)
- require.Equal(t, 42, value)
- require.Equal(t, 0, m.Len())
- value, ok = m.Take("key1")
- require.False(t, ok)
- require.Equal(t, 0, value)
- require.Equal(t, 0, m.Len())
- }
- func TestMap_Seq2(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 1)
- m.Set("key2", 2)
- m.Set("key3", 3)
- collected := maps.Collect(m.Seq2())
- require.Equal(t, 3, len(collected))
- require.Equal(t, 1, collected["key1"])
- require.Equal(t, 2, collected["key2"])
- require.Equal(t, 3, collected["key3"])
- }
- func TestMap_Seq2_EarlyReturn(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 1)
- m.Set("key2", 2)
- m.Set("key3", 3)
- count := 0
- for range m.Seq2() {
- count++
- if count == 2 {
- break
- }
- }
- require.Equal(t, 2, count)
- }
- func TestMap_Seq2_EmptyMap(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- count := 0
- for range m.Seq2() {
- count++
- }
- require.Equal(t, 0, count)
- }
- func TestMap_Seq(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 1)
- m.Set("key2", 2)
- m.Set("key3", 3)
- collected := make([]int, 0)
- for v := range m.Seq() {
- collected = append(collected, v)
- }
- require.Equal(t, 3, len(collected))
- require.Contains(t, collected, 1)
- require.Contains(t, collected, 2)
- require.Contains(t, collected, 3)
- }
- func TestMap_Seq_EarlyReturn(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 1)
- m.Set("key2", 2)
- m.Set("key3", 3)
- count := 0
- for range m.Seq() {
- count++
- if count == 2 {
- break
- }
- }
- require.Equal(t, 2, count)
- }
- func TestMap_Seq_EmptyMap(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- count := 0
- for range m.Seq() {
- count++
- }
- require.Equal(t, 0, count)
- }
- func TestMap_MarshalJSON(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("key1", 1)
- m.Set("key2", 2)
- data, err := json.Marshal(m)
- require.NoError(t, err)
- result := &Map[string, int]{}
- err = json.Unmarshal(data, result)
- require.NoError(t, err)
- require.Equal(t, 2, result.Len())
- v1, _ := result.Get("key1")
- v2, _ := result.Get("key2")
- require.Equal(t, 1, v1)
- require.Equal(t, 2, v2)
- }
- func TestMap_MarshalJSON_EmptyMap(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- data, err := json.Marshal(m)
- require.NoError(t, err)
- require.Equal(t, "{}", string(data))
- }
- func TestMap_UnmarshalJSON(t *testing.T) {
- t.Parallel()
- jsonData := `{"key1": 1, "key2": 2}`
- m := NewMap[string, int]()
- err := json.Unmarshal([]byte(jsonData), m)
- require.NoError(t, err)
- require.Equal(t, 2, m.Len())
- value, ok := m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 1, value)
- value, ok = m.Get("key2")
- require.True(t, ok)
- require.Equal(t, 2, value)
- }
- func TestMap_UnmarshalJSON_EmptyJSON(t *testing.T) {
- t.Parallel()
- jsonData := `{}`
- m := NewMap[string, int]()
- err := json.Unmarshal([]byte(jsonData), m)
- require.NoError(t, err)
- require.Equal(t, 0, m.Len())
- }
- func TestMap_UnmarshalJSON_InvalidJSON(t *testing.T) {
- t.Parallel()
- jsonData := `{"key1": 1, "key2":}`
- m := NewMap[string, int]()
- err := json.Unmarshal([]byte(jsonData), m)
- require.Error(t, err)
- }
- func TestMap_UnmarshalJSON_OverwritesExistingData(t *testing.T) {
- t.Parallel()
- m := NewMap[string, int]()
- m.Set("existing", 999)
- jsonData := `{"key1": 1, "key2": 2}`
- err := json.Unmarshal([]byte(jsonData), m)
- require.NoError(t, err)
- require.Equal(t, 2, m.Len())
- _, ok := m.Get("existing")
- require.False(t, ok)
- value, ok := m.Get("key1")
- require.True(t, ok)
- require.Equal(t, 1, value)
- }
- func TestMap_JSONRoundTrip(t *testing.T) {
- t.Parallel()
- original := NewMap[string, int]()
- original.Set("key1", 1)
- original.Set("key2", 2)
- original.Set("key3", 3)
- data, err := json.Marshal(original)
- require.NoError(t, err)
- restored := NewMap[string, int]()
- err = json.Unmarshal(data, restored)
- require.NoError(t, err)
- require.Equal(t, original.Len(), restored.Len())
- for k, v := range original.Seq2() {
- restoredValue, ok := restored.Get(k)
- require.True(t, ok)
- require.Equal(t, v, restoredValue)
- }
- }
- func TestMap_ConcurrentAccess(t *testing.T) {
- t.Parallel()
- m := NewMap[int, int]()
- const numGoroutines = 100
- const numOperations = 100
- var wg sync.WaitGroup
- wg.Add(numGoroutines)
- for i := range numGoroutines {
- go func(id int) {
- defer wg.Done()
- for j := range numOperations {
- key := id*numOperations + j
- m.Set(key, key*2)
- value, ok := m.Get(key)
- require.True(t, ok)
- require.Equal(t, key*2, value)
- }
- }(i)
- }
- wg.Wait()
- require.Equal(t, numGoroutines*numOperations, m.Len())
- }
- func TestMap_ConcurrentReadWrite(t *testing.T) {
- t.Parallel()
- m := NewMap[int, int]()
- const numReaders = 50
- const numWriters = 50
- const numOperations = 100
- for i := range 1000 {
- m.Set(i, i)
- }
- var wg sync.WaitGroup
- wg.Add(numReaders + numWriters)
- for range numReaders {
- go func() {
- defer wg.Done()
- for j := range numOperations {
- key := j % 1000
- value, ok := m.Get(key)
- if ok {
- require.Equal(t, key, value)
- }
- _ = m.Len()
- }
- }()
- }
- for i := range numWriters {
- go func(id int) {
- defer wg.Done()
- for j := range numOperations {
- key := 1000 + id*numOperations + j
- m.Set(key, key)
- if j%10 == 0 {
- m.Del(key)
- }
- }
- }(i)
- }
- wg.Wait()
- }
- func TestMap_ConcurrentSeq2(t *testing.T) {
- t.Parallel()
- m := NewMap[int, int]()
- for i := range 100 {
- m.Set(i, i*2)
- }
- var wg sync.WaitGroup
- const numIterators = 10
- wg.Add(numIterators)
- for range numIterators {
- go func() {
- defer wg.Done()
- count := 0
- for k, v := range m.Seq2() {
- require.Equal(t, k*2, v)
- count++
- }
- require.Equal(t, 100, count)
- }()
- }
- wg.Wait()
- }
- func TestMap_ConcurrentSeq(t *testing.T) {
- t.Parallel()
- m := NewMap[int, int]()
- for i := range 100 {
- m.Set(i, i*2)
- }
- var wg sync.WaitGroup
- const numIterators = 10
- wg.Add(numIterators)
- for range numIterators {
- go func() {
- defer wg.Done()
- count := 0
- values := make(map[int]bool)
- for v := range m.Seq() {
- values[v] = true
- count++
- }
- require.Equal(t, 100, count)
- for i := range 100 {
- require.True(t, values[i*2])
- }
- }()
- }
- wg.Wait()
- }
- func TestMap_ConcurrentTake(t *testing.T) {
- t.Parallel()
- m := NewMap[int, int]()
- const numItems = 1000
- for i := range numItems {
- m.Set(i, i*2)
- }
- var wg sync.WaitGroup
- const numWorkers = 10
- taken := make([][]int, numWorkers)
- wg.Add(numWorkers)
- for i := range numWorkers {
- go func(workerID int) {
- defer wg.Done()
- taken[workerID] = make([]int, 0)
- for j := workerID; j < numItems; j += numWorkers {
- if value, ok := m.Take(j); ok {
- taken[workerID] = append(taken[workerID], value)
- }
- }
- }(i)
- }
- wg.Wait()
- require.Equal(t, 0, m.Len())
- allTaken := make(map[int]bool)
- for _, workerTaken := range taken {
- for _, value := range workerTaken {
- require.False(t, allTaken[value], "Value %d was taken multiple times", value)
- allTaken[value] = true
- }
- }
- require.Equal(t, numItems, len(allTaken))
- for i := range numItems {
- require.True(t, allTaken[i*2], "Expected value %d to be taken", i*2)
- }
- }
- func TestMap_TypeSafety(t *testing.T) {
- t.Parallel()
- stringIntMap := NewMap[string, int]()
- stringIntMap.Set("key", 42)
- value, ok := stringIntMap.Get("key")
- require.True(t, ok)
- require.Equal(t, 42, value)
- intStringMap := NewMap[int, string]()
- intStringMap.Set(42, "value")
- strValue, ok := intStringMap.Get(42)
- require.True(t, ok)
- require.Equal(t, "value", strValue)
- structMap := NewMap[string, struct{ Name string }]()
- structMap.Set("key", struct{ Name string }{Name: "test"})
- structValue, ok := structMap.Get("key")
- require.True(t, ok)
- require.Equal(t, "test", structValue.Name)
- }
- func TestMap_InterfaceCompliance(t *testing.T) {
- t.Parallel()
- var _ json.Marshaler = &Map[string, any]{}
- var _ json.Unmarshaler = &Map[string, any]{}
- }
- func BenchmarkMap_Set(b *testing.B) {
- m := NewMap[int, int]()
- for i := 0; b.Loop(); i++ {
- m.Set(i, i*2)
- }
- }
- func BenchmarkMap_Get(b *testing.B) {
- m := NewMap[int, int]()
- for i := range 1000 {
- m.Set(i, i*2)
- }
- for i := 0; b.Loop(); i++ {
- m.Get(i % 1000)
- }
- }
- func BenchmarkMap_Seq2(b *testing.B) {
- m := NewMap[int, int]()
- for i := range 1000 {
- m.Set(i, i*2)
- }
- for b.Loop() {
- for range m.Seq2() {
- }
- }
- }
- func BenchmarkMap_Seq(b *testing.B) {
- m := NewMap[int, int]()
- for i := range 1000 {
- m.Set(i, i*2)
- }
- for b.Loop() {
- for range m.Seq() {
- }
- }
- }
- func BenchmarkMap_Take(b *testing.B) {
- m := NewMap[int, int]()
- for i := range 1000 {
- m.Set(i, i*2)
- }
- b.ResetTimer()
- for i := 0; b.Loop(); i++ {
- key := i % 1000
- m.Take(key)
- if i%1000 == 999 {
- b.StopTimer()
- for j := range 1000 {
- m.Set(j, j*2)
- }
- b.StartTimer()
- }
- }
- }
- func BenchmarkMap_ConcurrentReadWrite(b *testing.B) {
- m := NewMap[int, int]()
- for i := range 1000 {
- m.Set(i, i*2)
- }
- b.ResetTimer()
- b.RunParallel(func(pb *testing.PB) {
- i := 0
- for pb.Next() {
- if i%2 == 0 {
- m.Get(i % 1000)
- } else {
- m.Set(i+1000, i*2)
- }
- i++
- }
- })
- }
|