github.com/MetalBlockchain/metalgo@v1.11.9/utils/linked/hashmap_test.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package linked 5 6 import ( 7 "testing" 8 9 "github.com/stretchr/testify/require" 10 11 "github.com/MetalBlockchain/metalgo/ids" 12 ) 13 14 func TestHashmap(t *testing.T) { 15 require := require.New(t) 16 17 lh := NewHashmap[ids.ID, int]() 18 require.Zero(lh.Len(), "a new hashmap should be empty") 19 20 key0 := ids.GenerateTestID() 21 _, exists := lh.Get(key0) 22 require.False(exists, "shouldn't have found the value") 23 24 _, _, exists = lh.Oldest() 25 require.False(exists, "shouldn't have found a value") 26 27 _, _, exists = lh.Newest() 28 require.False(exists, "shouldn't have found a value") 29 30 lh.Put(key0, 0) 31 require.Equal(1, lh.Len(), "wrong hashmap length") 32 33 val0, exists := lh.Get(key0) 34 require.True(exists, "should have found the value") 35 require.Zero(val0, "wrong value") 36 37 rkey0, val0, exists := lh.Oldest() 38 require.True(exists, "should have found the value") 39 require.Equal(key0, rkey0, "wrong key") 40 require.Zero(val0, "wrong value") 41 42 rkey0, val0, exists = lh.Newest() 43 require.True(exists, "should have found the value") 44 require.Equal(key0, rkey0, "wrong key") 45 require.Zero(val0, "wrong value") 46 47 key1 := ids.GenerateTestID() 48 lh.Put(key1, 1) 49 require.Equal(2, lh.Len(), "wrong hashmap length") 50 51 val1, exists := lh.Get(key1) 52 require.True(exists, "should have found the value") 53 require.Equal(1, val1, "wrong value") 54 55 rkey0, val0, exists = lh.Oldest() 56 require.True(exists, "should have found the value") 57 require.Equal(key0, rkey0, "wrong key") 58 require.Zero(val0, "wrong value") 59 60 rkey1, val1, exists := lh.Newest() 61 require.True(exists, "should have found the value") 62 require.Equal(key1, rkey1, "wrong key") 63 require.Equal(1, val1, "wrong value") 64 65 require.True(lh.Delete(key0)) 66 require.Equal(1, lh.Len(), "wrong hashmap length") 67 68 _, exists = lh.Get(key0) 69 require.False(exists, "shouldn't have found the value") 70 71 rkey1, val1, exists = lh.Oldest() 72 require.True(exists, "should have found the value") 73 require.Equal(rkey1, key1, "wrong key") 74 require.Equal(1, val1, "wrong value") 75 76 rkey1, val1, exists = lh.Newest() 77 require.True(exists, "should have found the value") 78 require.Equal(key1, rkey1, "wrong key") 79 require.Equal(1, val1, "wrong value") 80 81 lh.Put(key0, 0) 82 require.Equal(2, lh.Len(), "wrong hashmap length") 83 84 lh.Put(key1, 1) 85 require.Equal(2, lh.Len(), "wrong hashmap length") 86 87 rkey0, val0, exists = lh.Oldest() 88 require.True(exists, "should have found the value") 89 require.Equal(key0, rkey0, "wrong key") 90 require.Zero(val0, "wrong value") 91 92 rkey1, val1, exists = lh.Newest() 93 require.True(exists, "should have found the value") 94 require.Equal(key1, rkey1, "wrong key") 95 require.Equal(1, val1, "wrong value") 96 } 97 98 func TestHashmapClear(t *testing.T) { 99 require := require.New(t) 100 101 lh := NewHashmap[int, int]() 102 lh.Put(1, 1) 103 lh.Put(2, 2) 104 105 lh.Clear() 106 107 require.Empty(lh.entryMap) 108 require.Zero(lh.entryList.Len()) 109 require.Len(lh.freeList, 2) 110 for _, e := range lh.freeList { 111 require.Zero(e.Value) // Make sure the value is cleared 112 } 113 } 114 115 func TestIterator(t *testing.T) { 116 require := require.New(t) 117 id1, id2, id3 := ids.GenerateTestID(), ids.GenerateTestID(), ids.GenerateTestID() 118 119 // Case: No elements 120 { 121 lh := NewHashmap[ids.ID, int]() 122 iter := lh.NewIterator() 123 require.NotNil(iter) 124 // Should immediately be exhausted 125 require.False(iter.Next()) 126 require.False(iter.Next()) 127 // Should be empty 128 require.Equal(ids.Empty, iter.Key()) 129 require.Zero(iter.Value()) 130 } 131 132 // Case: 1 element 133 { 134 lh := NewHashmap[ids.ID, int]() 135 iter := lh.NewIterator() 136 require.NotNil(iter) 137 lh.Put(id1, 1) 138 require.True(iter.Next()) 139 require.Equal(id1, iter.Key()) 140 require.Equal(1, iter.Value()) 141 // Should be empty 142 require.False(iter.Next()) 143 // Re-assign id1 --> 10 144 lh.Put(id1, 10) 145 iter = lh.NewIterator() // New iterator 146 require.True(iter.Next()) 147 require.Equal(id1, iter.Key()) 148 require.Equal(10, iter.Value()) 149 // Should be empty 150 require.False(iter.Next()) 151 // Delete id1 152 require.True(lh.Delete(id1)) 153 iter = lh.NewIterator() 154 require.NotNil(iter) 155 // Should immediately be exhausted 156 require.False(iter.Next()) 157 } 158 159 // Case: Multiple elements 160 { 161 lh := NewHashmap[ids.ID, int]() 162 lh.Put(id1, 1) 163 lh.Put(id2, 2) 164 lh.Put(id3, 3) 165 iter := lh.NewIterator() 166 // Should give back all 3 elements 167 require.True(iter.Next()) 168 require.Equal(id1, iter.Key()) 169 require.Equal(1, iter.Value()) 170 require.True(iter.Next()) 171 require.Equal(id2, iter.Key()) 172 require.Equal(2, iter.Value()) 173 require.True(iter.Next()) 174 require.Equal(id3, iter.Key()) 175 require.Equal(3, iter.Value()) 176 // Should be exhausted 177 require.False(iter.Next()) 178 } 179 180 // Case: Delete element that has been iterated over 181 { 182 lh := NewHashmap[ids.ID, int]() 183 lh.Put(id1, 1) 184 lh.Put(id2, 2) 185 lh.Put(id3, 3) 186 iter := lh.NewIterator() 187 require.True(iter.Next()) 188 require.True(iter.Next()) 189 require.True(lh.Delete(id1)) 190 require.True(lh.Delete(id2)) 191 require.True(iter.Next()) 192 require.Equal(id3, iter.Key()) 193 require.Equal(3, iter.Value()) 194 // Should be exhausted 195 require.False(iter.Next()) 196 } 197 } 198 199 func Benchmark_Hashmap_Put(b *testing.B) { 200 key := "hello" 201 value := "world" 202 203 lh := NewHashmap[string, string]() 204 205 b.ResetTimer() 206 for i := 0; i < b.N; i++ { 207 lh.Put(key, value) 208 } 209 } 210 211 func Benchmark_Hashmap_PutDelete(b *testing.B) { 212 key := "hello" 213 value := "world" 214 215 lh := NewHashmap[string, string]() 216 217 b.ResetTimer() 218 for i := 0; i < b.N; i++ { 219 lh.Put(key, value) 220 lh.Delete(key) 221 } 222 }