package cmap import ( "encoding/json" "hash/fnv" "sort" "strconv" "testing" ) type Animal struct { name string } func TestMapCreation(t *testing.T) { m := New() if m == nil { t.Error("map is null.") } if m.Count() != 0 { t.Error("new map should be empty.") } } func TestInsert(t *testing.T) { m := New() elephant := Animal{"elephant"} monkey := Animal{"monkey"} m.Set("elephant", elephant) m.Set("monkey", monkey) if m.Count() != 2 { t.Error("map should contain exactly two elements.") } } func TestInsertAbsent(t *testing.T) { m := New() elephant := Animal{"elephant"} monkey := Animal{"monkey"} m.SetIfAbsent("elephant", elephant) if ok := m.SetIfAbsent("elephant", monkey); ok { t.Error("map set a new value even the entry is already present") } } func TestGet(t *testing.T) { m := New() // Get a missing element. val, ok := m.Get("Money") if ok == true { t.Error("ok should be false when item is missing from map.") } if val != nil { t.Error("Missing values should return as null.") } elephant := Animal{"elephant"} m.Set("elephant", elephant) // Retrieve inserted element. tmp, ok := m.Get("elephant") elephant = tmp.(Animal) // Type assertion. if ok == false { t.Error("ok should be true for item stored within the map.") } if &elephant == nil { t.Error("expecting an element, not null.") } if elephant.name != "elephant" { t.Error("item was modified.") } } func TestHas(t *testing.T) { m := New() // Get a missing element. if m.Has("Money") == true { t.Error("element shouldn't exists") } elephant := Animal{"elephant"} m.Set("elephant", elephant) if m.Has("elephant") == false { t.Error("element exists, expecting Has to return True.") } } func TestRemove(t *testing.T) { m := New() monkey := Animal{"monkey"} m.Set("monkey", monkey) m.Remove("monkey") if m.Count() != 0 { t.Error("Expecting count to be zero once item was removed.") } temp, ok := m.Get("monkey") if ok != false { t.Error("Expecting ok to be false for missing items.") } if temp != nil { t.Error("Expecting item to be nil after its removal.") } // Remove a none existing element. m.Remove("noone") } func TestPop(t *testing.T) { m := New() monkey := Animal{"monkey"} m.Set("monkey", monkey) v, exists := m.Pop("monkey") if !exists { t.Error("Pop didn't find a monkey.") } m1, ok := v.(Animal) if !ok || m1 != monkey { t.Error("Pop found something else, but monkey.") } v2, exists2 := m.Pop("monkey") m1, ok = v2.(Animal) if exists2 || ok || m1 == monkey { t.Error("Pop keeps finding monkey") } if m.Count() != 0 { t.Error("Expecting count to be zero once item was Pop'ed.") } temp, ok := m.Get("monkey") if ok != false { t.Error("Expecting ok to be false for missing items.") } if temp != nil { t.Error("Expecting item to be nil after its removal.") } } func TestCount(t *testing.T) { m := New() for i := 0; i < 100; i++ { m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) } if m.Count() != 100 { t.Error("Expecting 100 element within map.") } } func TestIsEmpty(t *testing.T) { m := New() if m.IsEmpty() == false { t.Error("new map should be empty") } m.Set("elephant", Animal{"elephant"}) if m.IsEmpty() != false { t.Error("map shouldn't be empty.") } } func TestIterator(t *testing.T) { m := New() // Insert 100 elements. for i := 0; i < 100; i++ { m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) } counter := 0 // Iterate over elements. for item := range m.Iter() { val := item.Val if val == nil { t.Error("Expecting an object.") } counter++ } if counter != 100 { t.Error("We should have counted 100 elements.") } } func TestBufferedIterator(t *testing.T) { m := New() // Insert 100 elements. for i := 0; i < 100; i++ { m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) } counter := 0 // Iterate over elements. for item := range m.IterBuffered() { val := item.Val if val == nil { t.Error("Expecting an object.") } counter++ } if counter != 100 { t.Error("We should have counted 100 elements.") } } func TestIterCb(t *testing.T) { m := New() // Insert 100 elements. for i := 0; i < 100; i++ { m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) } counter := 0 // Iterate over elements. m.IterCb(func(key string, v interface{}) { _, ok := v.(Animal) if !ok { t.Error("Expecting an animal object") } counter++ }) if counter != 100 { t.Error("We should have counted 100 elements.") } } func TestItems(t *testing.T) { m := New() // Insert 100 elements. for i := 0; i < 100; i++ { m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) } items := m.Items() if len(items) != 100 { t.Error("We should have counted 100 elements.") } } func TestConcurrent(t *testing.T) { m := New() ch := make(chan int) const iterations = 1000 var a [iterations]int // Using go routines insert 1000 ints into our map. go func() { for i := 0; i < iterations/2; i++ { // Add item to map. m.Set(strconv.Itoa(i), i) // Retrieve item from map. val, _ := m.Get(strconv.Itoa(i)) // Write to channel inserted value. ch <- val.(int) } // Call go routine with current index. }() go func() { for i := iterations / 2; i < iterations; i++ { // Add item to map. m.Set(strconv.Itoa(i), i) // Retrieve item from map. val, _ := m.Get(strconv.Itoa(i)) // Write to channel inserted value. ch <- val.(int) } // Call go routine with current index. }() // Wait for all go routines to finish. counter := 0 for elem := range ch { a[counter] = elem counter++ if counter == iterations { break } } // Sorts array, will make is simpler to verify all inserted values we're returned. sort.Ints(a[0:iterations]) // Make sure map contains 1000 elements. if m.Count() != iterations { t.Error("Expecting 1000 elements.") } // Make sure all inserted values we're fetched from map. for i := 0; i < iterations; i++ { if i != a[i] { t.Error("missing value", i) } } } func TestJsonMarshal(t *testing.T) { SHARD_COUNT = 2 defer func() { SHARD_COUNT = 32 }() expected := "{\"a\":1,\"b\":2}" m := New() m.Set("a", 1) m.Set("b", 2) j, err := json.Marshal(m) if err != nil { t.Error(err) } if string(j) != expected { t.Error("json", string(j), "differ from expected", expected) return } } func TestKeys(t *testing.T) { m := New() // Insert 100 elements. for i := 0; i < 100; i++ { m.Set(strconv.Itoa(i), Animal{strconv.Itoa(i)}) } keys := m.Keys() if len(keys) != 100 { t.Error("We should have counted 100 elements.") } } func TestMInsert(t *testing.T) { animals := map[string]interface{}{ "elephant": Animal{"elephant"}, "monkey": Animal{"monkey"}, } m := New() m.MSet(animals) if m.Count() != 2 { t.Error("map should contain exactly two elements.") } } func TestFnv32(t *testing.T) { key := []byte("ABC") hasher := fnv.New32() hasher.Write(key) if fnv32(string(key)) != hasher.Sum32() { t.Errorf("Bundled fnv32 produced %d, expected result from hash/fnv32 is %d", fnv32(string(key)), hasher.Sum32()) } } func TestUpsert(t *testing.T) { dolphin := Animal{"dolphin"} whale := Animal{"whale"} tiger := Animal{"tiger"} lion := Animal{"lion"} cb := func(exists bool, valueInMap interface{}, newValue interface{}) interface{} { nv := newValue.(Animal) if !exists { return []Animal{nv} } res := valueInMap.([]Animal) return append(res, nv) } m := New() m.Set("marine", []Animal{dolphin}) m.Upsert("marine", whale, cb) m.Upsert("predator", tiger, cb) m.Upsert("predator", lion, cb) if m.Count() != 2 { t.Error("map should contain exactly two elements.") } compare := func(a, b []Animal) bool { if a == nil || b == nil { return false } if len(a) != len(b) { return false } for i, v := range a { if v != b[i] { return false } } return true } marineAnimals, ok := m.Get("marine") if !ok || !compare(marineAnimals.([]Animal), []Animal{dolphin, whale}) { t.Error("Set, then Upsert failed") } predators, ok := m.Get("predator") if !ok || !compare(predators.([]Animal), []Animal{tiger, lion}) { t.Error("Upsert, then Upsert failed") } }