func TestStateEnterProposeYesPrivValidator(t *testing.T) {
	config := configSetup(t)
	cs, _ := randState(config, 1)
	height, round := cs.Height, cs.Round
	timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose)
	proposalCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal)
	cs.enterNewRound(height, round)
	cs.startRoutines(3)
	ensureNewProposal(proposalCh, height, round)
	rs := cs.GetRoundState()
	if rs.Proposal == nil {
		t.Error("rs.Proposal should be set")
	}
	if rs.ProposalBlock == nil {
		t.Error("rs.ProposalBlock should be set")
	}
	if rs.ProposalBlockParts.Total() == 0 {
		t.Error("rs.ProposalBlockParts should be set")
	}
	ensureNoNewTimeout(timeoutCh, cs.config.TimeoutPropose.Nanoseconds())
}
a validator should not timeout of the prevote round (TODO: unless the block is really big!)
package consensus
import (
	"bytes"
	"context"
	"fmt"
	"testing"
	"time"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/tendermint/tendermint/abci/example/kvstore"
	"github.com/tendermint/tendermint/crypto/tmhash"
	cstypes "github.com/tendermint/tendermint/internal/consensus/types"
	"github.com/tendermint/tendermint/libs/log"
	tmpubsub "github.com/tendermint/tendermint/libs/pubsub"
	tmrand "github.com/tendermint/tendermint/libs/rand"
	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
	"github.com/tendermint/tendermint/types"
)
func TestStateProposerSelection0(t *testing.T) {
	config := configSetup(t)
	cs1, vss := randState(config, 4)
	height, round := cs1.Height, cs1.Round
	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
	proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
	startTestRound(cs1, height, round)
	ensureNewRound(newRoundCh, height, round)
	prop := cs1.GetRoundState().Validators.GetProposer()
	pv, err := cs1.privValidator.GetPubKey(context.Background())
	require.NoError(t, err)
	address := pv.Address()
	if !bytes.Equal(prop.Address, address) {
		t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
	}
	ensureNewProposal(proposalCh, height, round)
	rs := cs1.GetRoundState()
	signAddVotes(config, cs1, tmproto.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:]...)
	ensureNewRound(newRoundCh, height+1, 0)
	prop = cs1.GetRoundState().Validators.GetProposer()
	pv1, err := vss[1].GetPubKey(context.Background())
	require.NoError(t, err)
	addr := pv1.Address()
	if !bytes.Equal(prop.Address, addr) {
		panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address))
	}
}
func TestStateProposerSelection2(t *testing.T) {
	config := configSetup(t)
	cs1, vss := randState(config, 4)  
	height := cs1.Height
	newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
	incrementRound(vss[1:]...)
	incrementRound(vss[1:]...)
	var round int32 = 2
	startTestRound(cs1, height, round)
	ensureNewRound(newRoundCh, height, round)  
	for i := int32(0); int(i) < len(vss); i++ {
		prop := cs1.GetRoundState().Validators.GetProposer()
		pvk, err := vss[int(i+round)%len(vss)].GetPubKey(context.Background())
		require.NoError(t, err)
		addr := pvk.Address()
		correctProposer := addr
		if !bytes.Equal(prop.Address, correctProposer) {
			panic(fmt.Sprintf(
				"expected RoundState.Validators.GetProposer() to be validator %d. Got %X",
				int(i+2)%len(vss),
				prop.Address))
		}
		rs := cs1.GetRoundState()
		signAddVotes(config, cs1, tmproto.PrecommitType, nil, rs.ProposalBlockParts.Header(), vss[1:]...)
		ensureNewRound(newRoundCh, height, i+round+1)  
		incrementRound(vss[1:]...)
	}
}
func TestStateEnterProposeNoPrivValidator(t *testing.T) {
	config := configSetup(t)
	cs, _ := randState(config, 1)
	cs.SetPrivValidator(nil)
	height, round := cs.Height, cs.Round
	timeoutCh := subscribe(cs.eventBus, types.EventQueryTimeoutPropose)
	startTestRound(cs, height, round)
	ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds())
	if cs.GetRoundState().Proposal != nil {
		t.Error("Expected to make no proposal, since no privValidator")
	}
}