123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- package retry
- import (
- "context"
- "testing"
- "time"
- )
- func immediateTimeAfter(time.Duration) <-chan time.Time {
- c := make(chan time.Time, 1)
- c <- time.Now()
- return c
- }
- func TestBackoffRetries(t *testing.T) {
- ctx := context.Background()
- // make backoff return immediately
- backoff := BackoffHandler{maxRetries: 3, Clock: Clock{time.Now, immediateTimeAfter}}
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff failed immediately")
- }
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff failed after 1 retry")
- }
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff failed after 2 retry")
- }
- if backoff.Backoff(ctx) {
- t.Fatalf("backoff allowed after 3 (max) retries")
- }
- }
- func TestBackoffCancel(t *testing.T) {
- ctx, cancelFunc := context.WithCancel(context.Background())
- // prevent backoff from returning normally
- after := func(time.Duration) <-chan time.Time { return make(chan time.Time) }
- backoff := BackoffHandler{maxRetries: 3, Clock: Clock{time.Now, after}}
- cancelFunc()
- if backoff.Backoff(ctx) {
- t.Fatalf("backoff allowed after cancel")
- }
- if _, ok := backoff.GetMaxBackoffDuration(ctx); ok {
- t.Fatalf("backoff allowed after cancel")
- }
- }
- func TestBackoffGracePeriod(t *testing.T) {
- ctx := context.Background()
- currentTime := time.Now()
- // make Clock.Now return whatever we like
- now := func() time.Time { return currentTime }
- // make backoff return immediately
- backoff := BackoffHandler{maxRetries: 1, Clock: Clock{now, immediateTimeAfter}}
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff failed immediately")
- }
- // the next call to Backoff would fail unless it's after the grace period
- gracePeriod := backoff.SetGracePeriod()
- // advance time to after the grace period, which at most will be 8 seconds, but we will advance +1 second.
- currentTime = currentTime.Add(gracePeriod + time.Second)
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff failed after the grace period expired")
- }
- // confirm we ignore grace period after backoff
- if backoff.Backoff(ctx) {
- t.Fatalf("backoff allowed after 1 (max) retry")
- }
- }
- func TestGetMaxBackoffDurationRetries(t *testing.T) {
- ctx := context.Background()
- // make backoff return immediately
- backoff := BackoffHandler{maxRetries: 3, Clock: Clock{time.Now, immediateTimeAfter}}
- if _, ok := backoff.GetMaxBackoffDuration(ctx); !ok {
- t.Fatalf("backoff failed immediately")
- }
- backoff.Backoff(ctx) // noop
- if _, ok := backoff.GetMaxBackoffDuration(ctx); !ok {
- t.Fatalf("backoff failed after 1 retry")
- }
- backoff.Backoff(ctx) // noop
- if _, ok := backoff.GetMaxBackoffDuration(ctx); !ok {
- t.Fatalf("backoff failed after 2 retry")
- }
- backoff.Backoff(ctx) // noop
- if _, ok := backoff.GetMaxBackoffDuration(ctx); ok {
- t.Fatalf("backoff allowed after 3 (max) retries")
- }
- if backoff.Backoff(ctx) {
- t.Fatalf("backoff allowed after 3 (max) retries")
- }
- }
- func TestGetMaxBackoffDuration(t *testing.T) {
- ctx := context.Background()
- // make backoff return immediately
- backoff := BackoffHandler{maxRetries: 3, Clock: Clock{time.Now, immediateTimeAfter}}
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*2 {
- t.Fatalf("backoff (%s) didn't return < 2 seconds on first retry", duration)
- }
- backoff.Backoff(ctx) // noop
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*4 {
- t.Fatalf("backoff (%s) didn't return < 4 seconds on second retry", duration)
- }
- backoff.Backoff(ctx) // noop
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*8 {
- t.Fatalf("backoff (%s) didn't return < 8 seconds on third retry", duration)
- }
- backoff.Backoff(ctx) // noop
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); ok || duration != 0 {
- t.Fatalf("backoff (%s) didn't return 0 seconds on fourth retry (exceeding limit)", duration)
- }
- }
- func TestBackoffRetryForever(t *testing.T) {
- ctx := context.Background()
- // make backoff return immediately
- backoff := BackoffHandler{maxRetries: 3, retryForever: true, Clock: Clock{time.Now, immediateTimeAfter}}
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*2 {
- t.Fatalf("backoff (%s) didn't return < 2 seconds on first retry", duration)
- }
- backoff.Backoff(ctx) // noop
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*4 {
- t.Fatalf("backoff (%s) didn't return < 4 seconds on second retry", duration)
- }
- backoff.Backoff(ctx) // noop
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*8 {
- t.Fatalf("backoff (%s) didn't return < 8 seconds on third retry", duration)
- }
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff refused on fourth retry despire RetryForever")
- }
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*16 {
- t.Fatalf("backoff returned %v instead of 8 seconds on fourth retry", duration)
- }
- if !backoff.Backoff(ctx) {
- t.Fatalf("backoff refused on fifth retry despire RetryForever")
- }
- if duration, ok := backoff.GetMaxBackoffDuration(ctx); !ok || duration > time.Second*16 {
- t.Fatalf("backoff returned %v instead of 8 seconds on fifth retry", duration)
- }
- }
|