package p2p_test import ( "sync" "testing" "github.com/tendermint/tendermint/p2p" ) // TestMockPeerBehaviour tests the MockPeerBehaviour' ability to store reported // peer behaviour in memory indexed by the peerID func TestMockPeerBehaviourReporter(t *testing.T) { var peerID p2p.ID = "MockPeer" pr := p2p.NewMockPeerBehaviourReporter() behaviours := pr.GetBehaviours(peerID) if len(behaviours) != 0 { t.Error("Expected to have no behaviours reported") } pr.Report(peerID, p2p.PeerBehaviourBadMessage) behaviours = pr.GetBehaviours(peerID) if len(behaviours) != 1 { t.Error("Expected the peer have one reported behaviour") } if behaviours[0] != p2p.PeerBehaviourBadMessage { t.Error("Expected PeerBehaviourBadMessage to have been reported") } } type scriptedBehaviours struct { PeerID p2p.ID Behaviours []p2p.PeerBehaviour } type scriptItem struct { PeerID p2p.ID Behaviour p2p.PeerBehaviour } // equalBehaviours returns true if a and b contain the same PeerBehaviours with // the same freequency and otherwise false. func equalBehaviours(a []p2p.PeerBehaviour, b []p2p.PeerBehaviour) bool { aHistogram := map[p2p.PeerBehaviour]int{} bHistogram := map[p2p.PeerBehaviour]int{} for _, behaviour := range a { aHistogram[behaviour] += 1 } for _, behaviour := range b { bHistogram[behaviour] += 1 } if len(aHistogram) != len(bHistogram) { return false } for _, behaviour := range a { if aHistogram[behaviour] != bHistogram[behaviour] { return false } } for _, behaviour := range b { if bHistogram[behaviour] != aHistogram[behaviour] { return false } } return true } // TestEqualPeerBehaviours tests that equalBehaviours can tell that two slices // of peer behaviours can be compared for the behaviours they contain and the // freequencies that those behaviours occur. func TestEqualPeerBehaviours(t *testing.T) { equals := []struct { left []p2p.PeerBehaviour right []p2p.PeerBehaviour }{ // Empty sets {[]p2p.PeerBehaviour{}, []p2p.PeerBehaviour{}}, // Single behaviours {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, // Equal Frequencies {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, // Equal frequencies different orders {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart}, []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, } for _, test := range equals { if !equalBehaviours(test.left, test.right) { t.Errorf("Expected %#v and %#v to be equal", test.left, test.right) } } unequals := []struct { left []p2p.PeerBehaviour right []p2p.PeerBehaviour }{ // Comparing empty sets to non empty sets {[]p2p.PeerBehaviour{}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, // Different behaviours {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart}}, // Same behaviour with different frequencies {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, } for _, test := range unequals { if equalBehaviours(test.left, test.right) { t.Errorf("Expected %#v and %#v to be unequal", test.left, test.right) } } } // TestPeerBehaviourConcurrency constructs a scenario in which // multiple goroutines are using the same MockPeerBehaviourReporter instance. // This test reproduces the conditions in which MockPeerBehaviourReporter will // be used within a Reactor Receive method tests to ensure thread safety. func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { behaviourScript := []scriptedBehaviours{ {"1", []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, {"2", []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, {"3", []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, {"4", []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, {"5", []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, } var receiveWg sync.WaitGroup pr := p2p.NewMockPeerBehaviourReporter() 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: pr.Report(pb.PeerID, pb.Behaviour) case <-done: return } } }() } var sendingWg sync.WaitGroup sendingWg.Add(1) go func() { defer sendingWg.Done() for _, item := range behaviourScript { for _, reason := range item.Behaviours { scriptItems <- scriptItem{item.PeerID, reason} } } }() sendingWg.Wait() for i := 0; i < numConsumers; i++ { done <- 1 } receiveWg.Wait() for _, items := range behaviourScript { reported := pr.GetBehaviours(items.PeerID) if !equalBehaviours(reported, items.Behaviours) { t.Errorf("Expected peer %s to have behaved \nExpected: %#v \nGot %#v \n", items.PeerID, items.Behaviours, reported) } } }