git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/concurrentmap/concurrent_map_test.go (about) 1 package concurrentmap 2 3 import ( 4 "encoding/json" 5 "hash/fnv" 6 "sort" 7 "strconv" 8 "testing" 9 ) 10 11 type Animal struct { 12 name string 13 } 14 15 func TestMapCreation(t *testing.T) { 16 m := New[string]() 17 if m.shards == nil { 18 t.Error("map is null.") 19 } 20 21 if m.Count() != 0 { 22 t.Error("new map should be empty.") 23 } 24 } 25 26 func TestInsert(t *testing.T) { 27 m := New[Animal]() 28 elephant := Animal{"elephant"} 29 monkey := Animal{"monkey"} 30 31 m.Set("elephant", elephant) 32 m.Set("monkey", monkey) 33 34 if m.Count() != 2 { 35 t.Error("map should contain exactly two elements.") 36 } 37 } 38 39 func TestInsertAbsent(t *testing.T) { 40 m := New[Animal]() 41 elephant := Animal{"elephant"} 42 monkey := Animal{"monkey"} 43 44 m.SetIfAbsent("elephant", elephant) 45 if ok := m.SetIfAbsent("elephant", monkey); ok { 46 t.Error("map set a new value even the entry is already present") 47 } 48 } 49 50 func TestGet(t *testing.T) { 51 m := New[Animal]() 52 53 // Get a missing element. 54 val, ok := m.Get("Money") 55 56 if ok == true { 57 t.Error("ok should be false when item is missing from map.") 58 } 59 60 if (val != Animal{}) { 61 t.Error("Missing values should return as null.") 62 } 63 64 elephant := Animal{"elephant"} 65 m.Set("elephant", elephant) 66 67 // Retrieve inserted element. 68 elephant, ok = m.Get("elephant") 69 if ok == false { 70 t.Error("ok should be true for item stored within the map.") 71 } 72 73 if elephant.name != "elephant" { 74 t.Error("item was modified.") 75 } 76 } 77 78 func TestHas(t *testing.T) { 79 m := New[Animal]() 80 81 // Get a missing element. 82 if m.Has("Money") == true { 83 t.Error("element shouldn't exists") 84 } 85 86 elephant := Animal{"elephant"} 87 m.Set("elephant", elephant) 88 89 if m.Has("elephant") == false { 90 t.Error("element exists, expecting Has to return True.") 91 } 92 } 93 94 func TestRemove(t *testing.T) { 95 m := New[Animal]() 96 97 monkey := Animal{"monkey"} 98 m.Set("monkey", monkey) 99 100 m.Remove("monkey") 101 102 if m.Count() != 0 { 103 t.Error("Expecting count to be zero once item was removed.") 104 } 105 106 temp, ok := m.Get("monkey") 107 108 if ok != false { 109 t.Error("Expecting ok to be false for missing items.") 110 } 111 112 if (temp != Animal{}) { 113 t.Error("Expecting item to be nil after its removal.") 114 } 115 116 // Remove a none existing element. 117 m.Remove("noone") 118 } 119 120 func TestRemoveCb(t *testing.T) { 121 m := New[Animal]() 122 123 monkey := Animal{"monkey"} 124 m.Set("monkey", monkey) 125 elephant := Animal{"elephant"} 126 m.Set("elephant", elephant) 127 128 var ( 129 mapKey string 130 mapVal Animal 131 wasFound bool 132 ) 133 cb := func(key string, val Animal, exists bool) bool { 134 mapKey = key 135 mapVal = val 136 wasFound = exists 137 138 return val.name == "monkey" 139 } 140 141 // Monkey should be removed 142 result := m.RemoveCb("monkey", cb) 143 if !result { 144 t.Errorf("Result was not true") 145 } 146 147 if mapKey != "monkey" { 148 t.Error("Wrong key was provided to the callback") 149 } 150 151 if mapVal != monkey { 152 t.Errorf("Wrong value was provided to the value") 153 } 154 155 if !wasFound { 156 t.Errorf("Key was not found") 157 } 158 159 if m.Has("monkey") { 160 t.Errorf("Key was not removed") 161 } 162 163 // Elephant should not be removed 164 result = m.RemoveCb("elephant", cb) 165 if result { 166 t.Errorf("Result was true") 167 } 168 169 if mapKey != "elephant" { 170 t.Error("Wrong key was provided to the callback") 171 } 172 173 if mapVal != elephant { 174 t.Errorf("Wrong value was provided to the value") 175 } 176 177 if !wasFound { 178 t.Errorf("Key was not found") 179 } 180 181 if !m.Has("elephant") { 182 t.Errorf("Key was removed") 183 } 184 185 // Unset key should remain unset 186 result = m.RemoveCb("horse", cb) 187 if result { 188 t.Errorf("Result was true") 189 } 190 191 if mapKey != "horse" { 192 t.Error("Wrong key was provided to the callback") 193 } 194 195 if (mapVal != Animal{}) { 196 t.Errorf("Wrong value was provided to the value") 197 } 198 199 if wasFound { 200 t.Errorf("Key was found") 201 } 202 203 if m.Has("horse") { 204 t.Errorf("Key was created") 205 } 206 } 207 208 func TestPop(t *testing.T) { 209 m := New[Animal]() 210 211 monkey := Animal{"monkey"} 212 m.Set("monkey", monkey) 213 214 v, exists := m.Pop("monkey") 215 216 if !exists || v != monkey { 217 t.Error("Pop didn't find a monkey.") 218 } 219 220 v2, exists2 := m.Pop("monkey") 221 222 if exists2 || v2 == monkey { 223 t.Error("Pop keeps finding monkey") 224 } 225 226 if m.Count() != 0 { 227 t.Error("Expecting count to be zero once item was Pop'ed.") 228 } 229 230 temp, ok := m.Get("monkey") 231 232 if ok != false { 233 t.Error("Expecting ok to be false for missing items.") 234 } 235 236 if (temp != Animal{}) { 237 t.Error("Expecting item to be nil after its removal.") 238 } 239 } 240 241 func TestCount(t *testing.T) { 242 m := New[Animal]() 243 for i := 0; i < 100; i++ { 244 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 245 } 246 247 if m.Count() != 100 { 248 t.Error("Expecting 100 element within map.") 249 } 250 } 251 252 func TestIsEmpty(t *testing.T) { 253 m := New[Animal]() 254 255 if m.IsEmpty() == false { 256 t.Error("new map should be empty") 257 } 258 259 m.Set("elephant", Animal{"elephant"}) 260 261 if m.IsEmpty() != false { 262 t.Error("map shouldn't be empty.") 263 } 264 } 265 266 func TestIterator(t *testing.T) { 267 m := New[Animal]() 268 269 // Insert 100 elements. 270 for i := 0; i < 100; i++ { 271 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 272 } 273 274 counter := 0 275 // Iterate over elements. 276 for item := range m.Iter() { 277 val := item.Val 278 279 if (val == Animal{}) { 280 t.Error("Expecting an object.") 281 } 282 counter++ 283 } 284 285 if counter != 100 { 286 t.Error("We should have counted 100 elements.") 287 } 288 } 289 290 func TestBufferedIterator(t *testing.T) { 291 m := New[Animal]() 292 293 // Insert 100 elements. 294 for i := 0; i < 100; i++ { 295 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 296 } 297 298 counter := 0 299 // Iterate over elements. 300 for item := range m.IterBuffered() { 301 val := item.Val 302 303 if (val == Animal{}) { 304 t.Error("Expecting an object.") 305 } 306 counter++ 307 } 308 309 if counter != 100 { 310 t.Error("We should have counted 100 elements.") 311 } 312 } 313 314 func TestClear(t *testing.T) { 315 m := New[Animal]() 316 317 // Insert 100 elements. 318 for i := 0; i < 100; i++ { 319 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 320 } 321 322 m.Clear() 323 324 if m.Count() != 0 { 325 t.Error("We should have 0 elements.") 326 } 327 } 328 329 func TestIterCb(t *testing.T) { 330 m := New[Animal]() 331 332 // Insert 100 elements. 333 for i := 0; i < 100; i++ { 334 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 335 } 336 337 counter := 0 338 // Iterate over elements. 339 m.IterCb(func(key string, v Animal) { 340 counter++ 341 }) 342 if counter != 100 { 343 t.Error("We should have counted 100 elements.") 344 } 345 } 346 347 func TestItems(t *testing.T) { 348 m := New[Animal]() 349 350 // Insert 100 elements. 351 for i := 0; i < 100; i++ { 352 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 353 } 354 355 items := m.Items() 356 357 if len(items) != 100 { 358 t.Error("We should have counted 100 elements.") 359 } 360 } 361 362 func TestConcurrent(t *testing.T) { 363 m := New[int]() 364 ch := make(chan int) 365 const iterations = 1000 366 var a [iterations]int 367 368 // Using go routines insert 1000 ints into our map. 369 go func() { 370 for i := 0; i < iterations/2; i++ { 371 // Add item to map. 372 m.Set(strconv.Itoa(i), i) 373 374 // Retrieve item from map. 375 val, _ := m.Get(strconv.Itoa(i)) 376 377 // Write to channel inserted value. 378 ch <- val 379 } // Call go routine with current index. 380 }() 381 382 go func() { 383 for i := iterations / 2; i < iterations; i++ { 384 // Add item to map. 385 m.Set(strconv.Itoa(i), i) 386 387 // Retrieve item from map. 388 val, _ := m.Get(strconv.Itoa(i)) 389 390 // Write to channel inserted value. 391 ch <- val 392 } // Call go routine with current index. 393 }() 394 395 // Wait for all go routines to finish. 396 counter := 0 397 for elem := range ch { 398 a[counter] = elem 399 counter++ 400 if counter == iterations { 401 break 402 } 403 } 404 405 // Sorts array, will make is simpler to verify all inserted values we're returned. 406 sort.Ints(a[0:iterations]) 407 408 // Make sure map contains 1000 elements. 409 if m.Count() != iterations { 410 t.Error("Expecting 1000 elements.") 411 } 412 413 // Make sure all inserted values we're fetched from map. 414 for i := 0; i < iterations; i++ { 415 if i != a[i] { 416 t.Error("missing value", i) 417 } 418 } 419 } 420 421 func TestJsonMarshal(t *testing.T) { 422 SHARD_COUNT = 2 423 defer func() { 424 SHARD_COUNT = 32 425 }() 426 expected := "{\"a\":1,\"b\":2}" 427 m := New[int]() 428 m.Set("a", 1) 429 m.Set("b", 2) 430 j, err := json.Marshal(m) 431 if err != nil { 432 t.Error(err) 433 } 434 435 if string(j) != expected { 436 t.Error("json", string(j), "differ from expected", expected) 437 return 438 } 439 } 440 441 func TestKeys(t *testing.T) { 442 m := New[Animal]() 443 444 // Insert 100 elements. 445 for i := 0; i < 100; i++ { 446 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 447 } 448 449 keys := m.Keys() 450 if len(keys) != 100 { 451 t.Error("We should have counted 100 elements.") 452 } 453 } 454 455 func TestMInsert(t *testing.T) { 456 animals := map[string]Animal{ 457 "elephant": {"elephant"}, 458 "monkey": {"monkey"}, 459 } 460 m := New[Animal]() 461 m.MSet(animals) 462 463 if m.Count() != 2 { 464 t.Error("map should contain exactly two elements.") 465 } 466 } 467 468 func TestFnv32(t *testing.T) { 469 key := []byte("ABC") 470 471 hasher := fnv.New32() 472 _, err := hasher.Write(key) 473 if err != nil { 474 t.Errorf(err.Error()) 475 } 476 if fnv32(string(key)) != hasher.Sum32() { 477 t.Errorf("Bundled fnv32 produced %d, expected result from hash/fnv32 is %d", fnv32(string(key)), hasher.Sum32()) 478 } 479 480 } 481 482 func TestUpsert(t *testing.T) { 483 dolphin := Animal{"dolphin"} 484 whale := Animal{"whale"} 485 tiger := Animal{"tiger"} 486 lion := Animal{"lion"} 487 488 cb := func(exists bool, valueInMap Animal, newValue Animal) Animal { 489 if !exists { 490 return newValue 491 } 492 valueInMap.name += newValue.name 493 return valueInMap 494 } 495 496 m := New[Animal]() 497 m.Set("marine", dolphin) 498 m.Upsert("marine", whale, cb) 499 m.Upsert("predator", tiger, cb) 500 m.Upsert("predator", lion, cb) 501 502 if m.Count() != 2 { 503 t.Error("map should contain exactly two elements.") 504 } 505 506 marineAnimals, ok := m.Get("marine") 507 if marineAnimals.name != "dolphinwhale" || !ok { 508 t.Error("Set, then Upsert failed") 509 } 510 511 predators, ok := m.Get("predator") 512 if !ok || predators.name != "tigerlion" { 513 t.Error("Upsert, then Upsert failed") 514 } 515 } 516 517 func TestKeysWhenRemoving(t *testing.T) { 518 m := New[Animal]() 519 520 // Insert 100 elements. 521 Total := 100 522 for i := 0; i < Total; i++ { 523 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 524 } 525 526 // Remove 10 elements concurrently. 527 Num := 10 528 for i := 0; i < Num; i++ { 529 go func(c *ConcurrentMap[string, Animal], n int) { 530 c.Remove(strconv.Itoa(n)) 531 }(&m, i) 532 } 533 keys := m.Keys() 534 for _, k := range keys { 535 if k == "" { 536 t.Error("Empty keys returned") 537 } 538 } 539 } 540 541 func TestUnDrainedIter(t *testing.T) { 542 m := New[Animal]() 543 // Insert 100 elements. 544 Total := 100 545 for i := 0; i < Total; i++ { 546 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 547 } 548 counter := 0 549 // Iterate over elements. 550 ch := m.Iter() 551 for item := range ch { 552 val := item.Val 553 554 if (val == Animal{}) { 555 t.Error("Expecting an object.") 556 } 557 counter++ 558 if counter == 42 { 559 break 560 } 561 } 562 for i := Total; i < 2*Total; i++ { 563 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 564 } 565 for item := range ch { 566 val := item.Val 567 568 if (val == Animal{}) { 569 t.Error("Expecting an object.") 570 } 571 counter++ 572 } 573 574 if counter != 100 { 575 t.Error("We should have been right where we stopped") 576 } 577 578 counter = 0 579 for item := range m.IterBuffered() { 580 val := item.Val 581 582 if (val == Animal{}) { 583 t.Error("Expecting an object.") 584 } 585 counter++ 586 } 587 588 if counter != 200 { 589 t.Error("We should have counted 200 elements.") 590 } 591 } 592 593 func TestUnDrainedIterBuffered(t *testing.T) { 594 m := New[Animal]() 595 // Insert 100 elements. 596 Total := 100 597 for i := 0; i < Total; i++ { 598 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 599 } 600 counter := 0 601 // Iterate over elements. 602 ch := m.IterBuffered() 603 for item := range ch { 604 val := item.Val 605 606 if (val == Animal{}) { 607 t.Error("Expecting an object.") 608 } 609 counter++ 610 if counter == 42 { 611 break 612 } 613 } 614 for i := Total; i < 2*Total; i++ { 615 m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) 616 } 617 for item := range ch { 618 val := item.Val 619 620 if (val == Animal{}) { 621 t.Error("Expecting an object.") 622 } 623 counter++ 624 } 625 626 if counter != 100 { 627 t.Error("We should have been right where we stopped") 628 } 629 630 counter = 0 631 for item := range m.IterBuffered() { 632 val := item.Val 633 634 if (val == Animal{}) { 635 t.Error("Expecting an object.") 636 } 637 counter++ 638 } 639 640 if counter != 200 { 641 t.Error("We should have counted 200 elements.") 642 } 643 }