1package csync
2
3import (
4 "encoding/json"
5 "maps"
6 "sync"
7 "testing"
8
9 "github.com/stretchr/testify/assert"
10)
11
12func TestNewMap(t *testing.T) {
13 t.Parallel()
14
15 m := NewMap[string, int]()
16 assert.NotNil(t, m)
17 assert.NotNil(t, m.inner)
18 assert.Equal(t, 0, m.Len())
19}
20
21func TestNewMapFrom(t *testing.T) {
22 t.Parallel()
23
24 original := map[string]int{
25 "key1": 1,
26 "key2": 2,
27 }
28
29 m := NewMapFrom(original)
30 assert.NotNil(t, m)
31 assert.Equal(t, original, m.inner)
32 assert.Equal(t, 2, m.Len())
33
34 value, ok := m.Get("key1")
35 assert.True(t, ok)
36 assert.Equal(t, 1, value)
37}
38
39func TestMap_Set(t *testing.T) {
40 t.Parallel()
41
42 m := NewMap[string, int]()
43
44 m.Set("key1", 42)
45 value, ok := m.Get("key1")
46 assert.True(t, ok)
47 assert.Equal(t, 42, value)
48 assert.Equal(t, 1, m.Len())
49
50 m.Set("key1", 100)
51 value, ok = m.Get("key1")
52 assert.True(t, ok)
53 assert.Equal(t, 100, value)
54 assert.Equal(t, 1, m.Len())
55}
56
57func TestMap_Get(t *testing.T) {
58 t.Parallel()
59
60 m := NewMap[string, int]()
61
62 value, ok := m.Get("nonexistent")
63 assert.False(t, ok)
64 assert.Equal(t, 0, value)
65
66 m.Set("key1", 42)
67 value, ok = m.Get("key1")
68 assert.True(t, ok)
69 assert.Equal(t, 42, value)
70}
71
72func TestMap_Del(t *testing.T) {
73 t.Parallel()
74
75 m := NewMap[string, int]()
76 m.Set("key1", 42)
77 m.Set("key2", 100)
78
79 assert.Equal(t, 2, m.Len())
80
81 m.Del("key1")
82 _, ok := m.Get("key1")
83 assert.False(t, ok)
84 assert.Equal(t, 1, m.Len())
85
86 value, ok := m.Get("key2")
87 assert.True(t, ok)
88 assert.Equal(t, 100, value)
89
90 m.Del("nonexistent")
91 assert.Equal(t, 1, m.Len())
92}
93
94func TestMap_Len(t *testing.T) {
95 t.Parallel()
96
97 m := NewMap[string, int]()
98 assert.Equal(t, 0, m.Len())
99
100 m.Set("key1", 1)
101 assert.Equal(t, 1, m.Len())
102
103 m.Set("key2", 2)
104 assert.Equal(t, 2, m.Len())
105
106 m.Del("key1")
107 assert.Equal(t, 1, m.Len())
108
109 m.Del("key2")
110 assert.Equal(t, 0, m.Len())
111}
112
113func TestMap_Seq2(t *testing.T) {
114 t.Parallel()
115
116 m := NewMap[string, int]()
117 m.Set("key1", 1)
118 m.Set("key2", 2)
119 m.Set("key3", 3)
120
121 collected := maps.Collect(m.Seq2())
122
123 assert.Equal(t, 3, len(collected))
124 assert.Equal(t, 1, collected["key1"])
125 assert.Equal(t, 2, collected["key2"])
126 assert.Equal(t, 3, collected["key3"])
127}
128
129func TestMap_Seq2_EarlyReturn(t *testing.T) {
130 t.Parallel()
131
132 m := NewMap[string, int]()
133 m.Set("key1", 1)
134 m.Set("key2", 2)
135 m.Set("key3", 3)
136
137 count := 0
138 for range m.Seq2() {
139 count++
140 if count == 2 {
141 break
142 }
143 }
144
145 assert.Equal(t, 2, count)
146}
147
148func TestMap_Seq2_EmptyMap(t *testing.T) {
149 t.Parallel()
150
151 m := NewMap[string, int]()
152
153 count := 0
154 for range m.Seq2() {
155 count++
156 }
157
158 assert.Equal(t, 0, count)
159}
160
161func TestMap_MarshalJSON(t *testing.T) {
162 t.Parallel()
163
164 m := NewMap[string, int]()
165 m.Set("key1", 1)
166 m.Set("key2", 2)
167
168 data, err := json.Marshal(m)
169 assert.NoError(t, err)
170
171 result := &Map[string, int]{}
172 err = json.Unmarshal(data, result)
173 assert.NoError(t, err)
174 assert.Equal(t, 2, result.Len())
175 v1, _ := result.Get("key1")
176 v2, _ := result.Get("key2")
177 assert.Equal(t, 1, v1)
178 assert.Equal(t, 2, v2)
179}
180
181func TestMap_MarshalJSON_EmptyMap(t *testing.T) {
182 t.Parallel()
183
184 m := NewMap[string, int]()
185
186 data, err := json.Marshal(m)
187 assert.NoError(t, err)
188 assert.Equal(t, "{}", string(data))
189}
190
191func TestMap_UnmarshalJSON(t *testing.T) {
192 t.Parallel()
193
194 jsonData := `{"key1": 1, "key2": 2}`
195
196 m := NewMap[string, int]()
197 err := json.Unmarshal([]byte(jsonData), m)
198 assert.NoError(t, err)
199
200 assert.Equal(t, 2, m.Len())
201 value, ok := m.Get("key1")
202 assert.True(t, ok)
203 assert.Equal(t, 1, value)
204
205 value, ok = m.Get("key2")
206 assert.True(t, ok)
207 assert.Equal(t, 2, value)
208}
209
210func TestMap_UnmarshalJSON_EmptyJSON(t *testing.T) {
211 t.Parallel()
212
213 jsonData := `{}`
214
215 m := NewMap[string, int]()
216 err := json.Unmarshal([]byte(jsonData), m)
217 assert.NoError(t, err)
218 assert.Equal(t, 0, m.Len())
219}
220
221func TestMap_UnmarshalJSON_InvalidJSON(t *testing.T) {
222 t.Parallel()
223
224 jsonData := `{"key1": 1, "key2":}`
225
226 m := NewMap[string, int]()
227 err := json.Unmarshal([]byte(jsonData), m)
228 assert.Error(t, err)
229}
230
231func TestMap_UnmarshalJSON_OverwritesExistingData(t *testing.T) {
232 t.Parallel()
233
234 m := NewMap[string, int]()
235 m.Set("existing", 999)
236
237 jsonData := `{"key1": 1, "key2": 2}`
238 err := json.Unmarshal([]byte(jsonData), m)
239 assert.NoError(t, err)
240
241 assert.Equal(t, 2, m.Len())
242 _, ok := m.Get("existing")
243 assert.False(t, ok)
244
245 value, ok := m.Get("key1")
246 assert.True(t, ok)
247 assert.Equal(t, 1, value)
248}
249
250func TestMap_JSONRoundTrip(t *testing.T) {
251 t.Parallel()
252
253 original := NewMap[string, int]()
254 original.Set("key1", 1)
255 original.Set("key2", 2)
256 original.Set("key3", 3)
257
258 data, err := json.Marshal(original)
259 assert.NoError(t, err)
260
261 restored := NewMap[string, int]()
262 err = json.Unmarshal(data, restored)
263 assert.NoError(t, err)
264
265 assert.Equal(t, original.Len(), restored.Len())
266
267 for k, v := range original.Seq2() {
268 restoredValue, ok := restored.Get(k)
269 assert.True(t, ok)
270 assert.Equal(t, v, restoredValue)
271 }
272}
273
274func TestMap_ConcurrentAccess(t *testing.T) {
275 t.Parallel()
276
277 m := NewMap[int, int]()
278 const numGoroutines = 100
279 const numOperations = 100
280
281 var wg sync.WaitGroup
282 wg.Add(numGoroutines)
283
284 for i := range numGoroutines {
285 go func(id int) {
286 defer wg.Done()
287 for j := range numOperations {
288 key := id*numOperations + j
289 m.Set(key, key*2)
290 value, ok := m.Get(key)
291 assert.True(t, ok)
292 assert.Equal(t, key*2, value)
293 }
294 }(i)
295 }
296
297 wg.Wait()
298
299 assert.Equal(t, numGoroutines*numOperations, m.Len())
300}
301
302func TestMap_ConcurrentReadWrite(t *testing.T) {
303 t.Parallel()
304
305 m := NewMap[int, int]()
306 const numReaders = 50
307 const numWriters = 50
308 const numOperations = 100
309
310 for i := range 1000 {
311 m.Set(i, i)
312 }
313
314 var wg sync.WaitGroup
315 wg.Add(numReaders + numWriters)
316
317 for range numReaders {
318 go func() {
319 defer wg.Done()
320 for j := range numOperations {
321 key := j % 1000
322 value, ok := m.Get(key)
323 if ok {
324 assert.Equal(t, key, value)
325 }
326 _ = m.Len()
327 }
328 }()
329 }
330
331 for i := range numWriters {
332 go func(id int) {
333 defer wg.Done()
334 for j := range numOperations {
335 key := 1000 + id*numOperations + j
336 m.Set(key, key)
337 if j%10 == 0 {
338 m.Del(key)
339 }
340 }
341 }(i)
342 }
343
344 wg.Wait()
345}
346
347func TestMap_ConcurrentSeq2(t *testing.T) {
348 t.Parallel()
349
350 m := NewMap[int, int]()
351 for i := range 100 {
352 m.Set(i, i*2)
353 }
354
355 var wg sync.WaitGroup
356 const numIterators = 10
357
358 wg.Add(numIterators)
359 for range numIterators {
360 go func() {
361 defer wg.Done()
362 count := 0
363 for k, v := range m.Seq2() {
364 assert.Equal(t, k*2, v)
365 count++
366 }
367 assert.Equal(t, 100, count)
368 }()
369 }
370
371 wg.Wait()
372}
373
374func TestMap_TypeSafety(t *testing.T) {
375 t.Parallel()
376
377 stringIntMap := NewMap[string, int]()
378 stringIntMap.Set("key", 42)
379 value, ok := stringIntMap.Get("key")
380 assert.True(t, ok)
381 assert.Equal(t, 42, value)
382
383 intStringMap := NewMap[int, string]()
384 intStringMap.Set(42, "value")
385 strValue, ok := intStringMap.Get(42)
386 assert.True(t, ok)
387 assert.Equal(t, "value", strValue)
388
389 structMap := NewMap[string, struct{ Name string }]()
390 structMap.Set("key", struct{ Name string }{Name: "test"})
391 structValue, ok := structMap.Get("key")
392 assert.True(t, ok)
393 assert.Equal(t, "test", structValue.Name)
394}
395
396func TestMap_InterfaceCompliance(t *testing.T) {
397 t.Parallel()
398
399 var _ json.Marshaler = &Map[string, any]{}
400 var _ json.Unmarshaler = &Map[string, any]{}
401}
402
403func BenchmarkMap_Set(b *testing.B) {
404 m := NewMap[int, int]()
405
406 for i := 0; b.Loop(); i++ {
407 m.Set(i, i*2)
408 }
409}
410
411func BenchmarkMap_Get(b *testing.B) {
412 m := NewMap[int, int]()
413 for i := range 1000 {
414 m.Set(i, i*2)
415 }
416
417 for i := 0; b.Loop(); i++ {
418 m.Get(i % 1000)
419 }
420}
421
422func BenchmarkMap_Seq2(b *testing.B) {
423 m := NewMap[int, int]()
424 for i := range 1000 {
425 m.Set(i, i*2)
426 }
427
428 for b.Loop() {
429 for range m.Seq2() {
430 }
431 }
432}
433
434func BenchmarkMap_ConcurrentReadWrite(b *testing.B) {
435 m := NewMap[int, int]()
436 for i := range 1000 {
437 m.Set(i, i*2)
438 }
439
440 b.ResetTimer()
441 b.RunParallel(func(pb *testing.PB) {
442 i := 0
443 for pb.Next() {
444 if i%2 == 0 {
445 m.Get(i % 1000)
446 } else {
447 m.Set(i+1000, i*2)
448 }
449 i++
450 }
451 })
452}