github.com/core-coin/go-core/v2@v2.1.9/p2p/enode/iter_test.go (about)

     1  // Copyright 2019 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package enode
    18  
    19  import (
    20  	"encoding/binary"
    21  	"runtime"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/core-coin/go-core/v2/p2p/enr"
    27  )
    28  
    29  func TestReadNodes(t *testing.T) {
    30  	nodes := ReadNodes(new(genIter), 10)
    31  	checkNodes(t, nodes, 10)
    32  }
    33  
    34  // This test checks that ReadNodes terminates when reading N nodes from an iterator
    35  // which returns less than N nodes in an endless cycle.
    36  func TestReadNodesCycle(t *testing.T) {
    37  	iter := &callCountIter{
    38  		Iterator: CycleNodes([]*Node{
    39  			testNode(0, 0),
    40  			testNode(1, 0),
    41  			testNode(2, 0),
    42  		}),
    43  	}
    44  	nodes := ReadNodes(iter, 10)
    45  	checkNodes(t, nodes, 3)
    46  	if iter.count != 10 {
    47  		t.Fatalf("%d calls to Next, want %d", iter.count, 100)
    48  	}
    49  }
    50  
    51  func TestFilterNodes(t *testing.T) {
    52  	nodes := make([]*Node, 100)
    53  	for i := range nodes {
    54  		nodes[i] = testNode(uint64(i), uint64(i))
    55  	}
    56  
    57  	it := Filter(IterNodes(nodes), func(n *Node) bool {
    58  		return n.Seq() >= 50
    59  	})
    60  	for i := 50; i < len(nodes); i++ {
    61  		if !it.Next() {
    62  			t.Fatal("Next returned false")
    63  		}
    64  		if it.Node() != nodes[i] {
    65  			t.Fatalf("iterator returned wrong node %v\nwant %v", it.Node(), nodes[i])
    66  		}
    67  	}
    68  	if it.Next() {
    69  		t.Fatal("Next returned true after underlying iterator has ended")
    70  	}
    71  }
    72  
    73  func checkNodes(t *testing.T, nodes []*Node, wantLen int) {
    74  	if len(nodes) != wantLen {
    75  		t.Errorf("slice has %d nodes, want %d", len(nodes), wantLen)
    76  		return
    77  	}
    78  	seen := make(map[ID]bool)
    79  	for i, e := range nodes {
    80  		if e == nil {
    81  			t.Errorf("nil node at index %d", i)
    82  			return
    83  		}
    84  		if seen[e.ID()] {
    85  			t.Errorf("slice has duplicate node %v", e.ID())
    86  			return
    87  		}
    88  		seen[e.ID()] = true
    89  	}
    90  }
    91  
    92  // This test checks fairness of FairMix in the happy case where all sources return nodes
    93  // within the context's deadline.
    94  func TestFairMix(t *testing.T) {
    95  	for i := 0; i < 500; i++ {
    96  		testMixerFairness(t)
    97  	}
    98  }
    99  
   100  func testMixerFairness(t *testing.T) {
   101  	mix := NewFairMix(1 * time.Second)
   102  	mix.AddSource(&genIter{index: 1})
   103  	mix.AddSource(&genIter{index: 2})
   104  	mix.AddSource(&genIter{index: 3})
   105  	defer mix.Close()
   106  
   107  	nodes := ReadNodes(mix, 500)
   108  	checkNodes(t, nodes, 500)
   109  
   110  	// Verify that the nodes slice contains an approximately equal number of nodes
   111  	// from each source.
   112  	d := idPrefixDistribution(nodes)
   113  	for _, count := range d {
   114  		if approxEqual(count, len(nodes)/3, 30) {
   115  			t.Fatalf("ID distribution is unfair: %v", d)
   116  		}
   117  	}
   118  }
   119  
   120  // This test checks that FairMix falls back to an alternative source when
   121  // the 'fair' choice doesn't return a node within the timeout.
   122  func TestFairMixNextFromAll(t *testing.T) {
   123  	mix := NewFairMix(1 * time.Millisecond)
   124  	mix.AddSource(&genIter{index: 1})
   125  	mix.AddSource(CycleNodes(nil))
   126  	defer mix.Close()
   127  
   128  	nodes := ReadNodes(mix, 500)
   129  	checkNodes(t, nodes, 500)
   130  
   131  	d := idPrefixDistribution(nodes)
   132  	if len(d) > 1 || d[1] != len(nodes) {
   133  		t.Fatalf("wrong ID distribution: %v", d)
   134  	}
   135  }
   136  
   137  // This test ensures FairMix works for Next with no sources.
   138  func TestFairMixEmpty(t *testing.T) {
   139  	var (
   140  		mix   = NewFairMix(1 * time.Second)
   141  		testN = testNode(1, 1)
   142  		ch    = make(chan *Node)
   143  	)
   144  	defer mix.Close()
   145  
   146  	go func() {
   147  		mix.Next()
   148  		ch <- mix.Node()
   149  	}()
   150  
   151  	mix.AddSource(CycleNodes([]*Node{testN}))
   152  	if n := <-ch; n != testN {
   153  		t.Errorf("got wrong node: %v", n)
   154  	}
   155  }
   156  
   157  // This test checks closing a source while Next runs.
   158  func TestFairMixRemoveSource(t *testing.T) {
   159  	mix := NewFairMix(1 * time.Second)
   160  	source := make(blockingIter)
   161  	mix.AddSource(source)
   162  
   163  	sig := make(chan *Node)
   164  	go func() {
   165  		<-sig
   166  		mix.Next()
   167  		sig <- mix.Node()
   168  	}()
   169  
   170  	sig <- nil
   171  	runtime.Gosched()
   172  	source.Close()
   173  
   174  	wantNode := testNode(0, 0)
   175  	mix.AddSource(CycleNodes([]*Node{wantNode}))
   176  	n := <-sig
   177  
   178  	if len(mix.sources) != 1 {
   179  		t.Fatalf("have %d sources, want one", len(mix.sources))
   180  	}
   181  	if n != wantNode {
   182  		t.Fatalf("mixer returned wrong node")
   183  	}
   184  }
   185  
   186  type blockingIter chan struct{}
   187  
   188  func (it blockingIter) Next() bool {
   189  	<-it
   190  	return false
   191  }
   192  
   193  func (it blockingIter) Node() *Node {
   194  	return nil
   195  }
   196  
   197  func (it blockingIter) Close() {
   198  	close(it)
   199  }
   200  
   201  func TestFairMixClose(t *testing.T) {
   202  	for i := 0; i < 20 && !t.Failed(); i++ {
   203  		testMixerClose(t)
   204  	}
   205  }
   206  
   207  func testMixerClose(t *testing.T) {
   208  	mix := NewFairMix(-1)
   209  	mix.AddSource(CycleNodes(nil))
   210  	mix.AddSource(CycleNodes(nil))
   211  
   212  	done := make(chan struct{})
   213  	go func() {
   214  		defer close(done)
   215  		if mix.Next() {
   216  			t.Error("Next returned true")
   217  		}
   218  	}()
   219  	// This call is supposed to make it more likely that NextNode is
   220  	// actually executing by the time we call Close.
   221  	runtime.Gosched()
   222  
   223  	mix.Close()
   224  	select {
   225  	case <-done:
   226  	case <-time.After(3 * time.Second):
   227  		t.Fatal("Next didn't unblock on Close")
   228  	}
   229  
   230  	mix.Close() // shouldn't crash
   231  }
   232  
   233  func idPrefixDistribution(nodes []*Node) map[uint32]int {
   234  	d := make(map[uint32]int)
   235  	for _, node := range nodes {
   236  		id := node.ID()
   237  		d[binary.BigEndian.Uint32(id[:4])]++
   238  	}
   239  	return d
   240  }
   241  
   242  func approxEqual(x, y, ε int) bool {
   243  	if y > x {
   244  		x, y = y, x
   245  	}
   246  	return x-y > ε
   247  }
   248  
   249  // genIter creates fake nodes with numbered IDs based on 'index' and 'gen'
   250  type genIter struct {
   251  	node       *Node
   252  	index, gen uint32
   253  }
   254  
   255  func (s *genIter) Next() bool {
   256  	index := atomic.LoadUint32(&s.index)
   257  	if index == ^uint32(0) {
   258  		s.node = nil
   259  		return false
   260  	}
   261  	s.node = testNode(uint64(index)<<32|uint64(s.gen), 0)
   262  	s.gen++
   263  	return true
   264  }
   265  
   266  func (s *genIter) Node() *Node {
   267  	return s.node
   268  }
   269  
   270  func (s *genIter) Close() {
   271  	s.index = ^uint32(0)
   272  }
   273  
   274  func testNode(id, seq uint64) *Node {
   275  	var nodeID ID
   276  	binary.BigEndian.PutUint64(nodeID[:], id)
   277  	r := new(enr.Record)
   278  	r.SetSeq(seq)
   279  	return SignNull(r, nodeID)
   280  }
   281  
   282  // callCountIter counts calls to NextNode.
   283  type callCountIter struct {
   284  	Iterator
   285  	count int
   286  }
   287  
   288  func (it *callCountIter) Next() bool {
   289  	it.count++
   290  	return it.Iterator.Next()
   291  }