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 var result map[string]int
172 err = json.Unmarshal(data, &result)
173 assert.NoError(t, err)
174 assert.Equal(t, 2, len(result))
175 assert.Equal(t, 1, result["key1"])
176 assert.Equal(t, 2, result["key2"])
177}
178
179func TestMap_MarshalJSON_EmptyMap(t *testing.T) {
180 t.Parallel()
181
182 m := NewMap[string, int]()
183
184 data, err := json.Marshal(m)
185 assert.NoError(t, err)
186 assert.Equal(t, "{}", string(data))
187}
188
189func TestMap_UnmarshalJSON(t *testing.T) {
190 t.Parallel()
191
192 jsonData := `{"key1": 1, "key2": 2}`
193
194 m := NewMap[string, int]()
195 err := json.Unmarshal([]byte(jsonData), m)
196 assert.NoError(t, err)
197
198 assert.Equal(t, 2, m.Len())
199 value, ok := m.Get("key1")
200 assert.True(t, ok)
201 assert.Equal(t, 1, value)
202
203 value, ok = m.Get("key2")
204 assert.True(t, ok)
205 assert.Equal(t, 2, value)
206}
207
208func TestMap_UnmarshalJSON_EmptyJSON(t *testing.T) {
209 t.Parallel()
210
211 jsonData := `{}`
212
213 m := NewMap[string, int]()
214 err := json.Unmarshal([]byte(jsonData), m)
215 assert.NoError(t, err)
216 assert.Equal(t, 0, m.Len())
217}
218
219func TestMap_UnmarshalJSON_InvalidJSON(t *testing.T) {
220 t.Parallel()
221
222 jsonData := `{"key1": 1, "key2":}`
223
224 m := NewMap[string, int]()
225 err := json.Unmarshal([]byte(jsonData), m)
226 assert.Error(t, err)
227}
228
229func TestMap_UnmarshalJSON_OverwritesExistingData(t *testing.T) {
230 t.Parallel()
231
232 m := NewMap[string, int]()
233 m.Set("existing", 999)
234
235 jsonData := `{"key1": 1, "key2": 2}`
236 err := json.Unmarshal([]byte(jsonData), m)
237 assert.NoError(t, err)
238
239 assert.Equal(t, 2, m.Len())
240 _, ok := m.Get("existing")
241 assert.False(t, ok)
242
243 value, ok := m.Get("key1")
244 assert.True(t, ok)
245 assert.Equal(t, 1, value)
246}
247
248func TestMap_JSONRoundTrip(t *testing.T) {
249 t.Parallel()
250
251 original := NewMap[string, int]()
252 original.Set("key1", 1)
253 original.Set("key2", 2)
254 original.Set("key3", 3)
255
256 data, err := json.Marshal(original)
257 assert.NoError(t, err)
258
259 restored := NewMap[string, int]()
260 err = json.Unmarshal(data, restored)
261 assert.NoError(t, err)
262
263 assert.Equal(t, original.Len(), restored.Len())
264
265 for k, v := range original.Seq2() {
266 restoredValue, ok := restored.Get(k)
267 assert.True(t, ok)
268 assert.Equal(t, v, restoredValue)
269 }
270}
271
272func TestMap_ConcurrentAccess(t *testing.T) {
273 t.Parallel()
274
275 m := NewMap[int, int]()
276 const numGoroutines = 100
277 const numOperations = 100
278
279 var wg sync.WaitGroup
280 wg.Add(numGoroutines)
281
282 for i := range numGoroutines {
283 go func(id int) {
284 defer wg.Done()
285 for j := range numOperations {
286 key := id*numOperations + j
287 m.Set(key, key*2)
288 value, ok := m.Get(key)
289 assert.True(t, ok)
290 assert.Equal(t, key*2, value)
291 }
292 }(i)
293 }
294
295 wg.Wait()
296
297 assert.Equal(t, numGoroutines*numOperations, m.Len())
298}
299
300func TestMap_ConcurrentReadWrite(t *testing.T) {
301 t.Parallel()
302
303 m := NewMap[int, int]()
304 const numReaders = 50
305 const numWriters = 50
306 const numOperations = 100
307
308 for i := range 1000 {
309 m.Set(i, i)
310 }
311
312 var wg sync.WaitGroup
313 wg.Add(numReaders + numWriters)
314
315 for range numReaders {
316 go func() {
317 defer wg.Done()
318 for j := range numOperations {
319 key := j % 1000
320 value, ok := m.Get(key)
321 if ok {
322 assert.Equal(t, key, value)
323 }
324 _ = m.Len()
325 }
326 }()
327 }
328
329 for i := range numWriters {
330 go func(id int) {
331 defer wg.Done()
332 for j := range numOperations {
333 key := 1000 + id*numOperations + j
334 m.Set(key, key)
335 if j%10 == 0 {
336 m.Del(key)
337 }
338 }
339 }(i)
340 }
341
342 wg.Wait()
343}
344
345func TestMap_ConcurrentSeq2(t *testing.T) {
346 t.Parallel()
347
348 m := NewMap[int, int]()
349 for i := range 100 {
350 m.Set(i, i*2)
351 }
352
353 var wg sync.WaitGroup
354 const numIterators = 10
355
356 wg.Add(numIterators)
357 for range numIterators {
358 go func() {
359 defer wg.Done()
360 count := 0
361 for k, v := range m.Seq2() {
362 assert.Equal(t, k*2, v)
363 count++
364 }
365 assert.Equal(t, 100, count)
366 }()
367 }
368
369 wg.Wait()
370}
371
372func TestMap_TypeSafety(t *testing.T) {
373 t.Parallel()
374
375 stringIntMap := NewMap[string, int]()
376 stringIntMap.Set("key", 42)
377 value, ok := stringIntMap.Get("key")
378 assert.True(t, ok)
379 assert.Equal(t, 42, value)
380
381 intStringMap := NewMap[int, string]()
382 intStringMap.Set(42, "value")
383 strValue, ok := intStringMap.Get(42)
384 assert.True(t, ok)
385 assert.Equal(t, "value", strValue)
386
387 structMap := NewMap[string, struct{ Name string }]()
388 structMap.Set("key", struct{ Name string }{Name: "test"})
389 structValue, ok := structMap.Get("key")
390 assert.True(t, ok)
391 assert.Equal(t, "test", structValue.Name)
392}
393
394func TestMap_InterfaceCompliance(t *testing.T) {
395 t.Parallel()
396
397 var _ json.Marshaler = &Map[string, any]{}
398 var _ json.Unmarshaler = &Map[string, any]{}
399}
400
401func BenchmarkMap_Set(b *testing.B) {
402 m := NewMap[int, int]()
403
404 for i := 0; b.Loop(); i++ {
405 m.Set(i, i*2)
406 }
407}
408
409func BenchmarkMap_Get(b *testing.B) {
410 m := NewMap[int, int]()
411 for i := range 1000 {
412 m.Set(i, i*2)
413 }
414
415 for i := 0; b.Loop(); i++ {
416 m.Get(i % 1000)
417 }
418}
419
420func BenchmarkMap_Seq2(b *testing.B) {
421 m := NewMap[int, int]()
422 for i := range 1000 {
423 m.Set(i, i*2)
424 }
425
426 for b.Loop() {
427 for range m.Seq2() {
428 }
429 }
430}
431
432func BenchmarkMap_ConcurrentReadWrite(b *testing.B) {
433 m := NewMap[int, int]()
434 for i := range 1000 {
435 m.Set(i, i*2)
436 }
437
438 b.ResetTimer()
439 b.RunParallel(func(pb *testing.PB) {
440 i := 0
441 for pb.Next() {
442 if i%2 == 0 {
443 m.Get(i % 1000)
444 } else {
445 m.Set(i+1000, i*2)
446 }
447 i++
448 }
449 })
450}