github.com/okex/exchain@v1.8.0/libs/tendermint/blockchain/v0/pool_test.go (about) 1 package v0 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 11 "github.com/okex/exchain/libs/tendermint/libs/log" 12 tmrand "github.com/okex/exchain/libs/tendermint/libs/rand" 13 "github.com/okex/exchain/libs/tendermint/p2p" 14 "github.com/okex/exchain/libs/tendermint/types" 15 ) 16 17 func init() { 18 peerTimeout = 2 * time.Second 19 } 20 21 type testPeer struct { 22 id p2p.ID 23 base int64 24 height int64 25 inputChan chan inputData //make sure each peer's data is sequential 26 } 27 28 type inputData struct { 29 t *testing.T 30 pool *BlockPool 31 request BlockRequest 32 } 33 34 func (p testPeer) runInputRoutine() { 35 go func() { 36 for input := range p.inputChan { 37 p.simulateInput(input) 38 } 39 }() 40 } 41 42 // Request desired, pretend like we got the block immediately. 43 func (p testPeer) simulateInput(input inputData) { 44 block := &types.Block{Header: types.Header{Height: input.request.Height}} 45 input.pool.AddBlock(input.request.PeerID, &bcBlockResponseMessage{Block: block}, 123) 46 // TODO: uncommenting this creates a race which is detected by: 47 // https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856 48 // see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890 49 // input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height) 50 } 51 52 type testPeers map[p2p.ID]testPeer 53 54 func (ps testPeers) start() { 55 for _, v := range ps { 56 v.runInputRoutine() 57 } 58 } 59 60 func (ps testPeers) stop() { 61 for _, v := range ps { 62 close(v.inputChan) 63 } 64 } 65 66 func makePeers(numPeers int, minHeight, maxHeight int64) testPeers { 67 peers := make(testPeers, numPeers) 68 for i := 0; i < numPeers; i++ { 69 peerID := p2p.ID(tmrand.Str(12)) 70 height := minHeight + tmrand.Int63n(maxHeight-minHeight) 71 base := minHeight + int64(i) 72 if base > height { 73 base = height 74 } 75 peers[peerID] = testPeer{peerID, base, height, make(chan inputData, 10)} 76 } 77 return peers 78 } 79 80 func TestBlockPoolBasic(t *testing.T) { 81 start := int64(42) 82 peers := makePeers(10, start+1, 1000) 83 errorsCh := make(chan peerError, 1000) 84 requestsCh := make(chan BlockRequest, 1000) 85 pool := NewBlockPool(start, requestsCh, errorsCh) 86 pool.SetLogger(log.TestingLogger()) 87 88 err := pool.Start() 89 if err != nil { 90 t.Error(err) 91 } 92 93 defer pool.Stop() 94 95 peers.start() 96 defer peers.stop() 97 98 // Introduce each peer. 99 go func() { 100 for _, peer := range peers { 101 pool.SetPeerRange(peer.id, peer.base, peer.height, 0) 102 } 103 }() 104 105 // Start a goroutine to pull blocks 106 go func() { 107 for { 108 if !pool.IsRunning() { 109 return 110 } 111 first, second, _ := pool.PeekTwoBlocks() 112 if first != nil && second != nil { 113 pool.PopRequest() 114 } else { 115 time.Sleep(1 * time.Second) 116 } 117 } 118 }() 119 120 // Pull from channels 121 for { 122 select { 123 case err := <-errorsCh: 124 t.Error(err) 125 case request := <-requestsCh: 126 t.Logf("Pulled new BlockRequest %v", request) 127 if request.Height == 300 { 128 return // Done! 129 } 130 131 peers[request.PeerID].inputChan <- inputData{t, pool, request} 132 } 133 } 134 } 135 136 func TestBlockPoolTimeout(t *testing.T) { 137 start := int64(42) 138 peers := makePeers(10, start+1, 1000) 139 errorsCh := make(chan peerError, 1000) 140 requestsCh := make(chan BlockRequest, 1000) 141 pool := NewBlockPool(start, requestsCh, errorsCh) 142 pool.SetLogger(log.TestingLogger()) 143 err := pool.Start() 144 if err != nil { 145 t.Error(err) 146 } 147 defer pool.Stop() 148 149 for _, peer := range peers { 150 t.Logf("Peer %v", peer.id) 151 } 152 153 // Introduce each peer. 154 go func() { 155 for _, peer := range peers { 156 pool.SetPeerRange(peer.id, peer.base, peer.height, 0) 157 } 158 }() 159 160 // Start a goroutine to pull blocks 161 go func() { 162 for { 163 if !pool.IsRunning() { 164 return 165 } 166 first, second, _ := pool.PeekTwoBlocks() 167 if first != nil && second != nil { 168 pool.PopRequest() 169 } else { 170 time.Sleep(1 * time.Second) 171 } 172 } 173 }() 174 175 // Pull from channels 176 counter := 0 177 timedOut := map[p2p.ID]struct{}{} 178 for { 179 select { 180 case err := <-errorsCh: 181 t.Log(err) 182 // consider error to be always timeout here 183 if _, ok := timedOut[err.peerID]; !ok { 184 counter++ 185 if counter == len(peers) { 186 return // Done! 187 } 188 } 189 case request := <-requestsCh: 190 t.Logf("Pulled new BlockRequest %+v", request) 191 } 192 } 193 } 194 195 func TestBlockPoolRemovePeer(t *testing.T) { 196 peers := make(testPeers, 10) 197 for i := 0; i < 10; i++ { 198 peerID := p2p.ID(fmt.Sprintf("%d", i+1)) 199 height := int64(i + 1) 200 peers[peerID] = testPeer{peerID, 0, height, make(chan inputData)} 201 } 202 requestsCh := make(chan BlockRequest) 203 errorsCh := make(chan peerError) 204 205 pool := NewBlockPool(1, requestsCh, errorsCh) 206 pool.SetLogger(log.TestingLogger()) 207 err := pool.Start() 208 require.NoError(t, err) 209 defer pool.Stop() 210 211 // add peers 212 for peerID, peer := range peers { 213 pool.SetPeerRange(peerID, peer.base, peer.height, 0) 214 } 215 assert.EqualValues(t, 10, pool.MaxPeerHeight()) 216 217 // remove not-existing peer 218 assert.NotPanics(t, func() { pool.RemovePeer(p2p.ID("Superman")) }) 219 220 // remove peer with biggest height 221 pool.RemovePeer(p2p.ID("10")) 222 assert.EqualValues(t, 9, pool.MaxPeerHeight()) 223 224 // remove all peers 225 for peerID := range peers { 226 pool.RemovePeer(peerID) 227 } 228 229 assert.EqualValues(t, 0, pool.MaxPeerHeight()) 230 }