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 }