|
|
- 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)
- }
- }
- }
|