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  }