on e00a70a help sign in
crush/internal/csync/maps_test.go
687 lines 12 kB history raw
1package csync
3import (
4 "encoding/json"
5 "maps"
6 "sync"
7 "testing"
9 "github.com/stretchr/testify/require"
12func TestNewMap(t *testing.T) {
13 t.Parallel()
15 m := NewMap[string, int]()
16 require.NotNil(t, m)
17 require.NotNil(t, m.inner)
18 require.Equal(t, 0, m.Len())
21func TestNewMapFrom(t *testing.T) {
22 t.Parallel()
24 original := map[string]int{
25 "key1": 1,
26 "key2": 2,
27 }
29 m := NewMapFrom(original)
30 require.NotNil(t, m)
31 require.Equal(t, original, m.inner)
32 require.Equal(t, 2, m.Len())
34 value, ok := m.Get("key1")
35 require.True(t, ok)
36 require.Equal(t, 1, value)
39func TestMap_Set(t *testing.T) {
40 t.Parallel()
42 m := NewMap[string, int]()
44 m.Set("key1", 42)
45 value, ok := m.Get("key1")
46 require.True(t, ok)
47 require.Equal(t, 42, value)
48 require.Equal(t, 1, m.Len())
50 m.Set("key1", 100)
51 value, ok = m.Get("key1")
52 require.True(t, ok)
53 require.Equal(t, 100, value)
54 require.Equal(t, 1, m.Len())
57func TestMap_GetOrSet(t *testing.T) {
58 t.Parallel()
60 m := NewMap[string, int]()
62 require.Equal(t, 42, m.GetOrSet("key1", func() int { return 42 }))
63 require.Equal(t, 42, m.GetOrSet("key1", func() int { return 99999 }))
64 require.Equal(t, 1, m.Len())
67func TestMap_Get(t *testing.T) {
68 t.Parallel()
70 m := NewMap[string, int]()
72 value, ok := m.Get("nonexistent")
73 require.False(t, ok)
74 require.Equal(t, 0, value)
76 m.Set("key1", 42)
77 value, ok = m.Get("key1")
78 require.True(t, ok)
79 require.Equal(t, 42, value)
82func TestMap_Del(t *testing.T) {
83 t.Parallel()
85 m := NewMap[string, int]()
86 m.Set("key1", 42)
87 m.Set("key2", 100)
89 require.Equal(t, 2, m.Len())
91 m.Del("key1")
92 _, ok := m.Get("key1")
93 require.False(t, ok)
94 require.Equal(t, 1, m.Len())
96 value, ok := m.Get("key2")
97 require.True(t, ok)
98 require.Equal(t, 100, value)
100 m.Del("nonexistent")
101 require.Equal(t, 1, m.Len())
104func TestMap_Len(t *testing.T) {
105 t.Parallel()
107 m := NewMap[string, int]()
108 require.Equal(t, 0, m.Len())
110 m.Set("key1", 1)
111 require.Equal(t, 1, m.Len())
113 m.Set("key2", 2)
114 require.Equal(t, 2, m.Len())
116 m.Del("key1")
117 require.Equal(t, 1, m.Len())
119 m.Del("key2")
120 require.Equal(t, 0, m.Len())
123func TestMap_Take(t *testing.T) {
124 t.Parallel()
126 m := NewMap[string, int]()
127 m.Set("key1", 42)
128 m.Set("key2", 100)
130 require.Equal(t, 2, m.Len())
132 value, ok := m.Take("key1")
133 require.True(t, ok)
134 require.Equal(t, 42, value)
135 require.Equal(t, 1, m.Len())
137 _, exists := m.Get("key1")
138 require.False(t, exists)
140 value, ok = m.Get("key2")
141 require.True(t, ok)
142 require.Equal(t, 100, value)
145func TestMap_Take_NonexistentKey(t *testing.T) {
146 t.Parallel()
148 m := NewMap[string, int]()
149 m.Set("key1", 42)
151 value, ok := m.Take("nonexistent")
152 require.False(t, ok)
153 require.Equal(t, 0, value)
154 require.Equal(t, 1, m.Len())
156 value, ok = m.Get("key1")
157 require.True(t, ok)
158 require.Equal(t, 42, value)
161func TestMap_Take_EmptyMap(t *testing.T) {
162 t.Parallel()
164 m := NewMap[string, int]()
166 value, ok := m.Take("key1")
167 require.False(t, ok)
168 require.Equal(t, 0, value)
169 require.Equal(t, 0, m.Len())
172func TestMap_Take_SameKeyTwice(t *testing.T) {
173 t.Parallel()
175 m := NewMap[string, int]()
176 m.Set("key1", 42)
178 value, ok := m.Take("key1")
179 require.True(t, ok)
180 require.Equal(t, 42, value)
181 require.Equal(t, 0, m.Len())
183 value, ok = m.Take("key1")
184 require.False(t, ok)
185 require.Equal(t, 0, value)
186 require.Equal(t, 0, m.Len())
189func TestMap_Seq2(t *testing.T) {
190 t.Parallel()
192 m := NewMap[string, int]()
193 m.Set("key1", 1)
194 m.Set("key2", 2)
195 m.Set("key3", 3)
197 collected := maps.Collect(m.Seq2())
199 require.Equal(t, 3, len(collected))
200 require.Equal(t, 1, collected["key1"])
201 require.Equal(t, 2, collected["key2"])
202 require.Equal(t, 3, collected["key3"])
205func TestMap_Seq2_EarlyReturn(t *testing.T) {
206 t.Parallel()
208 m := NewMap[string, int]()
209 m.Set("key1", 1)
210 m.Set("key2", 2)
211 m.Set("key3", 3)
213 count := 0
214 for range m.Seq2() {
215 count++
216 if count == 2 {
217 break
218 }
219 }
221 require.Equal(t, 2, count)
224func TestMap_Seq2_EmptyMap(t *testing.T) {
225 t.Parallel()
227 m := NewMap[string, int]()
229 count := 0
230 for range m.Seq2() {
231 count++
232 }
234 require.Equal(t, 0, count)
237func TestMap_Seq(t *testing.T) {
238 t.Parallel()
240 m := NewMap[string, int]()
241 m.Set("key1", 1)
242 m.Set("key2", 2)
243 m.Set("key3", 3)
245 collected := make([]int, 0)
246 for v := range m.Seq() {
247 collected = append(collected, v)
248 }
250 require.Equal(t, 3, len(collected))
251 require.Contains(t, collected, 1)
252 require.Contains(t, collected, 2)
253 require.Contains(t, collected, 3)
256func TestMap_Seq_EarlyReturn(t *testing.T) {
257 t.Parallel()
259 m := NewMap[string, int]()
260 m.Set("key1", 1)
261 m.Set("key2", 2)
262 m.Set("key3", 3)
264 count := 0
265 for range m.Seq() {
266 count++
267 if count == 2 {
268 break
269 }
270 }
272 require.Equal(t, 2, count)
275func TestMap_Seq_EmptyMap(t *testing.T) {
276 t.Parallel()
278 m := NewMap[string, int]()
280 count := 0
281 for range m.Seq() {
282 count++
283 }
285 require.Equal(t, 0, count)
288func TestMap_MarshalJSON(t *testing.T) {
289 t.Parallel()
291 m := NewMap[string, int]()
292 m.Set("key1", 1)
293 m.Set("key2", 2)
295 data, err := json.Marshal(m)
296 require.NoError(t, err)
298 result := &Map[string, int]{}
299 err = json.Unmarshal(data, result)
300 require.NoError(t, err)
301 require.Equal(t, 2, result.Len())
302 v1, _ := result.Get("key1")
303 v2, _ := result.Get("key2")
304 require.Equal(t, 1, v1)
305 require.Equal(t, 2, v2)
308func TestMap_MarshalJSON_EmptyMap(t *testing.T) {
309 t.Parallel()
311 m := NewMap[string, int]()
313 data, err := json.Marshal(m)
314 require.NoError(t, err)
315 require.Equal(t, "{}", string(data))
318func TestMap_UnmarshalJSON(t *testing.T) {
319 t.Parallel()
321 jsonData := `{"key1": 1, "key2": 2}`
323 m := NewMap[string, int]()
324 err := json.Unmarshal([]byte(jsonData), m)
325 require.NoError(t, err)
327 require.Equal(t, 2, m.Len())
328 value, ok := m.Get("key1")
329 require.True(t, ok)
330 require.Equal(t, 1, value)
332 value, ok = m.Get("key2")
333 require.True(t, ok)
334 require.Equal(t, 2, value)
337func TestMap_UnmarshalJSON_EmptyJSON(t *testing.T) {
338 t.Parallel()
340 jsonData := `{}`
342 m := NewMap[string, int]()
343 err := json.Unmarshal([]byte(jsonData), m)
344 require.NoError(t, err)
345 require.Equal(t, 0, m.Len())
348func TestMap_UnmarshalJSON_InvalidJSON(t *testing.T) {
349 t.Parallel()
351 jsonData := `{"key1": 1, "key2":}`
353 m := NewMap[string, int]()
354 err := json.Unmarshal([]byte(jsonData), m)
355 require.Error(t, err)
358func TestMap_UnmarshalJSON_OverwritesExistingData(t *testing.T) {
359 t.Parallel()
361 m := NewMap[string, int]()
362 m.Set("existing", 999)
364 jsonData := `{"key1": 1, "key2": 2}`
365 err := json.Unmarshal([]byte(jsonData), m)
366 require.NoError(t, err)
368 require.Equal(t, 2, m.Len())
369 _, ok := m.Get("existing")
370 require.False(t, ok)
372 value, ok := m.Get("key1")
373 require.True(t, ok)
374 require.Equal(t, 1, value)
377func TestMap_JSONRoundTrip(t *testing.T) {
378 t.Parallel()
380 original := NewMap[string, int]()
381 original.Set("key1", 1)
382 original.Set("key2", 2)
383 original.Set("key3", 3)
385 data, err := json.Marshal(original)
386 require.NoError(t, err)
388 restored := NewMap[string, int]()
389 err = json.Unmarshal(data, restored)
390 require.NoError(t, err)
392 require.Equal(t, original.Len(), restored.Len())
394 for k, v := range original.Seq2() {
395 restoredValue, ok := restored.Get(k)
396 require.True(t, ok)
397 require.Equal(t, v, restoredValue)
398 }
401func TestMap_ConcurrentAccess(t *testing.T) {
402 t.Parallel()
404 m := NewMap[int, int]()
405 const numGoroutines = 100
406 const numOperations = 100
408 var wg sync.WaitGroup
409 wg.Add(numGoroutines)
411 for i := range numGoroutines {
412 go func(id int) {
413 defer wg.Done()
414 for j := range numOperations {
415 key := id*numOperations + j
416 m.Set(key, key*2)
417 value, ok := m.Get(key)
418 require.True(t, ok)
419 require.Equal(t, key*2, value)
420 }
421 }(i)
422 }
424 wg.Wait()
426 require.Equal(t, numGoroutines*numOperations, m.Len())
429func TestMap_ConcurrentReadWrite(t *testing.T) {
430 t.Parallel()
432 m := NewMap[int, int]()
433 const numReaders = 50
434 const numWriters = 50
435 const numOperations = 100
437 for i := range 1000 {
438 m.Set(i, i)
439 }
441 var wg sync.WaitGroup
442 wg.Add(numReaders + numWriters)
444 for range numReaders {
445 go func() {
446 defer wg.Done()
447 for j := range numOperations {
448 key := j % 1000
449 value, ok := m.Get(key)
450 if ok {
451 require.Equal(t, key, value)
452 }
453 _ = m.Len()
454 }
455 }()
456 }
458 for i := range numWriters {
459 go func(id int) {
460 defer wg.Done()
461 for j := range numOperations {
462 key := 1000 + id*numOperations + j
463 m.Set(key, key)
464 if j%10 == 0 {
465 m.Del(key)
466 }
467 }
468 }(i)
469 }
471 wg.Wait()
474func TestMap_ConcurrentSeq2(t *testing.T) {
475 t.Parallel()
477 m := NewMap[int, int]()
478 for i := range 100 {
479 m.Set(i, i*2)
480 }
482 var wg sync.WaitGroup
483 const numIterators = 10
485 wg.Add(numIterators)
486 for range numIterators {
487 go func() {
488 defer wg.Done()
489 count := 0
490 for k, v := range m.Seq2() {
491 require.Equal(t, k*2, v)
492 count++
493 }
494 require.Equal(t, 100, count)
495 }()
496 }
498 wg.Wait()
501func TestMap_ConcurrentSeq(t *testing.T) {
502 t.Parallel()
504 m := NewMap[int, int]()
505 for i := range 100 {
506 m.Set(i, i*2)
507 }
509 var wg sync.WaitGroup
510 const numIterators = 10
512 wg.Add(numIterators)
513 for range numIterators {
514 go func() {
515 defer wg.Done()
516 count := 0
517 values := make(map[int]bool)
518 for v := range m.Seq() {
519 values[v] = true
520 count++
521 }
522 require.Equal(t, 100, count)
523 for i := range 100 {
524 require.True(t, values[i*2])
525 }
526 }()
527 }
529 wg.Wait()
532func TestMap_ConcurrentTake(t *testing.T) {
533 t.Parallel()
535 m := NewMap[int, int]()
536 const numItems = 1000
538 for i := range numItems {
539 m.Set(i, i*2)
540 }
542 var wg sync.WaitGroup
543 const numWorkers = 10
544 taken := make([][]int, numWorkers)
546 wg.Add(numWorkers)
547 for i := range numWorkers {
548 go func(workerID int) {
549 defer wg.Done()
550 taken[workerID] = make([]int, 0)
551 for j := workerID; j < numItems; j += numWorkers {
552 if value, ok := m.Take(j); ok {
553 taken[workerID] = append(taken[workerID], value)
554 }
555 }
556 }(i)
557 }
559 wg.Wait()
561 require.Equal(t, 0, m.Len())
563 allTaken := make(map[int]bool)
564 for _, workerTaken := range taken {
565 for _, value := range workerTaken {
566 require.False(t, allTaken[value], "Value %d was taken multiple times", value)
567 allTaken[value] = true
568 }
569 }
571 require.Equal(t, numItems, len(allTaken))
572 for i := range numItems {
573 require.True(t, allTaken[i*2], "Expected value %d to be taken", i*2)
574 }
577func TestMap_TypeSafety(t *testing.T) {
578 t.Parallel()
580 stringIntMap := NewMap[string, int]()
581 stringIntMap.Set("key", 42)
582 value, ok := stringIntMap.Get("key")
583 require.True(t, ok)
584 require.Equal(t, 42, value)
586 intStringMap := NewMap[int, string]()
587 intStringMap.Set(42, "value")
588 strValue, ok := intStringMap.Get(42)
589 require.True(t, ok)
590 require.Equal(t, "value", strValue)
592 structMap := NewMap[string, struct{ Name string }]()
593 structMap.Set("key", struct{ Name string }{Name: "test"})
594 structValue, ok := structMap.Get("key")
595 require.True(t, ok)
596 require.Equal(t, "test", structValue.Name)
599func TestMap_InterfaceCompliance(t *testing.T) {
600 t.Parallel()
602 var _ json.Marshaler = &Map[string, any]{}
603 var _ json.Unmarshaler = &Map[string, any]{}
606func BenchmarkMap_Set(b *testing.B) {
607 m := NewMap[int, int]()
609 for i := 0; b.Loop(); i++ {
610 m.Set(i, i*2)
611 }
614func BenchmarkMap_Get(b *testing.B) {
615 m := NewMap[int, int]()
616 for i := range 1000 {
617 m.Set(i, i*2)
618 }
620 for i := 0; b.Loop(); i++ {
621 m.Get(i % 1000)
622 }
625func BenchmarkMap_Seq2(b *testing.B) {
626 m := NewMap[int, int]()
627 for i := range 1000 {
628 m.Set(i, i*2)
629 }
631 for b.Loop() {
632 for range m.Seq2() {
633 }
634 }
637func BenchmarkMap_Seq(b *testing.B) {
638 m := NewMap[int, int]()
639 for i := range 1000 {
640 m.Set(i, i*2)
641 }
643 for b.Loop() {
644 for range m.Seq() {
645 }
646 }
649func BenchmarkMap_Take(b *testing.B) {
650 m := NewMap[int, int]()
651 for i := range 1000 {
652 m.Set(i, i*2)
653 }
655 b.ResetTimer()
656 for i := 0; b.Loop(); i++ {
657 key := i % 1000
658 m.Take(key)
659 if i%1000 == 999 {
660 b.StopTimer()
661 for j := range 1000 {
662 m.Set(j, j*2)
663 }
664 b.StartTimer()
665 }
666 }
669func BenchmarkMap_ConcurrentReadWrite(b *testing.B) {
670 m := NewMap[int, int]()
671 for i := range 1000 {
672 m.Set(i, i*2)
673 }
675 b.ResetTimer()
676 b.RunParallel(func(pb *testing.PB) {
677 i := 0
678 for pb.Next() {
679 if i%2 == 0 {
680 m.Get(i % 1000)
681 } else {
682 m.Set(i+1000, i*2)
683 }
684 i++
685 }
686 })