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  }