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  }