github.com/aergoio/aergo@v1.3.1/p2p/raftsupport/concclusterreceiver_test.go (about) 1 /* 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package raftsupport 7 8 import ( 9 "bytes" 10 "github.com/aergoio/aergo-lib/log" 11 "github.com/aergoio/aergo/p2p/p2putil" 12 "sync" 13 "sync/atomic" 14 "testing" 15 "time" 16 17 "github.com/aergoio/aergo/message" 18 "github.com/aergoio/aergo/p2p/p2pcommon" 19 "github.com/aergoio/aergo/p2p/p2pmock" 20 "github.com/aergoio/aergo/types" 21 "github.com/golang/mock/gomock" 22 ) 23 24 func TestConcurrentClusterInfoReceiver_StartGet(t *testing.T) { 25 logger := log.NewLogger("raft.support.test") 26 ctrl := gomock.NewController(t) 27 defer ctrl.Finish() 28 29 type args struct { 30 peerCnt int 31 timeout time.Duration 32 } 33 tests := []struct { 34 name string 35 args args 36 37 wantSentCnt int // count of sent to remote peers 38 wantTimeout bool // whether receiver returns result or not (=timeout) 39 wantErrResp bool // result with error or not 40 }{ 41 {"TTimeout", args{peerCnt: 3}, 3, true, true}, 42 {"TNoPeers", args{peerCnt: 0}, 0, false, true}, 43 } 44 for _, tt := range tests { 45 t.Run(tt.name, func(t *testing.T) { 46 mockActor := p2pmock.NewMockActorService(ctrl) 47 mockMF := p2pmock.NewMockMoFactory(ctrl) 48 mockMo := createDummyMo(ctrl) 49 mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).Return(mockMo).Times(tt.wantSentCnt) 50 peers := make([]p2pcommon.RemotePeer, 0, tt.args.peerCnt) 51 for i := 0; i < tt.args.peerCnt; i++ { 52 dummyPeerID, _ := types.IDB58Decode("16Uiu2HAmFqptXPfcdaCdwipB2fhHATgKGVFVPehDAPZsDKSU7jRm") 53 peers = append(peers, createDummyPeer(ctrl, dummyPeerID, types.RUNNING)) 54 } 55 replyChan := make(chan *message.GetClusterRsp) 56 dummyReq := &message.GetCluster{ReplyC: replyChan} 57 target := NewConcClusterInfoReceiver(mockActor, mockMF, peers, time.Millisecond*20, dummyReq, logger) 58 target.StartGet() 59 60 if !tt.wantTimeout { 61 timer := time.NewTimer(time.Second * 2) 62 select { 63 case resp := <-replyChan: 64 if (resp.Err != nil) != tt.wantErrResp { 65 t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp) 66 } 67 case <-timer.C: 68 t.Errorf("timeout occurred, want no time") 69 } 70 } else { 71 timer := time.NewTimer(time.Millisecond * 200) 72 select { 73 case resp := <-replyChan: 74 if (resp.Err != nil) != tt.wantErrResp { 75 t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp) 76 } 77 case <-timer.C: 78 // expected timeout 79 } 80 } 81 }) 82 } 83 } 84 85 func TestConcurrentClusterInfoReceiver_trySendAllPeers(t *testing.T) { 86 logger := log.NewLogger("raft.support.test") 87 ctrl := gomock.NewController(t) 88 defer ctrl.Finish() 89 90 type args struct { 91 stats []int 92 } 93 tests := []struct { 94 name string 95 args args 96 97 wantSentCnt int 98 wantSucc bool 99 }{ 100 {"TAllRunning", args{[]int{1, 1, 1, 1, 1}}, 5, true}, 101 {"TNoPeers", args{[]int{}}, 0, false}, 102 {"TNoRunning", args{[]int{0, 0, 0, 0, 0}}, 0, false}, 103 {"TMixed", args{[]int{0, 0, 1, 1, 1}}, 3, true}, 104 {"TMixed2", args{[]int{1, 1, 0, 0, 0}}, 2, false}, 105 {"TMixed3", args{[]int{1, 0, 1, 0, 1, 1}}, 4, true}, 106 {"TMixed3Fail", args{[]int{1, 0, 1, 0, 0, 1}}, 3, false}, 107 {"TMixed4", args{[]int{0, 1, 0, 1, 0, 1, 0}}, 3, false}, 108 {"TSingle", args{[]int{1}}, 1, true}, 109 {"TSingleFail", args{[]int{0}}, 0, false}, 110 {"TTwoFail", args{[]int{0, 1}}, 1, false}, 111 {"TTwoFail2", args{[]int{0, 0}}, 0, false}, 112 {"TThree", args{[]int{1, 0, 1}}, 2, true}, 113 {"TThreeFail", args{[]int{1, 0, 0}}, 1, false}, 114 } 115 for _, tt := range tests { 116 t.Run(tt.name, func(t *testing.T) { 117 mockActor := p2pmock.NewMockActorService(ctrl) 118 mockMF := p2pmock.NewMockMoFactory(ctrl) 119 mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(respReceiver p2pcommon.ResponseReceiver, protocolID p2pcommon.SubProtocol, message p2pcommon.MessageBody) p2pcommon.MsgOrder { 120 return createDummyMo(ctrl) 121 }).Times(tt.wantSentCnt) 122 peers := make([]p2pcommon.RemotePeer, 0, len(tt.args.stats)) 123 for _, run := range tt.args.stats { 124 dummyPeerID := p2putil.RandomPeerID() 125 stat := types.RUNNING 126 if run == 0 { 127 stat = types.STOPPING 128 } 129 peers = append(peers, createDummyPeer(ctrl, dummyPeerID, stat)) 130 } 131 132 replyChan := make(chan *message.GetClusterRsp) 133 dummyReq := &message.GetCluster{ReplyC: replyChan} 134 target := NewConcClusterInfoReceiver(mockActor, mockMF, peers, time.Millisecond, dummyReq, logger) 135 ret := target.trySendAllPeers() 136 137 if len(target.sent) != tt.wantSentCnt { 138 t.Errorf("trySendAllPeers sentCount %v, want %v ", len(target.sent), tt.wantSentCnt) 139 } 140 if ret != tt.wantSucc { 141 t.Errorf("trySendAllPeers() ret %v, want %v ", ret, tt.wantSucc) 142 } 143 }) 144 } 145 } 146 147 type retStat int 148 149 const ( 150 ERR = -1 151 NOR = -2 152 ) 153 154 func TestConcurrentClusterInfoReceiver_ReceiveResp(t *testing.T) { 155 logger := log.NewLogger("raft.support.test") 156 157 sampleChainID := []byte("testChain") 158 members := make([]*types.MemberAttr, 4) 159 160 type args struct { 161 retStats []retStat 162 } 163 tests := []struct { 164 name string 165 args args 166 167 wantBestNo int // count of sent to remote peers 168 wantErrResp bool // result with error or not 169 }{ 170 {"TAllSame", args{[]retStat{10, 10, 10, 10, 10}}, 10, false}, 171 {"TErrRet", args{ []retStat{ERR,ERR,ERR,ERR,ERR}}, 10, true}, 172 {"TMixed", args{ []retStat{100, ERR, 99, 98, ERR}}, 100, false}, 173 {"TMixed2", args{ []retStat{100, ERR, NOR, ERR, 100}}, 100, true}, 174 {"TTimeSucc", args{ []retStat{NOR, 99, NOR, 98, 99}}, 99, false}, 175 {"TTime1", args{ []retStat{NOR, ERR, NOR, 100, 100}}, 100, true}, 176 {"TTime2", args{ []retStat{NOR, NOR, NOR, 100, 100}}, 100, true}, 177 } 178 for _, tt := range tests { 179 t.Run(tt.name, func(t *testing.T) { 180 ctrl := gomock.NewController(t) 181 peers := make([]p2pcommon.RemotePeer, len(tt.args.retStats)) 182 183 mockActor := p2pmock.NewMockActorService(ctrl) 184 mockMF := p2pmock.NewMockMoFactory(ctrl) 185 mockMO := p2pmock.NewMockMsgOrder(ctrl) 186 187 replyChan := make(chan *message.GetClusterRsp) 188 dummyReq := &message.GetCluster{ReplyC: replyChan} 189 190 sMap := make(map[p2pcommon.MsgID]p2pcommon.RemotePeer) 191 rHeads := make([]p2pcommon.Message, 0, len(tt.args.retStats)) 192 rBodies := make([]*types.GetClusterInfoResponse, 0, len(tt.args.retStats)) 193 194 mockMF.EXPECT().NewMsgBlockRequestOrder(gomock.Any(), p2pcommon.GetClusterRequest, gomock.Any()). 195 Times(len(tt.args.retStats)).Return(mockMO) 196 sentTrigger := int32(0) 197 prevCall := (*gomock.Call)(nil) 198 for i, st := range tt.args.retStats { 199 dummyPeerID := p2putil.RandomPeerID() 200 msgID := p2pcommon.NewMsgID() 201 stat := types.RUNNING 202 mockPeer := p2pmock.NewMockRemotePeer(ctrl) 203 mockPeer.EXPECT().State().Return(stat).AnyTimes() 204 mockPeer.EXPECT().ID().Return(dummyPeerID).AnyTimes() 205 mockPeer.EXPECT().Name().Return("peer" + p2putil.ShortForm(dummyPeerID)).AnyTimes() 206 mockPeer.EXPECT().ConsumeRequest(gomock.Any()).AnyTimes() 207 mockPeer.EXPECT().SendMessage(mockMO).Do(func(arg interface{}) { 208 atomic.StoreInt32(&sentTrigger, 1)}) 209 peers[i] = mockPeer 210 sMap[msgID] = mockPeer 211 212 // mockMO 213 if prevCall == nil { 214 prevCall = mockMO.EXPECT().GetMsgID().Return(msgID) 215 } else { 216 prevCall = mockMO.EXPECT().GetMsgID().Return(msgID).After(prevCall) 217 } 218 219 head := p2pcommon.NewLiteMessageValue(p2pcommon.RaftWrapperMessage, p2pcommon.NewMsgID(), msgID, time.Now().UnixNano()) 220 body := &types.GetClusterInfoResponse{ChainID: sampleChainID, MbrAttrs: members, Error: ""} 221 switch st { 222 case NOR: 223 continue 224 case ERR: 225 body.Error = "getCluster failed" 226 fallthrough 227 default: 228 rHeads = append(rHeads, head) 229 rBodies = append(rBodies, body) 230 } 231 } 232 ttl := time.Second>>4 233 target := NewConcClusterInfoReceiver(mockActor, mockMF, peers, ttl , dummyReq, logger) 234 target.StartGet() 235 236 wg := sync.WaitGroup{} 237 wg.Add(len(rHeads)) 238 bar := sync.WaitGroup{} 239 bar.Add(1) 240 for i, head := range rHeads { 241 go func(msg p2pcommon.Message, body *types.GetClusterInfoResponse) { 242 wg.Done() 243 bar.Wait() 244 target.ReceiveResp(msg, body) 245 }(head, rBodies[i]) 246 } 247 for atomic.LoadInt32(&sentTrigger) == 0 { 248 time.Sleep(time.Millisecond) 249 } 250 wg.Wait() 251 bar.Done() 252 timer := time.NewTimer(ttl << 1) 253 select { 254 case resp := <-replyChan: 255 if (resp.Err != nil) != tt.wantErrResp { 256 t.Errorf("resp error %v, wantErr %v ", resp.Err, tt.wantErrResp) 257 } 258 // receiver return valid result 259 if !tt.wantErrResp { 260 if !bytes.Equal(resp.ChainID, sampleChainID) { 261 t.Errorf("resp chainid %v, want %v ", resp.ChainID, sampleChainID) 262 } 263 if len(resp.Members) != len(members) { 264 t.Errorf("resp members %v, want %v ", len(resp.Members), len(members)) 265 } 266 } 267 case <-timer.C: 268 t.Fatalf("timeout occurred, want not") 269 } 270 271 ctrl.Finish() 272 }) 273 } 274 }