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  }