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 }