- package v2
-
- import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/tendermint/tendermint/p2p"
- tmState "github.com/tendermint/tendermint/state"
- "github.com/tendermint/tendermint/types"
- )
-
- // pcBlock is a test helper structure with simple types. Its purpose is to help with test readability.
- type pcBlock struct {
- pid string
- height int64
- }
-
- // params is a test structure used to create processor state.
- type params struct {
- height int64
- items []pcBlock
- blocksSynced int
- verBL []int64
- appBL []int64
- draining bool
- }
-
- // makePcBlock makes an empty block.
- func makePcBlock(height int64) *types.Block {
- return &types.Block{Header: types.Header{Height: height}}
- }
-
- // makeState takes test parameters and creates a specific processor state.
- func makeState(p *params) *pcState {
- var (
- tmState = tmState.State{LastBlockHeight: p.height}
- context = newMockProcessorContext(tmState, p.verBL, p.appBL)
- )
- state := newPcState(context)
-
- for _, item := range p.items {
- state.enqueue(p2p.NodeID(item.pid), makePcBlock(item.height), item.height)
- }
-
- state.blocksSynced = p.blocksSynced
- state.draining = p.draining
- return state
- }
-
- func mBlockResponse(peerID p2p.NodeID, height int64) scBlockReceived {
- return scBlockReceived{
- peerID: peerID,
- block: makePcBlock(height),
- }
- }
-
- type pcFsmMakeStateValues struct {
- currentState *params
- event Event
- wantState *params
- wantNextEvent Event
- wantErr error
- wantPanic bool
- }
-
- type testFields struct {
- name string
- steps []pcFsmMakeStateValues
- }
-
- func executeProcessorTests(t *testing.T, tests []testFields) {
- for _, tt := range tests {
- tt := tt
- t.Run(tt.name, func(t *testing.T) {
- var state *pcState
- for _, step := range tt.steps {
- defer func() {
- r := recover()
- if (r != nil) != step.wantPanic {
- t.Errorf("recover = %v, wantPanic = %v", r, step.wantPanic)
- }
- }()
-
- // First step must always initialize the currentState as state.
- if step.currentState != nil {
- state = makeState(step.currentState)
- }
- if state == nil {
- panic("Bad (initial?) step")
- }
-
- nextEvent, err := state.handle(step.event)
- t.Log(state)
- assert.Equal(t, step.wantErr, err)
- assert.Equal(t, makeState(step.wantState), state)
- assert.Equal(t, step.wantNextEvent, nextEvent)
- // Next step may use the wantedState as their currentState.
- state = makeState(step.wantState)
- }
- })
- }
- }
-
- func TestRProcessPeerError(t *testing.T) {
- tests := []testFields{
- {
- name: "error for existing peer",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
- event: scPeerError{peerID: "P2"},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}}},
- wantNextEvent: noOp,
- },
- },
- },
- {
- name: "error for unknown peer",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
- event: scPeerError{peerID: "P3"},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
- wantNextEvent: noOp,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestPcBlockResponse(t *testing.T) {
- tests := []testFields{
- {
- name: "add one block",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{}, event: mBlockResponse("P1", 1),
- wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, wantNextEvent: noOp,
- },
- },
- },
-
- {
- name: "add two blocks",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{}, event: mBlockResponse("P1", 3),
- wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp,
- },
- { // use previous wantState as currentState,
- event: mBlockResponse("P1", 4),
- wantState: ¶ms{items: []pcBlock{{"P1", 3}, {"P1", 4}}}, wantNextEvent: noOp,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestRProcessBlockSuccess(t *testing.T) {
- tests := []testFields{
- {
- name: "noop - no blocks over current height",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{}, event: rProcessBlock{},
- wantState: ¶ms{}, wantNextEvent: noOp,
- },
- },
- },
- {
- name: "noop - high new blocks",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: rProcessBlock{},
- wantState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, wantNextEvent: noOp,
- },
- },
- },
- {
- name: "blocks H+1 and H+2 present",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: rProcessBlock{},
- wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}}, blocksSynced: 1},
- wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"},
- },
- },
- },
- {
- name: "blocks H+1 and H+2 present after draining",
- steps: []pcFsmMakeStateValues{
- { // some contiguous blocks - on stop check draining is set
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}},
- event: scFinishedEv{},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}, draining: true},
- wantNextEvent: noOp,
- },
- {
- event: rProcessBlock{},
- wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true},
- wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"},
- },
- { // finish when H+1 or/and H+2 are missing
- event: rProcessBlock{},
- wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true},
- wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 1}, blocksSynced: 1},
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestRProcessBlockFailures(t *testing.T) {
- tests := []testFields{
- {
- name: "blocks H+1 and H+2 present from different peers - H+1 verification fails ",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: rProcessBlock{},
- wantState: ¶ms{items: []pcBlock{}, verBL: []int64{1}},
- wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P2"},
- },
- },
- },
- {
- name: "blocks H+1 and H+2 present from same peer - H+1 applyBlock fails ",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: rProcessBlock{},
- wantState: ¶ms{items: []pcBlock{}, appBL: []int64{1}}, wantPanic: true,
- },
- },
- },
- {
- name: "blocks H+1 and H+2 present from same peers - H+1 verification fails ",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 0, items: []pcBlock{{"P1", 1}, {"P1", 2}, {"P2", 3}},
- verBL: []int64{1}}, event: rProcessBlock{},
- wantState: ¶ms{height: 0, items: []pcBlock{{"P2", 3}}, verBL: []int64{1}},
- wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P1"},
- },
- },
- },
- {
- name: "blocks H+1 and H+2 present from different peers - H+1 applyBlock fails ",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P2", 3}}, appBL: []int64{1}},
- event: rProcessBlock{},
- wantState: ¶ms{items: []pcBlock{{"P2", 3}}, appBL: []int64{1}}, wantPanic: true,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestScFinishedEv(t *testing.T) {
- tests := []testFields{
- {
- name: "no blocks",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: scFinishedEv{},
- wantState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100},
- wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100},
- },
- },
- },
- {
- name: "maxHeight+1 block present",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 100, items: []pcBlock{
- {"P1", 101}}, blocksSynced: 100}, event: scFinishedEv{},
- wantState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100},
- wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100},
- },
- },
- },
- {
- name: "more blocks present",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 100, items: []pcBlock{
- {"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: scFinishedEv{},
- wantState: ¶ms{height: 100, items: []pcBlock{
- {"P1", 101}, {"P1", 102}}, blocksSynced: 100, draining: true},
- wantNextEvent: noOp,
- wantErr: nil,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
|