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