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  }