github.com/zorawar87/trillian@v1.2.1/client/backoff/backoff_test.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package backoff
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"testing"
    21  	"time"
    22  
    23  	_ "github.com/golang/glog"
    24  )
    25  
    26  func TestBackoff(t *testing.T) {
    27  	b := Backoff{
    28  		Min:    time.Duration(1),
    29  		Max:    time.Duration(100),
    30  		Factor: 2,
    31  	}
    32  	for _, test := range []struct {
    33  		b     Backoff
    34  		times int
    35  		want  time.Duration
    36  	}{
    37  		{b, 1, time.Duration(1)},
    38  		{b, 2, time.Duration(2)},
    39  		{b, 3, time.Duration(4)},
    40  		{b, 4, time.Duration(8)},
    41  		{b, 8, time.Duration(100)},
    42  	} {
    43  		test.b.Reset()
    44  		var got time.Duration
    45  		for i := 0; i < test.times; i++ {
    46  			got = test.b.Duration()
    47  		}
    48  		if got != test.want {
    49  			t.Errorf("Duration() %v times: %v, want %v", test.times, got, test.want)
    50  		}
    51  	}
    52  }
    53  
    54  func TestJitter(t *testing.T) {
    55  	b := Backoff{
    56  		Min:    1 * time.Second,
    57  		Max:    100 * time.Second,
    58  		Factor: 2,
    59  		Jitter: true,
    60  	}
    61  	for _, test := range []struct {
    62  		b     Backoff
    63  		times int
    64  		min   time.Duration
    65  		max   time.Duration
    66  	}{
    67  		{b, 1, 1 * time.Second, 2 * time.Second},
    68  		{b, 2, 2 * time.Second, 4 * time.Second},
    69  		{b, 3, 4 * time.Second, 8 * time.Second},
    70  		{b, 4, 8 * time.Second, 16 * time.Second},
    71  		{b, 8, 100 * time.Second, 200 * time.Second},
    72  	} {
    73  		test.b.Reset()
    74  		var got1 time.Duration
    75  		for i := 0; i < test.times; i++ {
    76  			got1 = test.b.Duration()
    77  		}
    78  		if got1 < test.min || got1 > test.max {
    79  			t.Errorf("Duration() %v times, want  %v < %v < %v", test.times, test.min, got1, test.max)
    80  		}
    81  
    82  		// Ensure a random value is being produced.
    83  		test.b.Reset()
    84  		var got2 time.Duration
    85  		for i := 0; i < test.times; i++ {
    86  			got2 = test.b.Duration()
    87  		}
    88  		if got1 == got2 {
    89  			t.Errorf("Duration() %v times == Duration() %v times, want  %v != %v",
    90  				test.times, test.times, got1, got2)
    91  		}
    92  	}
    93  }
    94  
    95  func TestRetry(t *testing.T) {
    96  	b := Backoff{
    97  		Min:    50 * time.Millisecond,
    98  		Max:    200 * time.Millisecond,
    99  		Factor: 2,
   100  	}
   101  
   102  	// callCount is used by some test funcs to count how many times they've been called.
   103  	var callCount int
   104  	// ctx used by Retry(), declared here to that test.ctxFunc can set it.
   105  	var ctx context.Context
   106  	var cancel context.CancelFunc
   107  
   108  	for _, test := range []struct {
   109  		name    string
   110  		f       func() error
   111  		ctxFunc func()
   112  		wantErr bool
   113  	}{
   114  		{
   115  			name: "func that immediately succeeds",
   116  			f:    func() error { return nil },
   117  		},
   118  		{
   119  			name: "func that succeeds on second attempt",
   120  			f: func() error {
   121  				callCount++
   122  				if callCount == 1 {
   123  					return errors.New("error")
   124  				}
   125  				return nil
   126  			},
   127  		},
   128  		{
   129  			name: "func that takes too long to succeed",
   130  			f: func() error {
   131  				// Cancel the context and return an error. This func will succeed on
   132  				// any future calls, but it should not be retried due to the context
   133  				// being cancelled.
   134  				if ctx.Err() == nil {
   135  					cancel()
   136  					return errors.New("error")
   137  				}
   138  				return nil
   139  			},
   140  			wantErr: true,
   141  		},
   142  		{
   143  			name: "context done before Retry() called",
   144  			f: func() error {
   145  				return nil
   146  			},
   147  			ctxFunc: func() {
   148  				ctx, cancel = context.WithCancel(context.Background())
   149  				cancel()
   150  			},
   151  			wantErr: true,
   152  		},
   153  	} {
   154  		if test.ctxFunc != nil {
   155  			test.ctxFunc()
   156  		} else {
   157  			ctx, cancel = context.WithCancel(context.Background())
   158  		}
   159  
   160  		callCount = 0
   161  		err := b.Retry(ctx, test.f)
   162  		cancel()
   163  		if gotErr := err != nil; gotErr != test.wantErr {
   164  			t.Errorf("%v: Retry() = %v, want err? %v", test.name, err, test.wantErr)
   165  		}
   166  	}
   167  }