github.com/aergoio/aergo@v1.3.1/p2p/p2putil/channelpipe_test.go (about) 1 package p2putil 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/aergoio/aergo-lib/log" 10 "github.com/stretchr/testify/assert" 11 ) 12 13 func TestChannelPipe(t *testing.T) { 14 const arrSize = 30 15 var mos [arrSize]TestItem 16 for i := 0; i < arrSize; i++ { 17 mos[i] = &testItem{i} 18 } 19 20 logger := log.NewLogger("test") 21 tests := []struct { 22 name string 23 cap int 24 stallIdx int 25 26 expectMinOut uint64 27 expectConsec uint64 28 }{ 29 {"tStall", 10, 0, 2, 18}, 30 {"tmidStall", 10, 5, 7, 13}, 31 {"tfast", 10, 1000, arrSize - 10, 0}, 32 // TODO: Add test cases. 33 } 34 35 for _, tt := range tests { 36 t.Run(tt.name, func(t *testing.T) { 37 doneC := make(chan int, 1) 38 statListener := NewStatLister() 39 listener := NewMultiListener(statListener, &logListener{logger}, &orderCheckListener{t: t, outId: -1, dropId: -1}) 40 c := newDefaultChannelPipe(tt.cap, listener) 41 c.Open() 42 go consumeStall(c, tt.stallIdx, arrSize, doneC) 43 for _, mo := range mos { 44 c.Put(mo) 45 time.Sleep(time.Millisecond) 46 } 47 consumeCount := <-doneC 48 lock := &sync.Mutex{} 49 lock.Lock() 50 actStat := statListener 51 lock.Unlock() 52 53 fmt.Printf("In %d , out %d , consecutive drop %d\n", actStat.incnt, actStat.outcnt, actStat.consecdrop) 54 assert.True(t, actStat.incnt == arrSize) 55 if tt.expectConsec == 0 { 56 assert.Equal(t, uint64(consumeCount), actStat.outcnt) 57 assert.Equal(t, actStat.incnt, actStat.outcnt) 58 } else { 59 assert.Equal(t, uint64(consumeCount+1), actStat.outcnt) 60 assert.Equal(t, actStat.incnt, actStat.outcnt+actStat.consecdrop+uint64(tt.cap)) 61 } 62 63 c.Close() 64 }) 65 } 66 } 67 68 func Test_nonBlockWriteChan2(t *testing.T) { 69 const arrSize = 30 70 var mos [arrSize]TestItem 71 for i := 0; i < arrSize; i++ { 72 mos[i] = &testItem{i} 73 } 74 logger := log.NewLogger("test") 75 76 tests := []struct { 77 name string 78 cap int 79 stallIdx int 80 81 expectMinOut uint64 82 expectConsec uint64 83 }{ 84 {"tfast", 10, 1000, arrSize - 10, 0}, 85 // TODO: Add test cases. 86 } 87 88 for _, tt := range tests { 89 t.Run(tt.name, func(t *testing.T) { 90 doneC := make(chan int, 1) 91 statListener := NewStatLister() 92 listener := NewMultiListener(statListener, &logListener{logger}, &orderCheckListener{t: t, outId: -1, dropId: -1}) 93 c := newDefaultChannelPipe(tt.cap, listener) 94 c.Open() 95 96 go consumeStall2(c, tt.stallIdx, arrSize, doneC) 97 for _, mo := range mos { 98 c.Put(mo) 99 } 100 consumeCount := <-doneC 101 lock := &sync.Mutex{} 102 lock.Lock() 103 actStat := statListener 104 lock.Unlock() 105 106 fmt.Printf("In %d , out %d , consecutive drop %d\n", actStat.incnt, actStat.outcnt, actStat.consecdrop) 107 assert.True(t, actStat.incnt == arrSize) 108 assert.Equal(t, uint64(consumeCount), actStat.outcnt) 109 110 c.Close() 111 }) 112 } 113 } 114 115 func consumeStall(wc ChannelPipe, finishIdx int, maxCnt int, doneChannel chan<- int) { 116 arrs := make([]int, 0, maxCnt) 117 cnt := 0 118 LOOP: 119 for cnt < maxCnt { 120 select { 121 case <-time.NewTimer(time.Millisecond * 200).C: 122 fmt.Printf("Internal expiretime is out \n") 123 break LOOP 124 case mo := <-wc.Out(): 125 cnt++ 126 arrs = append(arrs, mo.(TestItem).ID()) 127 wc.Done() 128 // fmt.Printf("Consuming mo %s \n", mo.GetRequestID()) 129 if cnt >= finishIdx { 130 fmt.Printf("Finishing consume after index %d \n", cnt) 131 break LOOP 132 } 133 } 134 } 135 fmt.Println("Consumed ", arrs) 136 doneChannel <- cnt 137 } 138 139 func consumeStall2(wc ChannelPipe, idx int, maxCnt int, doneChannel chan<- int) { 140 arrs := make([]int, 0, maxCnt) 141 cnt := 0 142 LOOP: 143 for cnt < maxCnt { 144 select { 145 case <-time.NewTimer(time.Millisecond * 200).C: 146 fmt.Printf("Internal expiretime is out \n") 147 break LOOP 148 case mo := <-wc.Out(): 149 cnt++ 150 if cnt%4 == 3 { 151 time.Sleep(time.Millisecond) 152 } 153 arrs = append(arrs, mo.(TestItem).ID()) 154 wc.Done() 155 // fmt.Printf("Consuming mo %s \n", mo.GetRequestID()) 156 } 157 } 158 fmt.Println("Consumed ", arrs) 159 doneChannel <- cnt 160 } 161 162 type logListener struct { 163 logger *log.Logger 164 } 165 166 func (l *logListener) OnIn(element interface{}) { 167 l.logger.Info().Int("id", element.(TestItem).ID()).Msg("In") 168 } 169 170 func (l *logListener) OnDrop(element interface{}) { 171 l.logger.Info().Int("id", element.(TestItem).ID()).Msg("Drop") 172 } 173 174 func (l *logListener) OnOut(element interface{}) { 175 l.logger.Info().Int("id", element.(TestItem).ID()).Msg("Out") 176 } 177 178 type orderCheckListener struct { 179 t *testing.T 180 outId int 181 dropId int 182 } 183 184 func (l *orderCheckListener) OnIn(element interface{}) { 185 } 186 187 func (l *orderCheckListener) OnDrop(element interface{}) { 188 id := element.(TestItem).ID() 189 assert.Truef(l.t, id > l.dropId, "dropId expected higher thant %d, but %d", l.dropId, id) 190 l.dropId = id 191 } 192 193 func (l *orderCheckListener) OnOut(element interface{}) { 194 id := element.(TestItem).ID() 195 assert.Truef(l.t, id > l.outId, "outId expected higher thant %d, but %d", l.outId, id) 196 l.outId = id 197 } 198 199 func TestLongTerm(t *testing.T) { 200 // skip unit test in normal time.. 201 t.SkipNow() 202 const arrSize = 30 203 var mos [arrSize]TestItem 204 for i := 0; i < arrSize; i++ { 205 mos[i] = &testItem{i} 206 } 207 208 logger := log.NewLogger("test") 209 tests := []struct { 210 name string 211 cap int 212 testTime time.Duration 213 }{ 214 {"tlong", 20, time.Second * 10}, 215 {"tlong", 20, time.Second * 11}, 216 {"tlong", 20, time.Second * 12}, 217 {"tlong", 20, time.Second * 13}, 218 {"tlong", 20, time.Second * 14}, 219 {"tlong", 20, time.Second * 15}, 220 {"tlong", 20, time.Second * 16}, 221 {"tlong", 20, time.Second * 17}, 222 {"tlong", 20, time.Second * 18}, 223 {"tlong", 20, time.Second * 19}, 224 } 225 226 for _, tt := range tests { 227 t.Run(tt.name, func(t *testing.T) { 228 doneC := make(chan int, 1) 229 finish := make(chan interface{}) 230 statListener := NewStatLister() 231 listener := NewMultiListener(statListener, &logListener{logger}) 232 c := newDefaultChannelPipe(tt.cap, listener) 233 c.Open() 234 235 go consumeForLongTerm(c, tt.testTime+time.Minute, doneC, finish) 236 expire := time.Now().Add(tt.testTime) 237 238 i := 0 239 for time.Now().Before(expire) { 240 c.Put(mos[i%arrSize]) 241 time.Sleep(time.Millisecond * 5) 242 i++ 243 244 } 245 finish <- struct{}{} 246 consumeCount := <-doneC 247 lock := &sync.Mutex{} 248 lock.Lock() 249 actStat := statListener 250 rqueue := c.queue 251 lock.Unlock() 252 253 fmt.Printf("In %d , out %d , drop %d, consecutive drop %d\n", actStat.incnt, actStat.outcnt, actStat.dropcnt, actStat.consecdrop) 254 assert.Equal(t, actStat.incnt, uint64(i)) 255 // last one is in channel and not consumed 256 assert.Equal(t, uint64(consumeCount+1), actStat.outcnt) 257 // in should equal to sum of out, drop, and remained in queue 258 assert.Equal(t, actStat.incnt, actStat.outcnt+actStat.dropcnt+uint64(rqueue.Size())) 259 260 c.Close() 261 }) 262 } 263 } 264 265 func TestMultiLoads(t *testing.T) { 266 // skip unit test in normal time.. 267 // t.SkipNow() 268 const threadSize = 100 269 const arrSize = 30 270 var mos [threadSize][arrSize]TestItem 271 for j := 0; j < threadSize; j++ { 272 for i := 0; i < arrSize; i++ { 273 mos[j][i] = &testItem2{testItem{i}, j} 274 } 275 } 276 277 // logger := log.NewLogger("test") 278 tests := []struct { 279 name string 280 cap int 281 testTime time.Duration 282 }{ 283 {"tlong", 20, time.Second}, 284 } 285 286 for _, tt := range tests { 287 t.Run(tt.name, func(t *testing.T) { 288 doneC := make(chan int, 1) 289 finish := make(chan interface{}) 290 statListener := NewStatLister() 291 listener := NewMultiListener(statListener) 292 c := newDefaultChannelPipe(tt.cap, listener) 293 c.Open() 294 295 go consumeForLongTerm(c, tt.testTime+time.Minute, doneC, finish) 296 wg := sync.WaitGroup{} 297 expire := time.Now().Add(tt.testTime) 298 wg.Add(threadSize) 299 for j := 0; j < threadSize; j++ { 300 go func(tid int) { 301 i := 0 302 for time.Now().Before(expire) { 303 c.Put(mos[i%arrSize]) 304 i++ 305 } 306 wg.Done() 307 }(j) 308 } 309 wg.Wait() 310 finish <- struct{}{} 311 consumeCount := <-doneC 312 lock := &sync.Mutex{} 313 lock.Lock() 314 actStat := statListener 315 rqueue := c.queue 316 lock.Unlock() 317 318 fmt.Printf("In %d , out %d , drop %d, consecutive drop %d\n", actStat.incnt, actStat.outcnt, actStat.dropcnt, actStat.consecdrop) 319 // There are two cases, one is last one is in channel and not consumed, and another is consumed all items. 320 assert.True(t, actStat.outcnt-uint64(consumeCount) <= 1) 321 // in should equal to sum of out, drop, and remained in queue 322 assert.Equal(t, actStat.incnt, actStat.outcnt+actStat.dropcnt+uint64(rqueue.Size())) 323 324 c.Close() 325 }) 326 } 327 } 328 329 type testItem2 struct { 330 testItem 331 routineId int 332 } 333 334 func consumeForLongTerm(wc ChannelPipe, ttl time.Duration, doneChannel chan<- int, finishChannel <-chan interface{}) { 335 finishTime := time.NewTimer(ttl) 336 cnt := 0 337 LOOP: 338 for { 339 select { 340 case <-finishChannel: 341 fmt.Printf("Finish loop by signal\n") 342 break LOOP 343 case <-finishTime.C: 344 fmt.Printf("Finish loop by time expire \n") 345 break LOOP 346 case <-wc.Out(): 347 cnt++ 348 time.Sleep(time.Millisecond >> 2) 349 wc.Done() 350 } 351 } 352 fmt.Printf("Consumed %d items\n", cnt) 353 doneChannel <- cnt 354 }