github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/enode/iter_test.go (about)

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