You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

205 lines
5.2 KiB

package behavior_test
import (
"sync"
"testing"
bh "github.com/tendermint/tendermint/blockchain/v2/internal/behavior"
"github.com/tendermint/tendermint/p2p"
)
// TestMockReporter tests the MockReporter's ability to store reported
// peer behavior in memory indexed by the peerID.
func TestMockReporter(t *testing.T) {
var peerID p2p.NodeID = "MockPeer"
pr := bh.NewMockReporter()
behaviors := pr.GetBehaviors(peerID)
if len(behaviors) != 0 {
t.Error("Expected to have no behaviors reported")
}
badMessage := bh.BadMessage(peerID, "bad message")
if err := pr.Report(badMessage); err != nil {
t.Error(err)
}
behaviors = pr.GetBehaviors(peerID)
if len(behaviors) != 1 {
t.Error("Expected the peer have one reported behavior")
}
if behaviors[0] != badMessage {
t.Error("Expected Bad Message to have been reported")
}
}
type scriptItem struct {
peerID p2p.NodeID
behavior bh.PeerBehavior
}
// equalBehaviors returns true if a and b contain the same PeerBehaviors with
// the same freequencies and otherwise false.
func equalBehaviors(a []bh.PeerBehavior, b []bh.PeerBehavior) bool {
aHistogram := map[bh.PeerBehavior]int{}
bHistogram := map[bh.PeerBehavior]int{}
for _, behavior := range a {
aHistogram[behavior]++
}
for _, behavior := range b {
bHistogram[behavior]++
}
if len(aHistogram) != len(bHistogram) {
return false
}
for _, behavior := range a {
if aHistogram[behavior] != bHistogram[behavior] {
return false
}
}
for _, behavior := range b {
if bHistogram[behavior] != aHistogram[behavior] {
return false
}
}
return true
}
// TestEqualPeerBehaviors tests that equalBehaviors can tell that two slices
// of peer behaviors can be compared for the behaviors they contain and the
// freequencies that those behaviors occur.
func TestEqualPeerBehaviors(t *testing.T) {
var (
peerID p2p.NodeID = "MockPeer"
consensusVote = bh.ConsensusVote(peerID, "voted")
blockPart = bh.BlockPart(peerID, "blocked")
equals = []struct {
left []bh.PeerBehavior
right []bh.PeerBehavior
}{
// Empty sets
{[]bh.PeerBehavior{}, []bh.PeerBehavior{}},
// Single behaviors
{[]bh.PeerBehavior{consensusVote}, []bh.PeerBehavior{consensusVote}},
// Equal Frequencies
{[]bh.PeerBehavior{consensusVote, consensusVote},
[]bh.PeerBehavior{consensusVote, consensusVote}},
// Equal frequencies different orders
{[]bh.PeerBehavior{consensusVote, blockPart},
[]bh.PeerBehavior{blockPart, consensusVote}},
}
unequals = []struct {
left []bh.PeerBehavior
right []bh.PeerBehavior
}{
// Comparing empty sets to non empty sets
{[]bh.PeerBehavior{}, []bh.PeerBehavior{consensusVote}},
// Different behaviors
{[]bh.PeerBehavior{consensusVote}, []bh.PeerBehavior{blockPart}},
// Same behavior with different frequencies
{[]bh.PeerBehavior{consensusVote},
[]bh.PeerBehavior{consensusVote, consensusVote}},
}
)
for _, test := range equals {
if !equalBehaviors(test.left, test.right) {
t.Errorf("expected %#v and %#v to be equal", test.left, test.right)
}
}
for _, test := range unequals {
if equalBehaviors(test.left, test.right) {
t.Errorf("expected %#v and %#v to be unequal", test.left, test.right)
}
}
}
// TestPeerBehaviorConcurrency constructs a scenario in which
// multiple goroutines are using the same MockReporter instance.
// This test reproduces the conditions in which MockReporter will
// be used within a Reactor `Receive` method tests to ensure thread safety.
func TestMockPeerBehaviorReporterConcurrency(t *testing.T) {
var (
behaviorScript = []struct {
peerID p2p.NodeID
behaviors []bh.PeerBehavior
}{
{"1", []bh.PeerBehavior{bh.ConsensusVote("1", "")}},
{"2", []bh.PeerBehavior{bh.ConsensusVote("2", ""), bh.ConsensusVote("2", ""), bh.ConsensusVote("2", "")}},
{
"3",
[]bh.PeerBehavior{bh.BlockPart("3", ""),
bh.ConsensusVote("3", ""),
bh.BlockPart("3", ""),
bh.ConsensusVote("3", "")}},
{
"4",
[]bh.PeerBehavior{bh.ConsensusVote("4", ""),
bh.ConsensusVote("4", ""),
bh.ConsensusVote("4", ""),
bh.ConsensusVote("4", "")}},
{
"5",
[]bh.PeerBehavior{bh.BlockPart("5", ""),
bh.ConsensusVote("5", ""),
bh.BlockPart("5", ""),
bh.ConsensusVote("5", "")}},
}
)
var receiveWg sync.WaitGroup
pr := bh.NewMockReporter()
scriptItems := make(chan scriptItem)
done := make(chan int)
numConsumers := 3
for i := 0; i < numConsumers; i++ {
receiveWg.Add(1)
go func() {
defer receiveWg.Done()
for {
select {
case pb := <-scriptItems:
if err := pr.Report(pb.behavior); err != nil {
t.Error(err)
}
case <-done:
return
}
}
}()
}
var sendingWg sync.WaitGroup
sendingWg.Add(1)
go func() {
defer sendingWg.Done()
for _, item := range behaviorScript {
for _, reason := range item.behaviors {
scriptItems <- scriptItem{item.peerID, reason}
}
}
}()
sendingWg.Wait()
for i := 0; i < numConsumers; i++ {
done <- 1
}
receiveWg.Wait()
for _, items := range behaviorScript {
reported := pr.GetBehaviors(items.peerID)
if !equalBehaviors(reported, items.behaviors) {
t.Errorf("expected peer %s to have behaved \nExpected: %#v \nGot %#v \n",
items.peerID, items.behaviors, reported)
}
}
}