github.com/aergoio/aergo@v1.3.1/p2p/p2putil/timedinvoke_test.go (about)

     1  /*
     2   * @file
     3   * @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package p2putil
     7  
     8  import (
     9  	"fmt"
    10  	"sync/atomic"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/aergoio/aergo-lib/log"
    15  	"github.com/stretchr/testify/assert"
    16  )
    17  
    18  var logger *log.Logger
    19  
    20  func init() {
    21  	logger = log.NewLogger("test")
    22  }
    23  
    24  func Test_InvokeWithTimerSimple(t *testing.T) {
    25  	tests := []struct {
    26  		name      string
    27  		ttl       time.Duration
    28  		iteration int
    29  		expectErr bool
    30  		expected  interface{}
    31  	}{
    32  		{"TFast", time.Second, 2, false, 2},
    33  		{"TTimeout", time.Millisecond * 200, 1000, true, nil},
    34  	}
    35  	for _, test := range tests {
    36  		t.Run(test.name, func(t *testing.T) {
    37  			m := &sampleConstCaller{iteration: test.iteration}
    38  			actual, err := InvokeWithTimer(m, time.NewTimer(test.ttl))
    39  			assert.Equal(t, test.expectErr, err != nil)
    40  			if !test.expectErr {
    41  				assert.Equal(t, test.expected, actual)
    42  			}
    43  		})
    44  	}
    45  }
    46  
    47  type sampleConstCaller struct {
    48  	iteration int
    49  	cancel    int32
    50  }
    51  
    52  func (c *sampleConstCaller) DoCall(done chan<- interface{}) {
    53  	i := 0
    54  	for ; i < c.iteration; i++ {
    55  		fmt.Printf("%d th iteration \n", i)
    56  		time.Sleep(time.Millisecond * 50)
    57  		if atomic.LoadInt32(&c.cancel) != 0 {
    58  			// actually no put to done channel is needed.
    59  			return
    60  		}
    61  	}
    62  	done <- i
    63  }
    64  
    65  func (c *sampleConstCaller) Cancel() {
    66  	atomic.StoreInt32(&c.cancel, 1)
    67  }
    68  
    69  func Test_InvokeWithTimerRetainedTime(t *testing.T) {
    70  	// some test will be resulted by process time. so run it manually and make skip it in CI test
    71  	t.SkipNow()
    72  	tests := []struct {
    73  		name        string
    74  		ttl         time.Duration
    75  		iteration   int
    76  		retainCount int
    77  		expectErr   bool
    78  		expected    interface{}
    79  	}{
    80  
    81  		{"TTimeout", time.Millisecond * 100, 4, 1, true, nil},
    82  		{"TSuccRetain", time.Millisecond * 100, 4, 5, false, 4},
    83  		{"TTooLongIfRetain", time.Millisecond * 20, 1000, 100, true, nil},
    84  	}
    85  	for _, test := range tests {
    86  		t.Run(test.name, func(t *testing.T) {
    87  			m := &ratainableCaller{iteration: test.iteration, retainCount: test.retainCount, retainDuration: test.ttl, timer: time.NewTimer(test.ttl)}
    88  			actual, err := InvokeWithTimer(m, m.timer)
    89  			assert.Equal(t, test.expectErr, err != nil)
    90  			if !test.expectErr {
    91  				assert.Equal(t, test.expected, actual)
    92  			}
    93  			logger.Info().Str("name", test.name).Msg("Test finished")
    94  
    95  		})
    96  	}
    97  	time.Sleep(time.Millisecond * 200)
    98  }
    99  
   100  type ratainableCaller struct {
   101  	retainCount    int
   102  	retainDuration time.Duration
   103  	timer          *time.Timer
   104  
   105  	iteration int
   106  	cancel    int32
   107  }
   108  
   109  func (c *ratainableCaller) DoCall(done chan<- interface{}) {
   110  	retainCnt := c.retainCount
   111  	i := 0
   112  	for ; i < c.iteration; i++ {
   113  		fmt.Printf("%d th iteration \n", i)
   114  		time.Sleep(time.Millisecond * 50)
   115  		if retainCnt > 0 {
   116  			// do not retain if timer was already expired. and return soon because cancel will be set.
   117  			if !c.timer.Stop() {
   118  				logger.Info().Msg("Timer already expired.")
   119  				logger.Info().Msg("make place holder.")
   120  				time.Sleep(time.Millisecond * 100)
   121  				logger.Info().Msg("out")
   122  				return
   123  			}
   124  			c.timer.Reset(c.retainDuration)
   125  			retainCnt--
   126  			logger.Info().Msg("Retained Timer")
   127  		}
   128  		if atomic.LoadInt32(&c.cancel) != 0 {
   129  			// actually no put to done channel is needed.
   130  			return
   131  		}
   132  	}
   133  	done <- i
   134  }
   135  
   136  func (c *ratainableCaller) Cancel() {
   137  	atomic.StoreInt32(&c.cancel, 1)
   138  }