github.com/evdatsion/aphelion-dpos-bft@v0.32.1/behaviour/reporter_test.go (about) 1 package behaviour_test 2 3 import ( 4 "sync" 5 "testing" 6 7 bh "github.com/evdatsion/aphelion-dpos-bft/behaviour" 8 "github.com/evdatsion/aphelion-dpos-bft/p2p" 9 ) 10 11 // TestMockReporter tests the MockReporter's ability to store reported 12 // peer behaviour in memory indexed by the peerID. 13 func TestMockReporter(t *testing.T) { 14 var peerID p2p.ID = "MockPeer" 15 pr := bh.NewMockReporter() 16 17 behaviours := pr.GetBehaviours(peerID) 18 if len(behaviours) != 0 { 19 t.Error("Expected to have no behaviours reported") 20 } 21 22 badMessage := bh.BadMessage(peerID, "bad message") 23 pr.Report(badMessage) 24 behaviours = pr.GetBehaviours(peerID) 25 if len(behaviours) != 1 { 26 t.Error("Expected the peer have one reported behaviour") 27 } 28 29 if behaviours[0] != badMessage { 30 t.Error("Expected Bad Message to have been reported") 31 } 32 } 33 34 type scriptItem struct { 35 peerID p2p.ID 36 behaviour bh.PeerBehaviour 37 } 38 39 // equalBehaviours returns true if a and b contain the same PeerBehaviours with 40 // the same freequencies and otherwise false. 41 func equalBehaviours(a []bh.PeerBehaviour, b []bh.PeerBehaviour) bool { 42 aHistogram := map[bh.PeerBehaviour]int{} 43 bHistogram := map[bh.PeerBehaviour]int{} 44 45 for _, behaviour := range a { 46 aHistogram[behaviour] += 1 47 } 48 49 for _, behaviour := range b { 50 bHistogram[behaviour] += 1 51 } 52 53 if len(aHistogram) != len(bHistogram) { 54 return false 55 } 56 57 for _, behaviour := range a { 58 if aHistogram[behaviour] != bHistogram[behaviour] { 59 return false 60 } 61 } 62 63 for _, behaviour := range b { 64 if bHistogram[behaviour] != aHistogram[behaviour] { 65 return false 66 } 67 } 68 69 return true 70 } 71 72 // TestEqualPeerBehaviours tests that equalBehaviours can tell that two slices 73 // of peer behaviours can be compared for the behaviours they contain and the 74 // freequencies that those behaviours occur. 75 func TestEqualPeerBehaviours(t *testing.T) { 76 var ( 77 peerID p2p.ID = "MockPeer" 78 consensusVote = bh.ConsensusVote(peerID, "voted") 79 blockPart = bh.BlockPart(peerID, "blocked") 80 equals = []struct { 81 left []bh.PeerBehaviour 82 right []bh.PeerBehaviour 83 }{ 84 // Empty sets 85 {[]bh.PeerBehaviour{}, []bh.PeerBehaviour{}}, 86 // Single behaviours 87 {[]bh.PeerBehaviour{consensusVote}, []bh.PeerBehaviour{consensusVote}}, 88 // Equal Frequencies 89 {[]bh.PeerBehaviour{consensusVote, consensusVote}, 90 []bh.PeerBehaviour{consensusVote, consensusVote}}, 91 // Equal frequencies different orders 92 {[]bh.PeerBehaviour{consensusVote, blockPart}, 93 []bh.PeerBehaviour{blockPart, consensusVote}}, 94 } 95 unequals = []struct { 96 left []bh.PeerBehaviour 97 right []bh.PeerBehaviour 98 }{ 99 // Comparing empty sets to non empty sets 100 {[]bh.PeerBehaviour{}, []bh.PeerBehaviour{consensusVote}}, 101 // Different behaviours 102 {[]bh.PeerBehaviour{consensusVote}, []bh.PeerBehaviour{blockPart}}, 103 // Same behaviour with different frequencies 104 {[]bh.PeerBehaviour{consensusVote}, 105 []bh.PeerBehaviour{consensusVote, consensusVote}}, 106 } 107 ) 108 109 for _, test := range equals { 110 if !equalBehaviours(test.left, test.right) { 111 t.Errorf("Expected %#v and %#v to be equal", test.left, test.right) 112 } 113 } 114 115 for _, test := range unequals { 116 if equalBehaviours(test.left, test.right) { 117 t.Errorf("Expected %#v and %#v to be unequal", test.left, test.right) 118 } 119 } 120 } 121 122 // TestPeerBehaviourConcurrency constructs a scenario in which 123 // multiple goroutines are using the same MockReporter instance. 124 // This test reproduces the conditions in which MockReporter will 125 // be used within a Reactor `Receive` method tests to ensure thread safety. 126 func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { 127 var ( 128 behaviourScript = []struct { 129 peerID p2p.ID 130 behaviours []bh.PeerBehaviour 131 }{ 132 {"1", []bh.PeerBehaviour{bh.ConsensusVote("1", "")}}, 133 {"2", []bh.PeerBehaviour{bh.ConsensusVote("2", ""), bh.ConsensusVote("2", ""), bh.ConsensusVote("2", "")}}, 134 {"3", []bh.PeerBehaviour{bh.BlockPart("3", ""), bh.ConsensusVote("3", ""), bh.BlockPart("3", ""), bh.ConsensusVote("3", "")}}, 135 {"4", []bh.PeerBehaviour{bh.ConsensusVote("4", ""), bh.ConsensusVote("4", ""), bh.ConsensusVote("4", ""), bh.ConsensusVote("4", "")}}, 136 {"5", []bh.PeerBehaviour{bh.BlockPart("5", ""), bh.ConsensusVote("5", ""), bh.BlockPart("5", ""), bh.ConsensusVote("5", "")}}, 137 } 138 ) 139 140 var receiveWg sync.WaitGroup 141 pr := bh.NewMockReporter() 142 scriptItems := make(chan scriptItem) 143 done := make(chan int) 144 numConsumers := 3 145 for i := 0; i < numConsumers; i++ { 146 receiveWg.Add(1) 147 go func() { 148 defer receiveWg.Done() 149 for { 150 select { 151 case pb := <-scriptItems: 152 pr.Report(pb.behaviour) 153 case <-done: 154 return 155 } 156 } 157 }() 158 } 159 160 var sendingWg sync.WaitGroup 161 sendingWg.Add(1) 162 go func() { 163 defer sendingWg.Done() 164 for _, item := range behaviourScript { 165 for _, reason := range item.behaviours { 166 scriptItems <- scriptItem{item.peerID, reason} 167 } 168 } 169 }() 170 171 sendingWg.Wait() 172 173 for i := 0; i < numConsumers; i++ { 174 done <- 1 175 } 176 177 receiveWg.Wait() 178 179 for _, items := range behaviourScript { 180 reported := pr.GetBehaviours(items.peerID) 181 if !equalBehaviours(reported, items.behaviours) { 182 t.Errorf("Expected peer %s to have behaved \nExpected: %#v \nGot %#v \n", 183 items.peerID, items.behaviours, reported) 184 } 185 } 186 }