github.com/cilium/statedb@v0.3.2/part/map_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package part_test 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "iter" 10 "math/rand/v2" 11 "testing" 12 13 "github.com/cilium/statedb/part" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 "gopkg.in/yaml.v3" 17 ) 18 19 func TestStringMap(t *testing.T) { 20 var m part.Map[string, int] 21 22 // 23 // Operations on empty map 24 // 25 26 v, ok := m.Get("nonexisting") 27 assert.False(t, ok, "Get non-existing") 28 assert.Equal(t, 0, v) 29 30 assertIterEmpty := func(it iter.Seq2[string, int]) { 31 t.Helper() 32 for range it { 33 t.Fatalf("expected empty iterator") 34 } 35 } 36 assertIterEmpty(m.LowerBound("")) 37 assertIterEmpty(m.Prefix("")) 38 assertIterEmpty(m.All()) 39 40 // 41 // Operations on non-empty map 42 // 43 44 // Ordered list of key-value pairs we're testing with. 45 // Prefix set so that Map keeps them in the same order. 46 kvs := []struct { 47 k string 48 v int 49 }{ 50 {"1_one", 1}, 51 {"2_two", 2}, 52 {"3_three", 3}, 53 } 54 55 // Set some values in two different ways. 56 m = m.Set("1_one", 1) 57 m = part.FromMap(m, map[string]int{ 58 "2_two": 2, 59 "3_three": 3, 60 }) 61 62 // Setting on a copy doeen't affect original 63 m.Set("4_four", 4) 64 _, ok = m.Get("4_four") 65 assert.False(t, ok, "Get non-existing") 66 67 // Getting a non-existing value still does the same. 68 v, ok = m.Get("nonexisting") 69 assert.False(t, ok, "Get non-existing") 70 assert.Equal(t, 0, v) 71 72 for _, kv := range kvs { 73 v, ok := m.Get(kv.k) 74 assert.True(t, ok, "Get %q", kv.k) 75 assert.Equal(t, v, kv.v) 76 } 77 78 expected := kvs 79 for k, v := range m.All() { 80 kv := expected[0] 81 expected = expected[1:] 82 assert.EqualValues(t, kv.k, k) 83 assert.EqualValues(t, kv.v, v) 84 } 85 assert.Empty(t, expected) 86 87 expected = kvs[1:] 88 for k, v := range m.LowerBound("2") { 89 kv := expected[0] 90 expected = expected[1:] 91 assert.EqualValues(t, kv.k, k) 92 assert.EqualValues(t, kv.v, v) 93 } 94 assert.Empty(t, expected) 95 96 expected = kvs[1:2] 97 for k, v := range m.Prefix("2") { 98 kv := expected[0] 99 expected = expected[1:] 100 assert.EqualValues(t, kv.k, k) 101 assert.EqualValues(t, kv.v, v) 102 } 103 assert.Empty(t, expected) 104 105 assert.Equal(t, 3, m.Len()) 106 107 mOld := m 108 m = m.Delete(kvs[0].k) 109 _, ok = m.Get(kvs[0].k) 110 assert.False(t, ok, "Get after Delete") 111 112 _, ok = mOld.Get(kvs[0].k) 113 assert.True(t, ok, "Original modified by Delete") 114 mOld = mOld.Delete(kvs[0].k) 115 _, ok = mOld.Get(kvs[0].k) 116 assert.False(t, ok, "Get after Delete") 117 118 assert.Equal(t, 2, m.Len()) 119 } 120 121 func TestUint64Map(t *testing.T) { 122 // TestStringMap tests most of the operations. We just check here that 123 // fromBytes and toBytes work and can iterate in the right order. 124 var m part.Map[uint64, int] 125 m = m.Set(42, 42) 126 m = m.Set(55, 55) 127 m = m.Set(72, 72) 128 129 v, ok := m.Get(42) 130 assert.True(t, ok, "Get 42") 131 assert.Equal(t, 42, v) 132 133 count := 0 134 expected := []uint64{55, 72} 135 for k, v := range m.LowerBound(55) { 136 kv := expected[0] 137 expected = expected[1:] 138 assert.EqualValues(t, kv, k) 139 assert.EqualValues(t, kv, v) 140 count++ 141 } 142 assert.Equal(t, 2, count) 143 } 144 145 func TestRegisterKeyType(t *testing.T) { 146 type testKey struct { 147 X string 148 } 149 part.RegisterKeyType(func(k testKey) []byte { return []byte(k.X) }) 150 151 var m part.Map[testKey, int] 152 m = m.Set(testKey{"hello"}, 123) 153 154 v, ok := m.Get(testKey{"hello"}) 155 assert.True(t, ok, "Get 'hello'") 156 assert.Equal(t, 123, v) 157 158 for k, v := range m.All() { 159 assert.Equal(t, testKey{"hello"}, k) 160 assert.Equal(t, 123, v) 161 } 162 } 163 164 func TestMapJSON(t *testing.T) { 165 var m part.Map[string, int] 166 m = m.Set("foo", 1).Set("bar", 2).Set("baz", 3) 167 168 bs, err := json.Marshal(m) 169 require.NoError(t, err, "Marshal") 170 171 var m2 part.Map[string, int] 172 err = json.Unmarshal(bs, &m2) 173 require.NoError(t, err, "Unmarshal") 174 require.True(t, m.SlowEqual(m2), "SlowEqual") 175 } 176 177 func TestMapYAMLStringKey(t *testing.T) { 178 var m part.Map[string, int] 179 m = m.Set("foo", 1).Set("bar", 2).Set("baz", 3) 180 181 bs, err := yaml.Marshal(m) 182 require.NoError(t, err, "Marshal") 183 184 var m2 part.Map[string, int] 185 err = yaml.Unmarshal(bs, &m2) 186 require.NoError(t, err, "Unmarshal") 187 require.True(t, m.SlowEqual(m2), "SlowEqual") 188 } 189 190 func TestMapYAMLStructKey(t *testing.T) { 191 type key struct { 192 A int `yaml:"a"` 193 B string `yaml:"b"` 194 } 195 part.RegisterKeyType[key](func(k key) []byte { 196 return []byte(fmt.Sprintf("%d-%s", k.A, k.B)) 197 }) 198 var m part.Map[key, int] 199 m = m.Set(key{1, "one"}, 1).Set(key{2, "two"}, 2).Set(key{3, "three"}, 3) 200 201 bs, err := yaml.Marshal(m) 202 require.NoError(t, err, "Marshal") 203 204 var m2 part.Map[key, int] 205 err = yaml.Unmarshal(bs, &m2) 206 require.NoError(t, err, "Unmarshal") 207 require.True(t, m.SlowEqual(m2), "SlowEqual") 208 } 209 210 func Benchmark_Uint64Map_Random(b *testing.B) { 211 numItems := 1000 212 keys := map[uint64]int{} 213 for len(keys) < numItems { 214 k := uint64(rand.Int64()) 215 keys[k] = int(k) 216 } 217 for n := 0; n < b.N; n++ { 218 var m part.Map[uint64, int] 219 for k, v := range keys { 220 m = m.Set(k, v) 221 v2, ok := m.Get(k) 222 if !ok || v != v2 { 223 b.Fatalf("Get did not return value") 224 } 225 } 226 } 227 b.ReportMetric(float64(numItems*b.N)/b.Elapsed().Seconds(), "items/sec") 228 } 229 230 func Benchmark_Uint64Map_Sequential(b *testing.B) { 231 numItems := 1000 232 233 for n := 0; n < b.N; n++ { 234 var m part.Map[uint64, int] 235 for i := 0; i < numItems; i++ { 236 k := uint64(i) 237 m = m.Set(k, i) 238 v, ok := m.Get(k) 239 if !ok || v != i { 240 b.Fatalf("Get did not return value") 241 } 242 } 243 } 244 b.ReportMetric(float64(numItems*b.N)/b.Elapsed().Seconds(), "items/sec") 245 }