package v1
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
tmrand "github.com/tendermint/tendermint/libs/rand"
|
|
"github.com/tendermint/tendermint/p2p"
|
|
"github.com/tendermint/tendermint/types"
|
|
)
|
|
|
|
func TestPeerMonitor(t *testing.T) {
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 10,
|
|
func(err error, _ p2p.ID) {},
|
|
nil)
|
|
peer.SetLogger(log.TestingLogger())
|
|
peer.startMonitor()
|
|
assert.NotNil(t, peer.recvMonitor)
|
|
peer.stopMonitor()
|
|
assert.Nil(t, peer.recvMonitor)
|
|
}
|
|
|
|
func TestPeerResetBlockResponseTimer(t *testing.T) {
|
|
var (
|
|
numErrFuncCalls int // number of calls to the errFunc
|
|
lastErr error // last generated error
|
|
peerTestMtx sync.Mutex // modifications of ^^ variables are also done from timer handler goroutine
|
|
)
|
|
params := &BpPeerParams{timeout: 20 * time.Millisecond}
|
|
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 10,
|
|
func(err error, _ p2p.ID) {
|
|
peerTestMtx.Lock()
|
|
defer peerTestMtx.Unlock()
|
|
lastErr = err
|
|
numErrFuncCalls++
|
|
},
|
|
params)
|
|
|
|
peer.SetLogger(log.TestingLogger())
|
|
checkByStoppingPeerTimer(t, peer, false)
|
|
|
|
// initial reset call with peer having a nil timer
|
|
peer.resetBlockResponseTimer()
|
|
assert.NotNil(t, peer.blockResponseTimer)
|
|
// make sure timer is running and stop it
|
|
checkByStoppingPeerTimer(t, peer, true)
|
|
|
|
// reset with running timer
|
|
peer.resetBlockResponseTimer()
|
|
time.Sleep(5 * time.Millisecond)
|
|
peer.resetBlockResponseTimer()
|
|
assert.NotNil(t, peer.blockResponseTimer)
|
|
|
|
// let the timer expire and ...
|
|
time.Sleep(50 * time.Millisecond)
|
|
// ... check timer is not running
|
|
checkByStoppingPeerTimer(t, peer, false)
|
|
|
|
peerTestMtx.Lock()
|
|
// ... check errNoPeerResponse has been sent
|
|
assert.Equal(t, 1, numErrFuncCalls)
|
|
assert.Equal(t, lastErr, errNoPeerResponse)
|
|
peerTestMtx.Unlock()
|
|
}
|
|
|
|
func TestPeerRequestSent(t *testing.T) {
|
|
params := &BpPeerParams{timeout: 2 * time.Millisecond}
|
|
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 10,
|
|
func(err error, _ p2p.ID) {},
|
|
params)
|
|
|
|
peer.SetLogger(log.TestingLogger())
|
|
|
|
peer.RequestSent(1)
|
|
assert.NotNil(t, peer.recvMonitor)
|
|
assert.NotNil(t, peer.blockResponseTimer)
|
|
assert.Equal(t, 1, peer.NumPendingBlockRequests)
|
|
|
|
peer.RequestSent(1)
|
|
assert.NotNil(t, peer.recvMonitor)
|
|
assert.NotNil(t, peer.blockResponseTimer)
|
|
assert.Equal(t, 2, peer.NumPendingBlockRequests)
|
|
}
|
|
|
|
func TestPeerGetAndRemoveBlock(t *testing.T) {
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 100,
|
|
func(err error, _ p2p.ID) {},
|
|
nil)
|
|
|
|
// Change peer height
|
|
peer.Height = int64(10)
|
|
assert.Equal(t, int64(10), peer.Height)
|
|
|
|
// request some blocks and receive few of them
|
|
for i := 1; i <= 10; i++ {
|
|
peer.RequestSent(int64(i))
|
|
if i > 5 {
|
|
// only receive blocks 1..5
|
|
continue
|
|
}
|
|
_ = peer.AddBlock(makeSmallBlock(i), 10)
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
height int64
|
|
wantErr error
|
|
blockPresent bool
|
|
}{
|
|
{"no request", 100, errMissingBlock, false},
|
|
{"no block", 6, errMissingBlock, false},
|
|
{"block 1 present", 1, nil, true},
|
|
{"block max present", 5, nil, true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// try to get the block
|
|
b, err := peer.BlockAtHeight(tt.height)
|
|
assert.Equal(t, tt.wantErr, err)
|
|
assert.Equal(t, tt.blockPresent, b != nil)
|
|
|
|
// remove the block
|
|
peer.RemoveBlock(tt.height)
|
|
_, err = peer.BlockAtHeight(tt.height)
|
|
assert.Equal(t, errMissingBlock, err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPeerAddBlock(t *testing.T) {
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 100,
|
|
func(err error, _ p2p.ID) {},
|
|
nil)
|
|
|
|
// request some blocks, receive one
|
|
for i := 1; i <= 10; i++ {
|
|
peer.RequestSent(int64(i))
|
|
if i == 5 {
|
|
// receive block 5
|
|
_ = peer.AddBlock(makeSmallBlock(i), 10)
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
height int64
|
|
wantErr error
|
|
blockPresent bool
|
|
}{
|
|
{"no request", 50, errMissingBlock, false},
|
|
{"duplicate block", 5, errDuplicateBlock, true},
|
|
{"block 1 successfully received", 1, nil, true},
|
|
{"block max successfully received", 10, nil, true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// try to get the block
|
|
err := peer.AddBlock(makeSmallBlock(int(tt.height)), 10)
|
|
assert.Equal(t, tt.wantErr, err)
|
|
_, err = peer.BlockAtHeight(tt.height)
|
|
assert.Equal(t, tt.blockPresent, err == nil)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPeerOnErrFuncCalledDueToExpiration(t *testing.T) {
|
|
|
|
params := &BpPeerParams{timeout: 10 * time.Millisecond}
|
|
var (
|
|
numErrFuncCalls int // number of calls to the onErr function
|
|
lastErr error // last generated error
|
|
peerTestMtx sync.Mutex // modifications of ^^ variables are also done from timer handler goroutine
|
|
)
|
|
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 10,
|
|
func(err error, _ p2p.ID) {
|
|
peerTestMtx.Lock()
|
|
defer peerTestMtx.Unlock()
|
|
lastErr = err
|
|
numErrFuncCalls++
|
|
},
|
|
params)
|
|
|
|
peer.SetLogger(log.TestingLogger())
|
|
|
|
peer.RequestSent(1)
|
|
time.Sleep(50 * time.Millisecond)
|
|
// timer should have expired by now, check that the on error function was called
|
|
peerTestMtx.Lock()
|
|
assert.Equal(t, 1, numErrFuncCalls)
|
|
assert.Equal(t, errNoPeerResponse, lastErr)
|
|
peerTestMtx.Unlock()
|
|
}
|
|
|
|
func TestPeerCheckRate(t *testing.T) {
|
|
params := &BpPeerParams{
|
|
timeout: time.Second,
|
|
minRecvRate: int64(100), // 100 bytes/sec exponential moving average
|
|
}
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 10,
|
|
func(err error, _ p2p.ID) {},
|
|
params)
|
|
peer.SetLogger(log.TestingLogger())
|
|
|
|
require.Nil(t, peer.CheckRate())
|
|
|
|
for i := 0; i < 40; i++ {
|
|
peer.RequestSent(int64(i))
|
|
}
|
|
|
|
// monitor starts with a higher rEMA (~ 2*minRecvRate), wait for it to go down
|
|
time.Sleep(900 * time.Millisecond)
|
|
|
|
// normal peer - send a bit more than 100 bytes/sec, > 10 bytes/100msec, check peer is not considered slow
|
|
for i := 0; i < 10; i++ {
|
|
_ = peer.AddBlock(makeSmallBlock(i), 11)
|
|
time.Sleep(100 * time.Millisecond)
|
|
require.Nil(t, peer.CheckRate())
|
|
}
|
|
|
|
// slow peer - send a bit less than 10 bytes/100msec
|
|
for i := 10; i < 20; i++ {
|
|
_ = peer.AddBlock(makeSmallBlock(i), 9)
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
// check peer is considered slow
|
|
assert.Equal(t, errSlowPeer, peer.CheckRate())
|
|
}
|
|
|
|
func TestPeerCleanup(t *testing.T) {
|
|
params := &BpPeerParams{timeout: 2 * time.Millisecond}
|
|
|
|
peer := NewBpPeer(
|
|
p2p.ID(tmrand.Str(12)), 0, 10,
|
|
func(err error, _ p2p.ID) {},
|
|
params)
|
|
peer.SetLogger(log.TestingLogger())
|
|
|
|
assert.Nil(t, peer.blockResponseTimer)
|
|
peer.RequestSent(1)
|
|
assert.NotNil(t, peer.blockResponseTimer)
|
|
|
|
peer.Cleanup()
|
|
checkByStoppingPeerTimer(t, peer, false)
|
|
}
|
|
|
|
// Check if peer timer is running or not (a running timer can be successfully stopped).
|
|
// Note: stops the timer.
|
|
func checkByStoppingPeerTimer(t *testing.T, peer *BpPeer, running bool) {
|
|
assert.NotPanics(t, func() {
|
|
stopped := peer.stopBlockResponseTimer()
|
|
if running {
|
|
assert.True(t, stopped)
|
|
} else {
|
|
assert.False(t, stopped)
|
|
}
|
|
})
|
|
}
|
|
|
|
func makeSmallBlock(height int) *types.Block {
|
|
return types.MakeBlock(int64(height), []types.Tx{types.Tx("foo")}, nil, nil)
|
|
}
|