2019-06-19 15:18:08 +02:00
package v0
2015-03-22 12:46:53 -07:00
import (
2019-06-18 11:30:27 +02:00
"fmt"
2015-03-22 12:46:53 -07:00
"testing"
2015-03-22 19:20:54 -07:00
"time"
2015-03-22 12:46:53 -07:00
2019-03-20 03:59:33 +03:00
"github.com/stretchr/testify/assert"
2019-06-18 11:30:27 +02:00
"github.com/stretchr/testify/require"
2019-03-20 03:59:33 +03:00
2019-06-18 11:30:27 +02:00
cmn "github.com/tendermint/tendermint/libs/common"
2018-07-01 22:36:49 -04:00
"github.com/tendermint/tendermint/libs/log"
2018-01-03 11:29:19 +01:00
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/types"
2015-03-22 12:46:53 -07:00
)
2019-06-18 11:30:27 +02:00
func init ( ) {
peerTimeout = 2 * time . Second
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
type testPeer struct {
id p2p . ID
height int64
inputChan chan inputData //make sure each peer's data is sequential
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
type inputData struct {
t * testing . T
pool * BlockPool
request BlockRequest
2015-03-22 12:46:53 -07:00
}
2019-06-18 11:30:27 +02:00
func ( p testPeer ) runInputRoutine ( ) {
go func ( ) {
for input := range p . inputChan {
p . simulateInput ( input )
2019-04-28 22:03:26 -04:00
}
2019-06-18 11:30:27 +02:00
} ( )
2019-04-13 09:23:43 -04:00
}
2017-09-06 11:50:43 -04:00
2019-06-18 11:30:27 +02:00
// Request desired, pretend like we got the block immediately.
func ( p testPeer ) simulateInput ( input inputData ) {
block := & types . Block { Header : types . Header { Height : input . request . Height } }
input . pool . AddBlock ( input . request . PeerID , block , 123 )
// TODO: uncommenting this creates a race which is detected by: https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856
// see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890
// input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height)
2019-04-13 09:23:43 -04:00
}
2017-09-06 11:50:43 -04:00
2019-06-18 11:30:27 +02:00
type testPeers map [ p2p . ID ] testPeer
2015-03-22 12:46:53 -07:00
2019-06-18 11:30:27 +02:00
func ( ps testPeers ) start ( ) {
for _ , v := range ps {
v . runInputRoutine ( )
2015-03-22 12:46:53 -07:00
}
2015-03-22 16:23:24 -07:00
}
2019-06-18 11:30:27 +02:00
func ( ps testPeers ) stop ( ) {
for _ , v := range ps {
close ( v . inputChan )
2019-04-13 09:23:43 -04:00
}
}
2019-06-18 11:30:27 +02:00
func makePeers ( numPeers int , minHeight , maxHeight int64 ) testPeers {
peers := make ( testPeers , numPeers )
for i := 0 ; i < numPeers ; i ++ {
peerID := p2p . ID ( cmn . RandStr ( 12 ) )
height := minHeight + cmn . RandInt63n ( maxHeight - minHeight )
peers [ peerID ] = testPeer { peerID , height , make ( chan inputData , 10 ) }
2015-03-22 16:23:24 -07:00
}
2019-06-18 11:30:27 +02:00
return peers
2015-03-22 12:46:53 -07:00
}
2019-03-20 03:59:33 +03:00
2019-06-18 11:30:27 +02:00
func TestBlockPoolBasic ( t * testing . T ) {
start := int64 ( 42 )
peers := makePeers ( 10 , start + 1 , 1000 )
errorsCh := make ( chan peerError , 1000 )
requestsCh := make ( chan BlockRequest , 1000 )
pool := NewBlockPool ( start , requestsCh , errorsCh )
pool . SetLogger ( log . TestingLogger ( ) )
2019-04-28 22:03:26 -04:00
2019-06-18 11:30:27 +02:00
err := pool . Start ( )
if err != nil {
t . Error ( err )
2019-03-20 03:59:33 +03:00
}
2019-06-18 11:30:27 +02:00
defer pool . Stop ( )
peers . start ( )
defer peers . stop ( )
2019-04-13 09:23:43 -04:00
2019-06-18 11:30:27 +02:00
// Introduce each peer.
go func ( ) {
for _ , peer := range peers {
pool . SetPeerHeight ( peer . id , peer . height )
}
} ( )
2019-04-13 09:23:43 -04:00
2019-06-18 11:30:27 +02:00
// Start a goroutine to pull blocks
go func ( ) {
for {
if ! pool . IsRunning ( ) {
return
}
first , second := pool . PeekTwoBlocks ( )
if first != nil && second != nil {
pool . PopRequest ( )
} else {
time . Sleep ( 1 * time . Second )
}
}
} ( )
// Pull from channels
for {
select {
case err := <- errorsCh :
t . Error ( err )
case request := <- requestsCh :
t . Logf ( "Pulled new BlockRequest %v" , request )
if request . Height == 300 {
return // Done!
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
peers [ request . PeerID ] . inputChan <- inputData { t , pool , request }
}
2019-03-20 03:59:33 +03:00
}
2019-04-13 09:23:43 -04:00
}
2019-03-20 03:59:33 +03:00
2019-06-18 11:30:27 +02:00
func TestBlockPoolTimeout ( t * testing . T ) {
start := int64 ( 42 )
peers := makePeers ( 10 , start + 1 , 1000 )
errorsCh := make ( chan peerError , 1000 )
requestsCh := make ( chan BlockRequest , 1000 )
pool := NewBlockPool ( start , requestsCh , errorsCh )
pool . SetLogger ( log . TestingLogger ( ) )
err := pool . Start ( )
if err != nil {
t . Error ( err )
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
defer pool . Stop ( )
2019-03-20 03:59:33 +03:00
2019-06-18 11:30:27 +02:00
for _ , peer := range peers {
t . Logf ( "Peer %v" , peer . id )
2019-03-20 03:59:33 +03:00
}
2019-06-18 11:30:27 +02:00
// Introduce each peer.
go func ( ) {
for _ , peer := range peers {
pool . SetPeerHeight ( peer . id , peer . height )
}
} ( )
2019-04-13 09:23:43 -04:00
2019-06-18 11:30:27 +02:00
// Start a goroutine to pull blocks
go func ( ) {
for {
if ! pool . IsRunning ( ) {
return
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
first , second := pool . PeekTwoBlocks ( )
if first != nil && second != nil {
pool . PopRequest ( )
} else {
time . Sleep ( 1 * time . Second )
}
}
} ( )
// Pull from channels
counter := 0
timedOut := map [ p2p . ID ] struct { } { }
for {
select {
case err := <- errorsCh :
t . Log ( err )
// consider error to be always timeout here
if _ , ok := timedOut [ err . peerID ] ; ! ok {
counter ++
if counter == len ( peers ) {
return // Done!
}
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
case request := <- requestsCh :
t . Logf ( "Pulled new BlockRequest %+v" , request )
}
2019-04-13 09:23:43 -04:00
}
}
2019-06-18 11:30:27 +02:00
func TestBlockPoolRemovePeer ( t * testing . T ) {
peers := make ( testPeers , 10 )
for i := 0 ; i < 10 ; i ++ {
peerID := p2p . ID ( fmt . Sprintf ( "%d" , i + 1 ) )
height := int64 ( i + 1 )
peers [ peerID ] = testPeer { peerID , height , make ( chan inputData ) }
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
requestsCh := make ( chan BlockRequest )
errorsCh := make ( chan peerError )
2019-04-13 09:23:43 -04:00
2019-06-18 11:30:27 +02:00
pool := NewBlockPool ( 1 , requestsCh , errorsCh )
pool . SetLogger ( log . TestingLogger ( ) )
err := pool . Start ( )
require . NoError ( t , err )
defer pool . Stop ( )
2019-04-13 09:23:43 -04:00
2019-06-18 11:30:27 +02:00
// add peers
for peerID , peer := range peers {
pool . SetPeerHeight ( peerID , peer . height )
2019-04-28 22:03:26 -04:00
}
2019-06-18 11:30:27 +02:00
assert . EqualValues ( t , 10 , pool . MaxPeerHeight ( ) )
2019-04-28 22:03:26 -04:00
2019-06-18 11:30:27 +02:00
// remove not-existing peer
assert . NotPanics ( t , func ( ) { pool . RemovePeer ( p2p . ID ( "Superman" ) ) } )
2019-04-28 22:03:26 -04:00
2019-06-18 11:30:27 +02:00
// remove peer with biggest height
pool . RemovePeer ( p2p . ID ( "10" ) )
assert . EqualValues ( t , 9 , pool . MaxPeerHeight ( ) )
2019-04-28 22:03:26 -04:00
2019-06-18 11:30:27 +02:00
// remove all peers
for peerID := range peers {
pool . RemovePeer ( peerID )
2019-04-13 09:23:43 -04:00
}
2019-06-18 11:30:27 +02:00
assert . EqualValues ( t , 0 , pool . MaxPeerHeight ( ) )
2019-03-20 03:59:33 +03:00
}