tendermint/blockchain_new/reactor_fsm_test.go

282 lines
7.5 KiB
Go
Raw Normal View History

2019-03-26 09:58:30 +01:00
package blockchain_new
import (
"testing"
"time"
2019-03-26 09:58:30 +01:00
"github.com/stretchr/testify/assert"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
)
var (
failSendStatusRequest bool
failSendBlockRequest bool
numStatusRequests int
numBlockRequests int32
2019-03-26 09:58:30 +01:00
)
type lastBlockRequestT struct {
peerID p2p.ID
height int64
}
var lastBlockRequest lastBlockRequestT
type lastPeerErrorT struct {
peerID p2p.ID
err error
}
var lastPeerError lastPeerErrorT
var stateTimerStarts map[string]int
func resetTestValues() {
stateTimerStarts = make(map[string]int)
failSendBlockRequest = false
failSendStatusRequest = false
numStatusRequests = 0
numBlockRequests = 0
lastBlockRequest.peerID = ""
lastBlockRequest.height = 0
lastPeerError.peerID = ""
lastPeerError.err = nil
}
type fsmStepTestValues struct {
currentState string
event bReactorEvent
expectedState string
// input
failStatusReq bool
shouldSendStatusReq bool
failBlockReq bool
blockReqIncreased bool
data bReactorEventData
expectedLastBlockReq *lastBlockRequestT
}
2019-03-26 14:51:37 +01:00
func newTestReactor() *testReactor {
2019-03-26 22:43:50 +01:00
testBcR := &testReactor{logger: log.TestingLogger()}
2019-03-27 14:33:11 +01:00
testBcR.fsm = NewFSM(1, testBcR)
2019-03-26 22:43:50 +01:00
testBcR.fsm.setLogger(testBcR.logger)
2019-03-26 14:51:37 +01:00
return testBcR
}
2019-03-26 09:58:30 +01:00
// WIP
func TestFSMTransitionSequences(t *testing.T) {
maxRequestsPerPeer = 2
2019-03-26 09:58:30 +01:00
fsmTransitionSequenceTests := [][]fsmStepTestValues{
{
{currentState: "unknown", event: startFSMEv, shouldSendStatusReq: true,
expectedState: "waitForPeer"},
{currentState: "waitForPeer", event: statusResponseEv,
data: bReactorEventData{peerId: "P1", height: 10},
expectedState: "waitForBlock"},
{currentState: "waitForBlock", event: makeRequestsEv,
data: bReactorEventData{maxNumRequests: maxNumPendingRequests},
2019-03-26 09:58:30 +01:00
blockReqIncreased: true,
expectedState: "waitForBlock"},
},
}
for _, tt := range fsmTransitionSequenceTests {
2019-03-26 14:51:37 +01:00
// Create test reactor
testBcR := newTestReactor()
2019-03-26 09:58:30 +01:00
resetTestValues()
for _, step := range tt {
2019-03-26 14:51:37 +01:00
assert.Equal(t, step.currentState, testBcR.fsm.state.name)
2019-03-26 09:58:30 +01:00
failSendStatusRequest = step.failStatusReq
failSendBlockRequest = step.failBlockReq
oldNumStatusRequests := numStatusRequests
oldNumBlockRequests := numBlockRequests
2019-03-26 14:51:37 +01:00
_ = sendEventToFSM(testBcR.fsm, step.event, step.data)
2019-03-26 09:58:30 +01:00
if step.shouldSendStatusReq {
assert.Equal(t, oldNumStatusRequests+1, numStatusRequests)
} else {
assert.Equal(t, oldNumStatusRequests, numStatusRequests)
}
if step.blockReqIncreased {
assert.Equal(t, oldNumBlockRequests+maxRequestsPerPeer, numBlockRequests)
2019-03-26 09:58:30 +01:00
} else {
assert.Equal(t, oldNumBlockRequests, numBlockRequests)
}
2019-03-26 14:51:37 +01:00
assert.Equal(t, step.expectedState, testBcR.fsm.state.name)
2019-03-26 09:58:30 +01:00
}
}
}
func TestReactorFSMBasic(t *testing.T) {
maxRequestsPerPeer = 2
2019-03-26 09:58:30 +01:00
2019-03-26 14:51:37 +01:00
// Create test reactor
testBcR := newTestReactor()
2019-03-26 09:58:30 +01:00
resetTestValues()
2019-03-26 14:51:37 +01:00
fsm := testBcR.fsm
2019-03-26 09:58:30 +01:00
if err := fsm.handle(&bReactorMessageData{event: startFSMEv}); err != nil {
}
// Check that FSM sends a status request message
assert.Equal(t, 1, numStatusRequests)
assert.Equal(t, waitForPeer.name, fsm.state.name)
// Send a status response message to FSM
peerID := p2p.ID(cmn.RandStr(12))
sendStatusResponse2(fsm, peerID, 10)
if err := fsm.handle(&bReactorMessageData{
event: makeRequestsEv,
data: bReactorEventData{maxNumRequests: maxNumPendingRequests}}); err != nil {
}
2019-03-26 09:58:30 +01:00
// Check that FSM sends a block request message and...
assert.Equal(t, maxRequestsPerPeer, numBlockRequests)
2019-03-26 09:58:30 +01:00
// ... the block request has the expected height
assert.Equal(t, maxRequestsPerPeer, int32(len(fsm.pool.peers[peerID].blocks)))
2019-03-26 09:58:30 +01:00
assert.Equal(t, waitForBlock.name, fsm.state.name)
}
func TestReactorFSMPeerTimeout(t *testing.T) {
maxRequestsPerPeer = 2
2019-03-26 09:58:30 +01:00
resetTestValues()
peerTimeout = 20 * time.Millisecond
// Create and start the FSM
2019-03-26 14:51:37 +01:00
testBcR := newTestReactor()
fsm := testBcR.fsm
2019-03-26 09:58:30 +01:00
fsm.start()
// Check that FSM sends a status request message
time.Sleep(time.Millisecond)
assert.Equal(t, 1, numStatusRequests)
// Send a status response message to FSM
peerID := p2p.ID(cmn.RandStr(12))
sendStatusResponse(fsm, peerID, 10)
time.Sleep(5 * time.Millisecond)
if err := fsm.handle(&bReactorMessageData{
event: makeRequestsEv,
data: bReactorEventData{maxNumRequests: maxNumPendingRequests}}); err != nil {
}
2019-03-26 09:58:30 +01:00
// Check that FSM sends a block request message and...
assert.Equal(t, maxRequestsPerPeer, numBlockRequests)
2019-03-26 09:58:30 +01:00
// ... the block request has the expected height and peer
assert.Equal(t, maxRequestsPerPeer, int32(len(fsm.pool.peers[peerID].blocks)))
2019-03-26 09:58:30 +01:00
assert.Equal(t, peerID, lastBlockRequest.peerID)
// let FSM timeout on the block response message
time.Sleep(100 * time.Millisecond)
}
// reactor for FSM testing
type testReactor struct {
logger log.Logger
fsm *bReactorFSM
testCh chan bReactorMessageData
}
func sendEventToFSM(fsm *bReactorFSM, ev bReactorEvent, data bReactorEventData) error {
return fsm.handle(&bReactorMessageData{event: ev, data: data})
}
// ----------------------------------------
// implementation for the test reactor APIs
func (testR *testReactor) sendPeerError(err error, peerID p2p.ID) {
testR.logger.Info("Reactor received sendPeerError call from FSM", "peer", peerID, "err", err)
lastPeerError.peerID = peerID
lastPeerError.err = err
}
func (testR *testReactor) sendStatusRequest() {
2019-03-26 09:58:30 +01:00
testR.logger.Info("Reactor received sendStatusRequest call from FSM")
numStatusRequests++
}
func (testR *testReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
testR.logger.Info("Reactor received sendBlockRequest call from FSM", "peer", peerID, "height", height)
numBlockRequests++
lastBlockRequest.peerID = peerID
lastBlockRequest.height = height
return nil
}
func (testR *testReactor) resetStateTimer(name string, timer *time.Timer, timeout time.Duration, f func()) {
testR.logger.Info("Reactor received resetStateTimer call from FSM", "state", name, "timeout", timeout)
if _, ok := stateTimerStarts[name]; !ok {
stateTimerStarts[name] = 1
} else {
stateTimerStarts[name]++
}
}
func (testR *testReactor) switchToConsensus() {
testR.logger.Info("Reactor received switchToConsensus call from FSM")
}
// ----------------------------------------
// -------------------------------------------------------
// helper functions for tests to simulate different events
func sendStatusResponse(fsm *bReactorFSM, peerID p2p.ID, height int64) {
msgBytes := makeStatusResponseMessage(height)
msgData := bReactorMessageData{
event: statusResponseEv,
data: bReactorEventData{
peerId: peerID,
height: height,
length: len(msgBytes),
},
}
_ = sendMessageToFSMSync(fsm, msgData)
2019-03-26 09:58:30 +01:00
}
func sendStatusResponse2(fsm *bReactorFSM, peerID p2p.ID, height int64) {
msgBytes := makeStatusResponseMessage(height)
msgData := &bReactorMessageData{
event: statusResponseEv,
data: bReactorEventData{
peerId: peerID,
height: height,
length: len(msgBytes),
},
}
_ = fsm.handle(msgData)
}
func sendStateTimeout(fsm *bReactorFSM, name string) {
msgData := &bReactorMessageData{
event: stateTimeoutEv,
data: bReactorEventData{
stateName: name,
},
}
_ = fsm.handle(msgData)
}
// -------------------------------------------------------
// ----------------------------------------------------
// helper functions to make blockchain reactor messages
func makeStatusResponseMessage(height int64) []byte {
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{height})
return msgBytes
}
// ----------------------------------------------------