github.com/MetalBlockchain/metalgo@v1.11.9/snow/engine/snowman/bootstrap/interval/tree_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 interval 5 6 import ( 7 "math" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 12 "github.com/MetalBlockchain/metalgo/database" 13 "github.com/MetalBlockchain/metalgo/database/memdb" 14 ) 15 16 func newTree(require *require.Assertions, db database.Database, intervals []*Interval) *Tree { 17 tree, err := NewTree(db) 18 require.NoError(err) 19 20 for _, toAdd := range intervals { 21 for i := toAdd.LowerBound; i <= toAdd.UpperBound; i++ { 22 require.NoError(tree.Add(db, i)) 23 } 24 } 25 return tree 26 } 27 28 func TestTreeAdd(t *testing.T) { 29 tests := []struct { 30 name string 31 toAdd []*Interval 32 expected []*Interval 33 expectedLen uint64 34 }{ 35 { 36 name: "single addition", 37 toAdd: []*Interval{ 38 { 39 LowerBound: 10, 40 UpperBound: 10, 41 }, 42 }, 43 expected: []*Interval{ 44 { 45 LowerBound: 10, 46 UpperBound: 10, 47 }, 48 }, 49 expectedLen: 1, 50 }, 51 { 52 name: "extend above", 53 toAdd: []*Interval{ 54 { 55 LowerBound: 10, 56 UpperBound: 11, 57 }, 58 }, 59 expected: []*Interval{ 60 { 61 LowerBound: 10, 62 UpperBound: 11, 63 }, 64 }, 65 expectedLen: 2, 66 }, 67 { 68 name: "extend below", 69 toAdd: []*Interval{ 70 { 71 LowerBound: 11, 72 UpperBound: 11, 73 }, 74 { 75 LowerBound: 10, 76 UpperBound: 10, 77 }, 78 }, 79 expected: []*Interval{ 80 { 81 LowerBound: 10, 82 UpperBound: 11, 83 }, 84 }, 85 expectedLen: 2, 86 }, 87 { 88 name: "merge", 89 toAdd: []*Interval{ 90 { 91 LowerBound: 10, 92 UpperBound: 10, 93 }, 94 { 95 LowerBound: 12, 96 UpperBound: 12, 97 }, 98 { 99 LowerBound: 11, 100 UpperBound: 11, 101 }, 102 }, 103 expected: []*Interval{ 104 { 105 LowerBound: 10, 106 UpperBound: 12, 107 }, 108 }, 109 expectedLen: 3, 110 }, 111 { 112 name: "ignore duplicate", 113 toAdd: []*Interval{ 114 { 115 LowerBound: 10, 116 UpperBound: 11, 117 }, 118 { 119 LowerBound: 11, 120 UpperBound: 11, 121 }, 122 }, 123 expected: []*Interval{ 124 { 125 LowerBound: 10, 126 UpperBound: 11, 127 }, 128 }, 129 expectedLen: 2, 130 }, 131 } 132 for _, test := range tests { 133 t.Run(test.name, func(t *testing.T) { 134 require := require.New(t) 135 136 db := memdb.New() 137 treeFromAdditions := newTree(require, db, test.toAdd) 138 require.Equal(test.expected, treeFromAdditions.Flatten()) 139 require.Equal(test.expectedLen, treeFromAdditions.Len()) 140 141 treeFromDB := newTree(require, db, nil) 142 require.Equal(test.expected, treeFromDB.Flatten()) 143 require.Equal(test.expectedLen, treeFromDB.Len()) 144 }) 145 } 146 } 147 148 func TestTreeRemove(t *testing.T) { 149 tests := []struct { 150 name string 151 toAdd []*Interval 152 toRemove []*Interval 153 expected []*Interval 154 expectedLen uint64 155 }{ 156 { 157 name: "single removal", 158 toAdd: []*Interval{ 159 { 160 LowerBound: 10, 161 UpperBound: 10, 162 }, 163 }, 164 toRemove: []*Interval{ 165 { 166 LowerBound: 10, 167 UpperBound: 10, 168 }, 169 }, 170 expected: []*Interval{}, 171 expectedLen: 0, 172 }, 173 { 174 name: "reduce above", 175 toAdd: []*Interval{ 176 { 177 LowerBound: 10, 178 UpperBound: 11, 179 }, 180 }, 181 toRemove: []*Interval{ 182 { 183 LowerBound: 11, 184 UpperBound: 11, 185 }, 186 }, 187 expected: []*Interval{ 188 { 189 LowerBound: 10, 190 UpperBound: 10, 191 }, 192 }, 193 expectedLen: 1, 194 }, 195 { 196 name: "reduce below", 197 toAdd: []*Interval{ 198 { 199 LowerBound: 10, 200 UpperBound: 11, 201 }, 202 }, 203 toRemove: []*Interval{ 204 { 205 LowerBound: 10, 206 UpperBound: 10, 207 }, 208 }, 209 expected: []*Interval{ 210 { 211 LowerBound: 11, 212 UpperBound: 11, 213 }, 214 }, 215 expectedLen: 1, 216 }, 217 { 218 name: "split", 219 toAdd: []*Interval{ 220 { 221 LowerBound: 10, 222 UpperBound: 12, 223 }, 224 }, 225 toRemove: []*Interval{ 226 { 227 LowerBound: 11, 228 UpperBound: 11, 229 }, 230 }, 231 expected: []*Interval{ 232 { 233 LowerBound: 10, 234 UpperBound: 10, 235 }, 236 { 237 LowerBound: 12, 238 UpperBound: 12, 239 }, 240 }, 241 expectedLen: 2, 242 }, 243 { 244 name: "ignore missing", 245 toAdd: []*Interval{ 246 { 247 LowerBound: 10, 248 UpperBound: 10, 249 }, 250 }, 251 toRemove: []*Interval{ 252 { 253 LowerBound: 11, 254 UpperBound: 11, 255 }, 256 }, 257 expected: []*Interval{ 258 { 259 LowerBound: 10, 260 UpperBound: 10, 261 }, 262 }, 263 expectedLen: 1, 264 }, 265 } 266 for _, test := range tests { 267 t.Run(test.name, func(t *testing.T) { 268 require := require.New(t) 269 270 db := memdb.New() 271 treeFromModifications := newTree(require, db, test.toAdd) 272 for _, toRemove := range test.toRemove { 273 for i := toRemove.LowerBound; i <= toRemove.UpperBound; i++ { 274 require.NoError(treeFromModifications.Remove(db, i)) 275 } 276 } 277 require.Equal(test.expected, treeFromModifications.Flatten()) 278 require.Equal(test.expectedLen, treeFromModifications.Len()) 279 280 treeFromDB := newTree(require, db, nil) 281 require.Equal(test.expected, treeFromDB.Flatten()) 282 require.Equal(test.expectedLen, treeFromDB.Len()) 283 }) 284 } 285 } 286 287 func TestTreeContains(t *testing.T) { 288 tests := []struct { 289 name string 290 tree []*Interval 291 height uint64 292 expected bool 293 }{ 294 { 295 name: "below", 296 tree: []*Interval{ 297 { 298 LowerBound: 10, 299 UpperBound: 10, 300 }, 301 }, 302 height: 9, 303 expected: false, 304 }, 305 { 306 name: "above", 307 tree: []*Interval{ 308 { 309 LowerBound: 10, 310 UpperBound: 10, 311 }, 312 }, 313 height: 11, 314 expected: false, 315 }, 316 { 317 name: "equal both", 318 tree: []*Interval{ 319 { 320 LowerBound: 10, 321 UpperBound: 10, 322 }, 323 }, 324 height: 10, 325 expected: true, 326 }, 327 { 328 name: "equal lower", 329 tree: []*Interval{ 330 { 331 LowerBound: 10, 332 UpperBound: 11, 333 }, 334 }, 335 height: 10, 336 expected: true, 337 }, 338 { 339 name: "equal upper", 340 tree: []*Interval{ 341 { 342 LowerBound: 9, 343 UpperBound: 10, 344 }, 345 }, 346 height: 10, 347 expected: true, 348 }, 349 { 350 name: "inside", 351 tree: []*Interval{ 352 { 353 LowerBound: 9, 354 UpperBound: 11, 355 }, 356 }, 357 height: 10, 358 expected: true, 359 }, 360 } 361 for _, test := range tests { 362 t.Run(test.name, func(t *testing.T) { 363 require := require.New(t) 364 365 tree := newTree(require, memdb.New(), test.tree) 366 require.Equal(test.expected, tree.Contains(test.height)) 367 }) 368 } 369 } 370 371 func TestTreeLenOverflow(t *testing.T) { 372 require := require.New(t) 373 374 db := memdb.New() 375 require.NoError(PutInterval(db, math.MaxUint64, 0)) 376 377 tree, err := NewTree(db) 378 require.NoError(err) 379 require.Zero(tree.Len()) 380 require.True(tree.Contains(0)) 381 require.True(tree.Contains(math.MaxUint64 / 2)) 382 require.True(tree.Contains(math.MaxUint64)) 383 384 require.NoError(tree.Remove(db, 5)) 385 require.Equal(uint64(math.MaxUint64), tree.Len()) 386 387 require.NoError(tree.Add(db, 5)) 388 require.Zero(tree.Len()) 389 }