From dd79fcd03683eabaf4ff5f6655aebb6917241934 Mon Sep 17 00:00:00 2001 From: Dmitrii Dolgov Date: Wed, 7 Feb 2018 17:04:43 +0100 Subject: [PATCH] Tests for retry_utils One can argue about how necessary they are, but at least I remembered how to do golang. --- pkg/util/retryutil/retry_util.go | 31 ++++++++++-- pkg/util/retryutil/retry_util_test.go | 68 +++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 pkg/util/retryutil/retry_util_test.go diff --git a/pkg/util/retryutil/retry_util.go b/pkg/util/retryutil/retry_util.go index 6f90353bf..1f246b466 100644 --- a/pkg/util/retryutil/retry_util.go +++ b/pkg/util/retryutil/retry_util.go @@ -5,14 +5,39 @@ import ( "time" ) -// Retry calls ConditionFunc until it returns boolean true, a timeout expires or an error occurs. +type RetryTicker interface { + Stop() + Tick() +} + +type Ticker struct { + ticker *time.Ticker +} + +func (t *Ticker) Stop() { t.ticker.Stop() } + +func (t *Ticker) Tick() { <-t.ticker.C } + +// Retry calls ConditionFunc until either: +// * it returns boolean true +// * a timeout expires +// * an error occurs func Retry(interval time.Duration, timeout time.Duration, f func() (bool, error)) error { //TODO: make the retry exponential if timeout < interval { return fmt.Errorf("timout(%s) should be greater than interval(%v)", timeout, interval) } + tick := &Ticker{time.NewTicker(interval)} + return RetryWorker(interval, timeout, f, tick) +} + +func RetryWorker( + interval time.Duration, + timeout time.Duration, + f func() (bool, error), + tick RetryTicker) error { + maxRetries := int(timeout / interval) - tick := time.NewTicker(interval) defer tick.Stop() for i := 0; ; i++ { @@ -26,7 +51,7 @@ func Retry(interval time.Duration, timeout time.Duration, f func() (bool, error) if i+1 == maxRetries { break } - <-tick.C + tick.Tick() } return fmt.Errorf("still failing after %d retries", maxRetries) } diff --git a/pkg/util/retryutil/retry_util_test.go b/pkg/util/retryutil/retry_util_test.go new file mode 100644 index 000000000..35c4756d8 --- /dev/null +++ b/pkg/util/retryutil/retry_util_test.go @@ -0,0 +1,68 @@ +package retryutil + +import ( + "errors" + "testing" +) + +type mockTicker struct { + test *testing.T + counter int +} + +func (t *mockTicker) Stop() {} + +func (t *mockTicker) Tick() { + t.counter += 1 +} + +func TestRetryWorkerSuccess(t *testing.T) { + tick := &mockTicker{t, 0} + result := RetryWorker(10, 20, func() (bool, error) { + return true, nil + }, tick) + + if result != nil { + t.Errorf("Wrong result, expected: %#v, got: %#v", nil, result) + } + + if tick.counter != 0 { + t.Errorf("Ticker was started once, but it shouldn't be") + } +} + +func TestRetryWorkerOneFalse(t *testing.T) { + var counter = 0 + + tick := &mockTicker{t, 0} + result := RetryWorker(1, 3, func() (bool, error) { + counter += 1 + + if counter <= 1 { + return false, nil + } else { + return true, nil + } + }, tick) + + if result != nil { + t.Errorf("Wrong result, expected: %#v, got: %#v", nil, result) + } + + if tick.counter != 1 { + t.Errorf("Ticker was started %#v, but supposed to be just once", tick.counter) + } +} + +func TestRetryWorkerError(t *testing.T) { + fail := errors.New("Error") + + tick := &mockTicker{t, 0} + result := RetryWorker(1, 3, func() (bool, error) { + return false, fail + }, tick) + + if result != fail { + t.Errorf("Wrong result, expected: %#v, got: %#v", fail, result) + } +}