mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-25 18:51:39 +00:00
Initial commit
This commit is contained in:
287
blockchain_new/reactor_fsm_test.go
Normal file
287
blockchain_new/reactor_fsm_test.go
Normal file
@ -0,0 +1,287 @@
|
||||
package blockchain_new
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
failSendStatusRequest bool
|
||||
failSendBlockRequest bool
|
||||
numStatusRequests int
|
||||
numBlockRequests int
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// WIP
|
||||
func TestFSMTransitionSequences(t *testing.T) {
|
||||
maxRequestBatchSize = 2
|
||||
fsmTransitionSequenceTests := [][]fsmStepTestValues{
|
||||
{
|
||||
{currentState: "unknown", event: startFSMEv, shouldSendStatusReq: true,
|
||||
expectedState: "waitForPeer"},
|
||||
{currentState: "waitForPeer", event: statusResponseEv,
|
||||
data: bReactorEventData{peerId: "P1", height: 10},
|
||||
blockReqIncreased: true,
|
||||
expectedState: "waitForBlock"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range fsmTransitionSequenceTests {
|
||||
// Create and start the FSM
|
||||
testBcR := &testReactor{logger: log.TestingLogger()}
|
||||
blockDB := dbm.NewMemDB()
|
||||
store := NewBlockStore(blockDB)
|
||||
fsm := NewFSM(store, testBcR)
|
||||
fsm.setLogger(log.TestingLogger())
|
||||
resetTestValues()
|
||||
|
||||
// always start from unknown
|
||||
fsm.resetStateTimer(unknown)
|
||||
assert.Equal(t, 1, stateTimerStarts[unknown.name])
|
||||
|
||||
for _, step := range tt {
|
||||
assert.Equal(t, step.currentState, fsm.state.name)
|
||||
failSendStatusRequest = step.failStatusReq
|
||||
failSendBlockRequest = step.failBlockReq
|
||||
|
||||
oldNumStatusRequests := numStatusRequests
|
||||
oldNumBlockRequests := numBlockRequests
|
||||
|
||||
_ = sendEventToFSM(fsm, step.event, step.data)
|
||||
if step.shouldSendStatusReq {
|
||||
assert.Equal(t, oldNumStatusRequests+1, numStatusRequests)
|
||||
} else {
|
||||
assert.Equal(t, oldNumStatusRequests, numStatusRequests)
|
||||
}
|
||||
|
||||
if step.blockReqIncreased {
|
||||
assert.Equal(t, oldNumBlockRequests+maxRequestBatchSize, numBlockRequests)
|
||||
} else {
|
||||
assert.Equal(t, oldNumBlockRequests, numBlockRequests)
|
||||
}
|
||||
|
||||
assert.Equal(t, step.expectedState, fsm.state.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReactorFSMBasic(t *testing.T) {
|
||||
maxRequestBatchSize = 2
|
||||
|
||||
resetTestValues()
|
||||
// Create and start the FSM
|
||||
testBcR := &testReactor{logger: log.TestingLogger()}
|
||||
blockDB := dbm.NewMemDB()
|
||||
store := NewBlockStore(blockDB)
|
||||
fsm := NewFSM(store, testBcR)
|
||||
fsm.setLogger(log.TestingLogger())
|
||||
|
||||
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)
|
||||
|
||||
// Check that FSM sends a block request message and...
|
||||
assert.Equal(t, maxRequestBatchSize, numBlockRequests)
|
||||
// ... the block request has the expected height
|
||||
assert.Equal(t, int64(maxRequestBatchSize), lastBlockRequest.height)
|
||||
assert.Equal(t, waitForBlock.name, fsm.state.name)
|
||||
}
|
||||
|
||||
func TestReactorFSMPeerTimeout(t *testing.T) {
|
||||
maxRequestBatchSize = 2
|
||||
resetTestValues()
|
||||
peerTimeout = 20 * time.Millisecond
|
||||
// Create and start the FSM
|
||||
testBcR := &testReactor{logger: log.TestingLogger()}
|
||||
blockDB := dbm.NewMemDB()
|
||||
store := NewBlockStore(blockDB)
|
||||
fsm := NewFSM(store, testBcR)
|
||||
fsm.setLogger(log.TestingLogger())
|
||||
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)
|
||||
|
||||
// Check that FSM sends a block request message and...
|
||||
assert.Equal(t, maxRequestBatchSize, numBlockRequests)
|
||||
// ... the block request has the expected height and peer
|
||||
assert.Equal(t, int64(maxRequestBatchSize), lastBlockRequest.height)
|
||||
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() error {
|
||||
testR.logger.Info("Reactor received sendStatusRequest call from FSM")
|
||||
numStatusRequests++
|
||||
if failSendStatusRequest {
|
||||
return errSendQueueFull
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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) processBlocks(first *types.Block, second *types.Block) error {
|
||||
testR.logger.Info("Reactor received processBlocks call from FSM", "first", first.Height, "second", second.Height)
|
||||
return nil
|
||||
}
|
||||
|
||||
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),
|
||||
},
|
||||
}
|
||||
|
||||
sendMessageToFSM(fsm, msgData)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
Reference in New Issue
Block a user