github.com/AlohaMobile/go-ethereum@v1.9.7/p2p/enode/iter_test.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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-ethereum 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-ethereum 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/ethereum/go-ethereum/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 }