github.com/lingyao2333/mo-zero@v1.4.1/core/executors/periodicalexecutor_test.go (about) 1 package executors 2 3 import ( 4 "runtime" 5 "sync" 6 "sync/atomic" 7 "testing" 8 "time" 9 10 "github.com/lingyao2333/mo-zero/core/timex" 11 "github.com/stretchr/testify/assert" 12 ) 13 14 const threshold = 10 15 16 type container struct { 17 interval time.Duration 18 tasks []int 19 execute func(tasks interface{}) 20 } 21 22 func newContainer(interval time.Duration, execute func(tasks interface{})) *container { 23 return &container{ 24 interval: interval, 25 execute: execute, 26 } 27 } 28 29 func (c *container) AddTask(task interface{}) bool { 30 c.tasks = append(c.tasks, task.(int)) 31 return len(c.tasks) > threshold 32 } 33 34 func (c *container) Execute(tasks interface{}) { 35 if c.execute != nil { 36 c.execute(tasks) 37 } else { 38 time.Sleep(c.interval) 39 } 40 } 41 42 func (c *container) RemoveAll() interface{} { 43 tasks := c.tasks 44 c.tasks = nil 45 return tasks 46 } 47 48 func TestPeriodicalExecutor_Sync(t *testing.T) { 49 var done int32 50 exec := NewPeriodicalExecutor(time.Second, newContainer(time.Millisecond*500, nil)) 51 exec.Sync(func() { 52 atomic.AddInt32(&done, 1) 53 }) 54 assert.Equal(t, int32(1), atomic.LoadInt32(&done)) 55 } 56 57 func TestPeriodicalExecutor_QuitGoroutine(t *testing.T) { 58 ticker := timex.NewFakeTicker() 59 exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil)) 60 exec.newTicker = func(d time.Duration) timex.Ticker { 61 return ticker 62 } 63 routines := runtime.NumGoroutine() 64 exec.Add(1) 65 ticker.Tick() 66 ticker.Wait(time.Millisecond * idleRound * 2) 67 ticker.Tick() 68 ticker.Wait(time.Millisecond * idleRound) 69 assert.Equal(t, routines, runtime.NumGoroutine()) 70 } 71 72 func TestPeriodicalExecutor_Bulk(t *testing.T) { 73 ticker := timex.NewFakeTicker() 74 var vals []int 75 // avoid data race 76 var lock sync.Mutex 77 exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks interface{}) { 78 t := tasks.([]int) 79 for _, each := range t { 80 lock.Lock() 81 vals = append(vals, each) 82 lock.Unlock() 83 } 84 })) 85 exec.newTicker = func(d time.Duration) timex.Ticker { 86 return ticker 87 } 88 for i := 0; i < threshold*10; i++ { 89 if i%threshold == 5 { 90 time.Sleep(time.Millisecond * idleRound * 2) 91 } 92 exec.Add(i) 93 } 94 ticker.Tick() 95 ticker.Wait(time.Millisecond * idleRound * 2) 96 ticker.Tick() 97 ticker.Tick() 98 ticker.Wait(time.Millisecond * idleRound) 99 var expect []int 100 for i := 0; i < threshold*10; i++ { 101 expect = append(expect, i) 102 } 103 104 lock.Lock() 105 assert.EqualValues(t, expect, vals) 106 lock.Unlock() 107 } 108 109 func TestPeriodicalExecutor_Wait(t *testing.T) { 110 var lock sync.Mutex 111 executer := NewBulkExecutor(func(tasks []interface{}) { 112 lock.Lock() 113 defer lock.Unlock() 114 time.Sleep(10 * time.Millisecond) 115 }, WithBulkTasks(1), WithBulkInterval(time.Second)) 116 for i := 0; i < 10; i++ { 117 executer.Add(1) 118 } 119 executer.Flush() 120 executer.Wait() 121 } 122 123 func TestPeriodicalExecutor_WaitFast(t *testing.T) { 124 const total = 3 125 var cnt int 126 var lock sync.Mutex 127 executer := NewBulkExecutor(func(tasks []interface{}) { 128 defer func() { 129 cnt++ 130 }() 131 lock.Lock() 132 defer lock.Unlock() 133 time.Sleep(10 * time.Millisecond) 134 }, WithBulkTasks(1), WithBulkInterval(10*time.Millisecond)) 135 for i := 0; i < total; i++ { 136 executer.Add(2) 137 } 138 executer.Flush() 139 executer.Wait() 140 assert.Equal(t, total, cnt) 141 } 142 143 func TestPeriodicalExecutor_Deadlock(t *testing.T) { 144 executor := NewBulkExecutor(func(tasks []interface{}) { 145 }, WithBulkTasks(1), WithBulkInterval(time.Millisecond)) 146 for i := 0; i < 1e5; i++ { 147 executor.Add(1) 148 } 149 } 150 151 func TestPeriodicalExecutor_hasTasks(t *testing.T) { 152 ticker := timex.NewFakeTicker() 153 defer ticker.Stop() 154 155 exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil)) 156 exec.newTicker = func(d time.Duration) timex.Ticker { 157 return ticker 158 } 159 assert.False(t, exec.hasTasks(nil)) 160 assert.True(t, exec.hasTasks(1)) 161 } 162 163 // go test -benchtime 10s -bench . 164 func BenchmarkExecutor(b *testing.B) { 165 b.ReportAllocs() 166 167 executor := NewPeriodicalExecutor(time.Second, newContainer(time.Millisecond*500, nil)) 168 for i := 0; i < b.N; i++ { 169 executor.Add(1) 170 } 171 }