github.com/ethersphere/bee/v2@v2.2.0/pkg/topology/pslice/pslice_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package pslice_test 6 7 import ( 8 "errors" 9 "sort" 10 "testing" 11 12 "github.com/ethersphere/bee/v2/pkg/swarm" 13 14 "github.com/ethersphere/bee/v2/pkg/topology/pslice" 15 ) 16 17 // TestShallowestEmpty tests that ShallowestEmpty functionality works correctly. 18 func TestShallowestEmpty(t *testing.T) { 19 t.Parallel() 20 21 var ( 22 base = swarm.RandAddress(t) 23 ps = pslice.New(16, base) 24 peers = make([][]swarm.Address, 16) 25 ) 26 27 for i := 0; i < 16; i++ { 28 for j := 0; j < 3; j++ { 29 a := swarm.RandAddressAt(t, base, i) 30 peers[i] = append(peers[i], a) 31 } 32 } 33 34 for i, v := range peers { 35 ps.Add(v...) 36 sd, none := ps.ShallowestEmpty() 37 if i == 15 { 38 if !none { 39 t.Fatal("expected last bin to be empty, thus return no empty bins true") 40 } 41 } else { 42 if sd != uint8(i+1) { 43 t.Fatalf("expected shallow empty bin to be %d but got %d", i+1, sd) 44 } 45 if none { 46 t.Fatal("got no empty bins but wanted some") 47 } 48 } 49 } 50 51 // this part removes peers in certain bins and asserts 52 // that the shallowest empty bin behaves correctly once the bins 53 for _, tc := range []struct { 54 removePo int 55 expectShallowest uint8 56 }{ 57 { 58 removePo: 3, 59 expectShallowest: 3, 60 }, { 61 removePo: 1, 62 expectShallowest: 1, 63 }, { 64 removePo: 10, 65 expectShallowest: 1, 66 }, { 67 removePo: 15, 68 expectShallowest: 1, 69 }, { 70 removePo: 14, 71 expectShallowest: 1, 72 }, { 73 removePo: 0, 74 expectShallowest: 0, 75 }, 76 } { 77 for _, v := range peers[tc.removePo] { 78 ps.Remove(v) 79 } 80 sd, none := ps.ShallowestEmpty() 81 if sd != tc.expectShallowest || none { 82 t.Fatalf("empty bin mismatch got %d want %d", sd, tc.expectShallowest) 83 } 84 } 85 ps.Add(peers[0][0]) 86 if sd, none := ps.ShallowestEmpty(); sd != 1 || none { 87 t.Fatalf("expected bin 1 to be empty shallowest but got %d", sd) 88 } 89 } 90 91 func TestNoPanicOnEmptyRemove(t *testing.T) { 92 t.Parallel() 93 94 base := swarm.RandAddress(t) 95 var ps = pslice.New(4, base) 96 97 addr1 := swarm.RandAddressAt(t, base, 2) 98 addr2 := swarm.RandAddressAt(t, base, 2) 99 100 ps.Remove(addr1) 101 102 ps.Add(addr1) 103 ps.Remove(addr1) 104 chkNotExists(t, ps, addr1) 105 106 ps.Add(addr1) 107 ps.Add(addr2) 108 ps.Remove(addr2) 109 chkExists(t, ps, addr1) 110 chkNotExists(t, ps, addr2) 111 } 112 113 // TestAddRemove checks that the Add, Remove and Exists methods work as expected. 114 func TestAddRemove(t *testing.T) { 115 t.Parallel() 116 117 var ( 118 base = swarm.RandAddress(t) 119 ps = pslice.New(4, base) 120 peers = make([]swarm.Address, 8) 121 ) 122 123 // 2 peers per bin 124 // indexes {0,1} {2,3} {4,5} {6,7} 125 for i := 0; i < 8; i += 2 { 126 peers[i] = swarm.RandAddressAt(t, base, i/2) 127 peers[i+1] = swarm.RandAddressAt(t, base, i/2) 128 } 129 130 // add one 131 ps.Add(peers[0]) 132 chkLen(t, ps, 1) 133 chkExists(t, ps, peers[:1]...) 134 chkNotExists(t, ps, peers[1:]...) 135 136 // check duplicates 137 ps.Add(peers[0]) 138 chkLen(t, ps, 1) 139 chkExists(t, ps, peers[:1]...) 140 chkNotExists(t, ps, peers[1:]...) 141 142 // check empty 143 ps.Remove(peers[0]) 144 chkLen(t, ps, 0) 145 chkNotExists(t, ps, peers...) 146 147 // add two in bin 0 148 ps.Add(peers[0]) 149 ps.Add(peers[1]) 150 chkLen(t, ps, 2) 151 chkExists(t, ps, peers[:2]...) 152 chkNotExists(t, ps, peers[2:]...) 153 154 ps.Add(peers[2]) 155 ps.Add(peers[3]) 156 chkLen(t, ps, 4) 157 chkExists(t, ps, peers[:4]...) 158 chkNotExists(t, ps, peers[4:]...) 159 160 ps.Remove(peers[1]) 161 chkLen(t, ps, 3) 162 chkExists(t, ps, peers[0], peers[2], peers[3]) 163 chkNotExists(t, ps, append([]swarm.Address{peers[1]}, peers[4:]...)...) 164 165 // this should not move the last cursor 166 ps.Add(peers[7]) 167 chkLen(t, ps, 4) 168 chkExists(t, ps, peers[0], peers[2], peers[3], peers[7]) 169 chkNotExists(t, ps, append([]swarm.Address{peers[1]}, peers[4:7]...)...) 170 171 ps.Add(peers[5]) 172 chkLen(t, ps, 5) 173 chkExists(t, ps, peers[0], peers[2], peers[3], peers[5], peers[7]) 174 chkNotExists(t, ps, []swarm.Address{peers[1], peers[4], peers[6]}...) 175 176 ps.Remove(peers[2]) 177 chkLen(t, ps, 4) 178 chkExists(t, ps, peers[0], peers[3], peers[5], peers[7]) 179 chkNotExists(t, ps, []swarm.Address{peers[1], peers[2], peers[4], peers[6]}...) 180 181 p := uint8(0) 182 for i := 0; i < 8; i += 2 { 183 ps.Remove(peers[i]) 184 ps.Remove(peers[i+1]) 185 p++ 186 } 187 188 // check empty again 189 chkLen(t, ps, 0) 190 chkNotExists(t, ps, peers...) 191 } 192 193 // TestIteratorError checks that error propagation works correctly in the iterators. 194 func TestIteratorError(t *testing.T) { 195 t.Parallel() 196 197 var ( 198 base = swarm.RandAddress(t) 199 ps = pslice.New(4, base) 200 a = swarm.RandAddressAt(t, base, 0) 201 e = errors.New("err1") 202 ) 203 204 ps.Add(a) 205 206 f := func(p swarm.Address, _ uint8) (stop, jumpToNext bool, err error) { 207 return false, false, e 208 } 209 210 err := ps.EachBin(f) 211 if !errors.Is(err, e) { 212 t.Fatal("didn't get expected error") 213 } 214 } 215 216 // TestIterators tests that the EachBin and EachBinRev iterators work as expected. 217 func TestIterators(t *testing.T) { 218 t.Parallel() 219 220 base := swarm.RandAddress(t) 221 ps := pslice.New(4, base) 222 223 peers := make([]swarm.Address, 4) 224 for i := 0; i < 4; i++ { 225 peers[i] = swarm.RandAddressAt(t, base, i) 226 } 227 228 testIterator(t, ps, false, false, 0, []swarm.Address{}) 229 testIteratorRev(t, ps, false, false, 0, []swarm.Address{}) 230 231 for _, v := range peers { 232 ps.Add(v) 233 } 234 235 testIterator(t, ps, false, false, 4, []swarm.Address{peers[3], peers[2], peers[1], peers[0]}) 236 testIteratorRev(t, ps, false, false, 4, peers) 237 238 ps.Remove(peers[2]) 239 testIterator(t, ps, false, false, 3, []swarm.Address{peers[3], peers[1], peers[0]}) 240 testIteratorRev(t, ps, false, false, 3, []swarm.Address{peers[0], peers[1], peers[3]}) 241 242 ps.Remove(peers[0]) 243 testIterator(t, ps, false, false, 2, []swarm.Address{peers[3], peers[1]}) 244 testIteratorRev(t, ps, false, false, 2, []swarm.Address{peers[1], peers[3]}) 245 246 ps.Remove(peers[3]) 247 testIterator(t, ps, false, false, 1, []swarm.Address{peers[1]}) 248 testIteratorRev(t, ps, false, false, 1, []swarm.Address{peers[1]}) 249 250 ps.Remove(peers[1]) 251 testIterator(t, ps, false, false, 0, []swarm.Address{}) 252 testIteratorRev(t, ps, false, false, 0, []swarm.Address{}) 253 } 254 255 func TestBinPeers(t *testing.T) { 256 t.Parallel() 257 258 for _, tc := range []struct { 259 peersCount []int 260 label string 261 }{ 262 { 263 peersCount: []int{0, 0, 0, 0}, 264 label: "bins-empty", 265 }, 266 { 267 peersCount: []int{0, 2, 0, 4}, 268 label: "some-bins-empty", 269 }, 270 { 271 peersCount: []int{0, 0, 6, 0}, 272 label: "some-bins-empty", 273 }, 274 { 275 peersCount: []int{3, 4, 5, 6}, 276 label: "full-bins", 277 }, 278 } { 279 tc := tc 280 t.Run(tc.label, func(t *testing.T) { 281 t.Parallel() 282 283 base := swarm.RandAddress(t) 284 285 binPeers := make([][]swarm.Address, len(tc.peersCount)) 286 287 // prepare slice 288 ps := pslice.New(len(tc.peersCount), base) 289 for bin, peersCount := range tc.peersCount { 290 for i := 0; i < peersCount; i++ { 291 peer := swarm.RandAddressAt(t, base, bin) 292 binPeers[bin] = append(binPeers[bin], peer) 293 ps.Add(peer) 294 } 295 } 296 297 // compare 298 for bin := range tc.peersCount { 299 if !isEqual(binPeers[bin], ps.BinPeers(uint8(bin))) { 300 t.Fatal("peers list do not match") 301 } 302 if len(binPeers[bin]) != ps.BinSize(uint8(bin)) { 303 t.Fatal("peers list lengths do not match") 304 } 305 } 306 307 // out of bound bin check 308 bins := ps.BinPeers(uint8(len(tc.peersCount))) 309 if bins != nil { 310 t.Fatal("peers must be nil for out of bound bin") 311 } 312 }) 313 } 314 } 315 316 func isEqual(a, b []swarm.Address) bool { 317 318 if len(a) != len(b) { 319 return false 320 } 321 322 sort.Slice(a, func(i, j int) bool { 323 return a[i].String() < a[j].String() 324 }) 325 326 sort.Slice(b, func(i, j int) bool { 327 return b[i].String() < b[j].String() 328 }) 329 330 for i, addr := range a { 331 if !b[i].Equal(addr) { 332 return false 333 } 334 } 335 336 return true 337 } 338 339 // TestIteratorsJumpStop tests that the EachBin and EachBinRev iterators jump to next bin and stop as expected. 340 func TestIteratorsJumpStop(t *testing.T) { 341 t.Parallel() 342 343 base := swarm.RandAddress(t) 344 ps := pslice.New(4, base) 345 346 peers := make([]swarm.Address, 0, 12) 347 for i := 0; i < 4; i++ { 348 for ii := 0; ii < 3; ii++ { 349 a := swarm.RandAddressAt(t, base, i) 350 peers = append(peers, a) 351 ps.Add(a) 352 } 353 } 354 355 // check that jump to next bin works as expected 356 testIterator(t, ps, true, false, 4, []swarm.Address{peers[9], peers[6], peers[3], peers[0]}) 357 testIteratorRev(t, ps, true, false, 4, []swarm.Address{peers[0], peers[3], peers[6], peers[9]}) 358 359 // // check that the stop functionality works correctly 360 testIterator(t, ps, true, true, 1, []swarm.Address{peers[9]}) 361 testIteratorRev(t, ps, true, true, 1, []swarm.Address{peers[0]}) 362 363 } 364 365 func testIteratorRev(t *testing.T, ps *pslice.PSlice, skipNext, stop bool, iterations int, peerseq []swarm.Address) { 366 t.Helper() 367 i := 0 368 f := func(p swarm.Address, po uint8) (bool, bool, error) { 369 if i == iterations { 370 t.Fatal("too many iterations!") 371 } 372 if !p.Equal(peerseq[i]) { 373 t.Error("got wrong peer seq from iterator") 374 } 375 i++ 376 return stop, skipNext, nil 377 } 378 379 err := ps.EachBinRev(f) 380 if err != nil { 381 t.Fatal(err) 382 } 383 } 384 385 func testIterator(t *testing.T, ps *pslice.PSlice, skipNext, stop bool, iterations int, peerseq []swarm.Address) { 386 t.Helper() 387 i := 0 388 f := func(p swarm.Address, po uint8) (bool, bool, error) { 389 if i == iterations { 390 t.Fatal("too many iterations!") 391 } 392 if !p.Equal(peerseq[i]) { 393 t.Error("got wrong peer seq from iterator") 394 } 395 i++ 396 return stop, skipNext, nil 397 } 398 399 err := ps.EachBin(f) 400 if err != nil { 401 t.Fatal(err) 402 } 403 } 404 405 func chkLen(t *testing.T, ps *pslice.PSlice, l int) { 406 t.Helper() 407 if lp := ps.Length(); lp != l { 408 t.Fatalf("length mismatch, want %d got %d", l, lp) 409 } 410 } 411 412 func chkExists(t *testing.T, ps *pslice.PSlice, addrs ...swarm.Address) { 413 t.Helper() 414 for _, a := range addrs { 415 if !ps.Exists(a) { 416 t.Fatalf("peer %s does not exist but should have", a.String()) 417 } 418 } 419 } 420 421 func chkNotExists(t *testing.T, ps *pslice.PSlice, addrs ...swarm.Address) { 422 t.Helper() 423 for _, a := range addrs { 424 if ps.Exists(a) { 425 t.Fatalf("peer %s does exists but should have not", a.String()) 426 } 427 } 428 } 429 430 const ( 431 bins = int(swarm.MaxBins) 432 perBin = 1000 433 ) 434 435 func BenchmarkAdd(b *testing.B) { 436 base := swarm.RandAddress(b) 437 ps := pslice.New(bins, base) 438 439 addrs := swarm.RandAddresses(b, bins*perBin) 440 441 b.ResetTimer() 442 443 for n := 0; n < b.N; n++ { 444 for _, addr := range addrs { 445 ps.Add(addr) 446 } 447 } 448 } 449 450 func BenchmarkAddBatch(b *testing.B) { 451 base := swarm.RandAddress(b) 452 ps := pslice.New(bins, base) 453 454 addrs := swarm.RandAddresses(b, bins*perBin) 455 456 b.ResetTimer() 457 458 for n := 0; n < b.N; n++ { 459 ps.Add(addrs...) 460 } 461 } 462 463 func BenchmarkRemove(b *testing.B) { 464 base := swarm.RandAddress(b) 465 ps := pslice.New(bins, base) 466 467 addrs := swarm.RandAddresses(b, bins*perBin) 468 ps.Add(addrs...) 469 470 b.ResetTimer() 471 472 for n := 0; n < b.N; n++ { 473 for _, addr := range addrs { 474 ps.Remove(addr) 475 } 476 } 477 } 478 479 func BenchmarkEachBin(b *testing.B) { 480 base := swarm.RandAddress(b) 481 ps := pslice.New(bins, base) 482 483 addrs := swarm.RandAddresses(b, bins*perBin) 484 ps.Add(addrs...) 485 486 b.ResetTimer() 487 488 for n := 0; n < b.N; n++ { 489 _ = ps.EachBin(func(a swarm.Address, u uint8) (stop bool, jumpToNext bool, err error) { 490 return false, false, nil 491 }) 492 } 493 } 494 495 func BenchmarkEachBinRev(b *testing.B) { 496 base := swarm.RandAddress(b) 497 ps := pslice.New(bins, base) 498 499 addrs := swarm.RandAddresses(b, bins*perBin) 500 ps.Add(addrs...) 501 502 b.ResetTimer() 503 504 for n := 0; n < b.N; n++ { 505 _ = ps.EachBinRev(func(a swarm.Address, u uint8) (stop bool, jumpToNext bool, err error) { 506 return false, false, nil 507 }) 508 } 509 }