|
|
- package v2
-
- import (
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/tendermint/tendermint/p2p"
- tdState "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 int64
- 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 (
- tdState = tdState.State{}
- context = newMockProcessorContext(p.verBL, p.appBL)
- )
- state := newPcState(p.height, tdState, "test", context)
-
- for _, item := range p.items {
- _ = state.enqueue(p2p.ID(item.pid), makePcBlock(item.height), item.height)
- }
-
- state.blocksSynced = p.blocksSynced
- state.draining = p.draining
- return state
- }
-
- func mBlockResponse(peerID p2p.ID, height int64) *bcBlockResponse {
- return &bcBlockResponse{
- peerID: peerID,
- block: makePcBlock(height),
- height: 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 initialise 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 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,
- },
- },
- },
- {
- name: "add duplicate block from same peer",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{}, event: mBlockResponse("P1", 3),
- wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp,
- },
- { // use previous wantState as currentState,
- event: mBlockResponse("P1", 3),
- wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcDuplicateBlock{},
- },
- },
- },
- {
- name: "add duplicate block from different peer",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{}, event: mBlockResponse("P1", 3),
- wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp,
- },
- { // use previous wantState as currentState,
- event: mBlockResponse("P2", 3),
- wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcDuplicateBlock{},
- },
- },
- },
- {
- name: "attempt to add block with height equal to state.height",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, event: mBlockResponse("P1", 2),
- wantState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcShortBlock{},
- },
- },
- },
- {
- name: "attempt to add block with height smaller than state.height",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, event: mBlockResponse("P1", 1),
- wantState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcShortBlock{},
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestPcProcessBlockSuccess(t *testing.T) {
- tests := []testFields{
- {
- name: "noop - no blocks over current height",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{}, event: pcProcessBlock{},
- wantState: ¶ms{}, wantNextEvent: noOp,
- },
- },
- },
- {
- name: "noop - high new blocks",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: pcProcessBlock{},
- 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: pcProcessBlock{},
- 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: pcStop{},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}, draining: true},
- wantNextEvent: noOp,
- },
- {
- event: pcProcessBlock{},
- 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: pcProcessBlock{},
- wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true},
- wantNextEvent: noOp,
- wantErr: pcFinished{height: 1},
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestPcProcessBlockFailures(t *testing.T) {
- tests := []testFields{
- {
- name: "blocks H+1 and H+2 present - H+1 verification fails ",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: pcProcessBlock{},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}},
- wantNextEvent: pcBlockVerificationFailure{peerID: "P1", height: 1},
- },
- },
- },
- {
- name: "blocks H+1 and H+2 present - H+1 applyBlock fails ",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: pcProcessBlock{},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, wantPanic: true,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestPcPeerError(t *testing.T) {
- tests := []testFields{
- {
- name: "peer not present",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: &peerError{peerID: "P3"},
- wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
- wantNextEvent: noOp,
- },
- },
- },
- {
- name: "some blocks are from errored peer",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 100}, {"P1", 99}, {"P2", 101}}}, event: &peerError{peerID: "P1"},
- wantState: ¶ms{items: []pcBlock{{"P2", 101}}},
- wantNextEvent: noOp,
- },
- },
- },
- {
- name: "all blocks are from errored peer",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{items: []pcBlock{{"P1", 100}, {"P1", 99}}}, event: &peerError{peerID: "P1"},
- wantState: ¶ms{},
- wantNextEvent: noOp,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
-
- func TestStop(t *testing.T) {
- tests := []testFields{
- {
- name: "no blocks",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: pcStop{},
- wantState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100},
- wantNextEvent: noOp,
- wantErr: pcFinished{height: 100, blocksSynced: 100},
- },
- },
- },
- {
- name: "maxHeight+1 block present",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100}, event: pcStop{},
- wantState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100},
- wantNextEvent: noOp,
- wantErr: pcFinished{height: 100, blocksSynced: 100},
- },
- },
- },
- {
- name: "more blocks present",
- steps: []pcFsmMakeStateValues{
- {
- currentState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: pcStop{},
- wantState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}, {"P1", 102}}, blocksSynced: 100, draining: true},
- wantNextEvent: noOp,
- wantErr: nil,
- },
- },
- },
- }
-
- executeProcessorTests(t, tests)
- }
|