mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-05 04:55:18 +00:00
add another test and port internal test factory
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log/term"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"path"
|
||||
@@ -462,12 +463,16 @@ func loadPrivValidator(config *cfg.Config) *privval.FilePV {
|
||||
}
|
||||
|
||||
func randState(nValidators int) (*State, []*validatorStub) {
|
||||
return randStateWithApp(nValidators, counter.NewApplication(true))
|
||||
}
|
||||
|
||||
func randStateWithApp(nValidators int, app abci.Application) (*State, []*validatorStub) {
|
||||
// Get State
|
||||
state, privVals := randGenesisState(nValidators, false, 10)
|
||||
|
||||
vss := make([]*validatorStub, nValidators)
|
||||
|
||||
cs := newState(state, privVals[0], counter.NewApplication(true))
|
||||
cs := newState(state, privVals[0], app)
|
||||
|
||||
for i := 0; i < nValidators; i++ {
|
||||
vss[i] = newValidatorStub(privVals[i], int32(i))
|
||||
@@ -682,6 +687,38 @@ func ensureVote(voteCh <-chan tmpubsub.Message, height int64, round int32,
|
||||
}
|
||||
}
|
||||
|
||||
func ensurePrevoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, hash []byte) {
|
||||
t.Helper()
|
||||
ensureVoteMatch(t, voteCh, height, round, hash, tmproto.PrevoteType)
|
||||
}
|
||||
|
||||
func ensurePrecommitMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, hash []byte) {
|
||||
t.Helper()
|
||||
ensureVoteMatch(t, voteCh, height, round, hash, tmproto.PrecommitType)
|
||||
}
|
||||
|
||||
func ensureVoteMatch(t *testing.T, voteCh <-chan tmpubsub.Message, height int64, round int32, hash []byte, voteType tmproto.SignedMsgType) {
|
||||
t.Helper()
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
t.Fatal("Timeout expired while waiting for NewVote event")
|
||||
case msg := <-voteCh:
|
||||
voteEvent, ok := msg.Data().(types.EventDataVote)
|
||||
require.True(t, ok, "expected a EventDataVote, got %T. Wrong subscription channel?",
|
||||
msg.Data())
|
||||
|
||||
vote := voteEvent.Vote
|
||||
assert.Equal(t, height, vote.Height, "expected height %d, but got %d", height, vote.Height)
|
||||
assert.Equal(t, round, vote.Round, "expected round %d, but got %d", round, vote.Round)
|
||||
assert.Equal(t, voteType, vote.Type, "expected type %s, but got %s", voteType, vote.Type)
|
||||
if hash == nil {
|
||||
require.Nil(t, vote.BlockID.Hash, "Expected prevote to be for nil, got %X", vote.BlockID.Hash)
|
||||
} else {
|
||||
require.True(t, bytes.Equal(vote.BlockID.Hash, hash), "Expected prevote to be for %X, got %X", hash, vote.BlockID.Hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensurePrecommitTimeout(ch <-chan tmpubsub.Message) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
|
||||
@@ -8,11 +8,15 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/counter"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
abcimocks "github.com/tendermint/tendermint/abci/types/mocks"
|
||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
@@ -1368,6 +1372,55 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) {
|
||||
assert.True(t, rs.ValidRound == round)
|
||||
}
|
||||
|
||||
func TestProcessProposalAccept(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
accept bool
|
||||
expectedNilPrevote bool
|
||||
}{
|
||||
{
|
||||
name: "accepted block is prevoted",
|
||||
accept: true,
|
||||
expectedNilPrevote: false,
|
||||
},
|
||||
{
|
||||
name: "rejected block is not prevoted",
|
||||
accept: false,
|
||||
expectedNilPrevote: true,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
m := abcimocks.NewApplication(t)
|
||||
status := abci.ResponseProcessProposal_REJECT
|
||||
if testCase.accept {
|
||||
status = abci.ResponseProcessProposal_ACCEPT
|
||||
}
|
||||
m.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: status})
|
||||
m.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{}).Maybe()
|
||||
cs1, _ := randStateWithApp(4, m)
|
||||
height, round := cs1.Height, cs1.Round
|
||||
|
||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||
pv1, err := cs1.privValidator.GetPubKey()
|
||||
require.NoError(t, err)
|
||||
addr := pv1.Address()
|
||||
voteCh := subscribeToVoter(cs1, addr)
|
||||
|
||||
startTestRound(cs1, cs1.Height, round)
|
||||
ensureNewRound(newRoundCh, height, round)
|
||||
|
||||
ensureNewProposal(proposalCh, height, round)
|
||||
rs := cs1.GetRoundState()
|
||||
var prevoteHash tmbytes.HexBytes
|
||||
if !testCase.expectedNilPrevote {
|
||||
prevoteHash = rs.ProposalBlock.Hash()
|
||||
}
|
||||
ensurePrevoteMatch(t, voteCh, height, round, prevoteHash)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 4 vals, 3 Nil Precommits at P0
|
||||
// What we want:
|
||||
// P0 waits for timeoutPrecommit before starting next round
|
||||
|
||||
92
internal/test/block.go
Normal file
92
internal/test/block.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultTestChainID = "test-chain"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultTestTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
func RandomAddress() []byte {
|
||||
return crypto.CRandBytes(crypto.AddressSize)
|
||||
}
|
||||
|
||||
func RandomHash() []byte {
|
||||
return crypto.CRandBytes(tmhash.Size)
|
||||
}
|
||||
|
||||
func MakeBlockID() types.BlockID {
|
||||
return MakeBlockIDWithHash(RandomHash())
|
||||
}
|
||||
|
||||
func MakeBlockIDWithHash(hash []byte) types.BlockID {
|
||||
return types.BlockID{
|
||||
Hash: hash,
|
||||
PartSetHeader: types.PartSetHeader{
|
||||
Total: 100,
|
||||
Hash: RandomHash(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MakeHeader fills the rest of the contents of the header such that it passes
|
||||
// validate basic
|
||||
func MakeHeader(t *testing.T, h *types.Header) *types.Header {
|
||||
t.Helper()
|
||||
if h.Version.Block == 0 {
|
||||
h.Version.Block = version.BlockProtocol
|
||||
}
|
||||
if h.Height == 0 {
|
||||
h.Height = 1
|
||||
}
|
||||
if h.LastBlockID.IsZero() {
|
||||
h.LastBlockID = MakeBlockID()
|
||||
}
|
||||
if h.ChainID == "" {
|
||||
h.ChainID = DefaultTestChainID
|
||||
}
|
||||
if len(h.LastCommitHash) == 0 {
|
||||
h.LastCommitHash = RandomHash()
|
||||
}
|
||||
if len(h.DataHash) == 0 {
|
||||
h.DataHash = RandomHash()
|
||||
}
|
||||
if len(h.ValidatorsHash) == 0 {
|
||||
h.ValidatorsHash = RandomHash()
|
||||
}
|
||||
if len(h.NextValidatorsHash) == 0 {
|
||||
h.NextValidatorsHash = RandomHash()
|
||||
}
|
||||
if len(h.ConsensusHash) == 0 {
|
||||
h.ConsensusHash = RandomHash()
|
||||
}
|
||||
if len(h.AppHash) == 0 {
|
||||
h.AppHash = RandomHash()
|
||||
}
|
||||
if len(h.LastResultsHash) == 0 {
|
||||
h.LastResultsHash = RandomHash()
|
||||
}
|
||||
if len(h.EvidenceHash) == 0 {
|
||||
h.EvidenceHash = RandomHash()
|
||||
}
|
||||
if len(h.ProposerAddress) == 0 {
|
||||
h.ProposerAddress = RandomAddress()
|
||||
}
|
||||
|
||||
require.NoError(t, h.ValidateBasic())
|
||||
|
||||
return h
|
||||
}
|
||||
6
internal/test/doc.go
Normal file
6
internal/test/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
Package factory provides generation code for common structs in Tendermint.
|
||||
It is used primarily for the testing of internal components such as statesync,
|
||||
consensus, blocksync etc..
|
||||
*/
|
||||
package factory
|
||||
11
internal/test/factory_test.go
Normal file
11
internal/test/factory_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func TestMakeHeader(t *testing.T) {
|
||||
MakeHeader(t, &types.Header{})
|
||||
}
|
||||
34
internal/test/genesis.go
Normal file
34
internal/test/genesis.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func GenesisDoc(
|
||||
config *cfg.Config,
|
||||
time time.Time,
|
||||
validators []*types.Validator,
|
||||
consensusParams *tmproto.ConsensusParams,
|
||||
) *types.GenesisDoc {
|
||||
|
||||
genesisValidators := make([]types.GenesisValidator, len(validators))
|
||||
|
||||
for i := range validators {
|
||||
genesisValidators[i] = types.GenesisValidator{
|
||||
Power: validators[i].VotingPower,
|
||||
PubKey: validators[i].PubKey,
|
||||
}
|
||||
}
|
||||
|
||||
return &types.GenesisDoc{
|
||||
GenesisTime: time,
|
||||
InitialHeight: 1,
|
||||
ChainID: config.ChainID(),
|
||||
Validators: genesisValidators,
|
||||
ConsensusParams: consensusParams,
|
||||
}
|
||||
}
|
||||
11
internal/test/tx.go
Normal file
11
internal/test/tx.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package factory
|
||||
|
||||
import "github.com/tendermint/tendermint/types"
|
||||
|
||||
func MakeNTxs(height, n int64) []types.Tx {
|
||||
txs := make([]types.Tx, n)
|
||||
for i := range txs {
|
||||
txs[i] = types.Tx([]byte{byte(height), byte(i / 256), byte(i % 256)})
|
||||
}
|
||||
return txs
|
||||
}
|
||||
41
internal/test/validator.go
Normal file
41
internal/test/validator.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func Validator(ctx context.Context, votingPower int64) (*types.Validator, types.PrivValidator, error) {
|
||||
privVal := types.NewMockPV()
|
||||
pubKey, err := privVal.GetPubKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
val := types.NewValidator(pubKey, votingPower)
|
||||
return val, privVal, nil
|
||||
}
|
||||
|
||||
func ValidatorSet(ctx context.Context, t *testing.T, numValidators int, votingPower int64) (*types.ValidatorSet, []types.PrivValidator) {
|
||||
var (
|
||||
valz = make([]*types.Validator, numValidators)
|
||||
privValidators = make([]types.PrivValidator, numValidators)
|
||||
)
|
||||
t.Helper()
|
||||
|
||||
for i := 0; i < numValidators; i++ {
|
||||
val, privValidator, err := Validator(ctx, votingPower)
|
||||
require.NoError(t, err)
|
||||
valz[i] = val
|
||||
privValidators[i] = privValidator
|
||||
}
|
||||
|
||||
sort.Sort(types.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return types.NewValidatorSet(valz), privValidators
|
||||
}
|
||||
44
internal/test/vote.go
Normal file
44
internal/test/vote.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package factory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func MakeVote(
|
||||
ctx context.Context,
|
||||
val types.PrivValidator,
|
||||
chainID string,
|
||||
valIndex int32,
|
||||
height int64,
|
||||
round int32,
|
||||
step int,
|
||||
blockID types.BlockID,
|
||||
time time.Time,
|
||||
) (*types.Vote, error) {
|
||||
pubKey, err := val.GetPubKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &types.Vote{
|
||||
ValidatorAddress: pubKey.Address(),
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Type: tmproto.SignedMsgType(step),
|
||||
BlockID: blockID,
|
||||
Timestamp: time,
|
||||
}
|
||||
|
||||
vpb := v.ToProto()
|
||||
if err := val.SignVote(chainID, vpb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.Signature = vpb.Signature
|
||||
return v, nil
|
||||
}
|
||||
Reference in New Issue
Block a user