github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/generics/hashmap/map_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package hashmap
    22  
    23  import (
    24  	"testing"
    25  
    26  	"github.com/cespare/xxhash/v2"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  // newTestStringMap returns a new generic (non-specialized) version of
    32  // the map that is intended to be used with strings, this is useful for testing
    33  // the non-generated generic map source code.
    34  func newTestStringMap(size int) *Map {
    35  	return mapAlloc(mapOptions{
    36  		hash: func(key KeyType) MapHash {
    37  			return MapHash(xxhash.Sum64([]byte(key.(string))))
    38  		},
    39  		equals: func(x, y KeyType) bool {
    40  			return x.(string) == y.(string)
    41  		},
    42  		copy: func(k KeyType) KeyType {
    43  			// Strings are immutable, so we can just return the same string
    44  			return k
    45  		},
    46  		finalize: func(k KeyType) {
    47  			// No-op, not pooling
    48  		},
    49  		initialSize: size,
    50  	})
    51  }
    52  
    53  func TestMapGet(t *testing.T) {
    54  	m := newTestStringMap(0)
    55  
    56  	_, ok := m.Get("foo")
    57  	require.False(t, ok)
    58  
    59  	m.Set("foo", "bar")
    60  
    61  	v, ok := m.Get("foo")
    62  	require.True(t, ok)
    63  	require.Equal(t, "bar", v.(string))
    64  }
    65  
    66  func TestMapSetOverwrite(t *testing.T) {
    67  	m := newTestStringMap(0)
    68  
    69  	_, ok := m.Get("foo")
    70  	require.False(t, ok)
    71  
    72  	m.Set("foo", "bar")
    73  	m.Set("foo", "baz")
    74  	m.Set("foo", "qux")
    75  
    76  	v, ok := m.Get("foo")
    77  	require.True(t, ok)
    78  	require.Equal(t, "qux", v.(string))
    79  }
    80  
    81  func TestMapContains(t *testing.T) {
    82  	m := newTestStringMap(0)
    83  
    84  	exists := m.Contains("foo")
    85  	require.False(t, exists)
    86  
    87  	m.Set("foo", "bar")
    88  
    89  	exists = m.Contains("foo")
    90  	require.True(t, exists)
    91  }
    92  
    93  func TestMapDelete(t *testing.T) {
    94  	m := newTestStringMap(0)
    95  
    96  	m.Set("foo", "a")
    97  	m.Set("bar", "b")
    98  	m.Set("baz", "c")
    99  	require.Equal(t, 3, m.Len())
   100  
   101  	m.Delete("foo")
   102  	require.Equal(t, 2, m.Len())
   103  
   104  	m.Delete("keyThatDoesNotExist")
   105  	require.Equal(t, 2, m.Len())
   106  }
   107  
   108  func TestMapReset(t *testing.T) {
   109  	m := newTestStringMap(0)
   110  
   111  	m.Set("foo", "a")
   112  	m.Set("bar", "b")
   113  	m.Set("baz", "c")
   114  	require.Equal(t, 3, m.Len())
   115  
   116  	ref := m.lookup
   117  	m.Reset()
   118  	require.Equal(t, 0, m.Len())
   119  	assert.Equal(t, ref, m.lookup)
   120  }
   121  
   122  func TestMapReallocate(t *testing.T) {
   123  	m := newTestStringMap(0)
   124  
   125  	m.Set("foo", "a")
   126  	m.Set("bar", "b")
   127  	m.Set("baz", "c")
   128  	require.Equal(t, 3, m.Len())
   129  
   130  	ref := m.lookup
   131  	m.Reallocate()
   132  	require.Equal(t, 0, m.Len())
   133  	assert.NotEqual(t, ref, m.lookup)
   134  }
   135  
   136  func TestMapIter(t *testing.T) {
   137  	m := newTestStringMap(0)
   138  
   139  	strMap := make(map[string]string)
   140  	set := func(k, v string) {
   141  		m.Set(k, v)
   142  		strMap[k] = v
   143  	}
   144  
   145  	set("foo", "a")
   146  	set("bar", "b")
   147  	set("baz", "c")
   148  
   149  	iterated := 0
   150  	for _, entry := range m.Iter() {
   151  		iterated++
   152  		require.Equal(t, strMap[entry.Key().(string)], entry.Value().(string))
   153  	}
   154  	require.Equal(t, len(strMap), iterated)
   155  }
   156  
   157  func TestMapCollision(t *testing.T) {
   158  	m := newTestStringMap(0)
   159  	// Always collide
   160  	m.hash = func(_ KeyType) MapHash { return 0 }
   161  
   162  	// Insert foo, ensure set at fake hash
   163  	m.Set("foo", "a")
   164  
   165  	entry, ok := m.lookup[0]
   166  	assert.True(t, ok)
   167  	assert.Equal(t, "foo", entry.Key().(string))
   168  	assert.Equal(t, "a", entry.value.(string))
   169  
   170  	// Insert bar, ensure collides and both next to each other
   171  	m.Set("bar", "b")
   172  
   173  	entry, ok = m.lookup[0]
   174  	assert.True(t, ok)
   175  	assert.Equal(t, "foo", entry.Key().(string))
   176  	assert.Equal(t, "a", entry.value.(string))
   177  
   178  	entry, ok = m.lookup[1]
   179  	assert.True(t, ok)
   180  	assert.Equal(t, "bar", entry.Key().(string))
   181  	assert.Equal(t, "b", entry.value.(string))
   182  
   183  	// Test getting both keys works well too
   184  	v, ok := m.Get("foo")
   185  	assert.True(t, ok)
   186  	assert.Equal(t, "a", v.(string))
   187  
   188  	v, ok = m.Get("bar")
   189  	assert.True(t, ok)
   190  	assert.Equal(t, "b", v.(string))
   191  
   192  	// Ensure set for the colliding key works
   193  	m.Set("bar", "c")
   194  
   195  	entry, ok = m.lookup[0]
   196  	assert.True(t, ok)
   197  	assert.Equal(t, "foo", entry.Key().(string))
   198  	assert.Equal(t, "a", entry.value.(string))
   199  
   200  	entry, ok = m.lookup[1]
   201  	assert.True(t, ok)
   202  	assert.Equal(t, "bar", entry.Key().(string))
   203  	assert.Equal(t, "c", entry.value.(string))
   204  
   205  	// Test getting both keys works well too
   206  	v, ok = m.Get("foo")
   207  	assert.True(t, ok)
   208  	assert.Equal(t, "a", v.(string))
   209  
   210  	v, ok = m.Get("bar")
   211  	assert.True(t, ok)
   212  	assert.Equal(t, "c", v.(string))
   213  }
   214  
   215  func TestMapWithSize(t *testing.T) {
   216  	m := newTestStringMap(42)
   217  	m.Set("foo", "bar")
   218  	v, ok := m.Get("foo")
   219  	require.True(t, ok)
   220  	require.Equal(t, "bar", v.(string))
   221  }
   222  
   223  func TestMapSetUnsafeNoCopyKey(t *testing.T) {
   224  	m := newTestStringMap(0)
   225  
   226  	copies := 0
   227  	m.copy = func(k KeyType) KeyType {
   228  		copies++
   229  		return k
   230  	}
   231  
   232  	m.Set("foo", "a")
   233  	m.Set("bar", "b")
   234  	assert.Equal(t, 2, copies)
   235  
   236  	m.SetUnsafe("baz", "c", SetUnsafeOptions{NoCopyKey: true})
   237  	assert.Equal(t, 2, copies)
   238  }
   239  
   240  func TestMapSetUnsafeNoCopyNoFinalizeKey(t *testing.T) {
   241  	m := newTestStringMap(0)
   242  
   243  	copies := 0
   244  	m.copy = func(k KeyType) KeyType {
   245  		copies++
   246  		return k
   247  	}
   248  
   249  	finalizes := 0
   250  	m.finalize = func(k KeyType) {
   251  		finalizes++
   252  	}
   253  
   254  	m.Set("foo", "a")
   255  	m.Set("bar", "b")
   256  	assert.Equal(t, 2, copies)
   257  
   258  	m.Delete("foo")
   259  	m.Delete("bar")
   260  	assert.Equal(t, 2, finalizes)
   261  
   262  	m.SetUnsafe("baz", "c", SetUnsafeOptions{NoCopyKey: true, NoFinalizeKey: true})
   263  	assert.Equal(t, 2, copies)
   264  	assert.Equal(t, 2, finalizes)
   265  }