// Copyright 2016, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. package gax import ( "errors" "testing" "time" "golang.org/x/net/context" ) var canceledContext context.Context func init() { ctx, cancel := context.WithCancel(context.Background()) cancel() canceledContext = ctx } // recordSleeper is a test implementation of sleeper. type recordSleeper int func (s *recordSleeper) sleep(ctx context.Context, _ time.Duration) error { *s++ return ctx.Err() } type boolRetryer bool func (r boolRetryer) Retry(err error) (time.Duration, bool) { return 0, bool(r) } func TestInvokeSuccess(t *testing.T) { apiCall := func(context.Context, CallSettings) error { return nil } var sp recordSleeper err := invoke(context.Background(), apiCall, CallSettings{}, sp.sleep) if err != nil { t.Errorf("found error %s, want nil", err) } if sp != 0 { t.Errorf("slept %d times, should not have slept since the call succeeded", int(sp)) } } func TestInvokeNoRetry(t *testing.T) { apiErr := errors.New("foo error") apiCall := func(context.Context, CallSettings) error { return apiErr } var sp recordSleeper err := invoke(context.Background(), apiCall, CallSettings{}, sp.sleep) if err != apiErr { t.Errorf("found error %s, want %s", err, apiErr) } if sp != 0 { t.Errorf("slept %d times, should not have slept since retry is not specified", int(sp)) } } func TestInvokeNilRetry(t *testing.T) { apiErr := errors.New("foo error") apiCall := func(context.Context, CallSettings) error { return apiErr } var settings CallSettings WithRetry(func() Retryer { return nil }).Resolve(&settings) var sp recordSleeper err := invoke(context.Background(), apiCall, settings, sp.sleep) if err != apiErr { t.Errorf("found error %s, want %s", err, apiErr) } if sp != 0 { t.Errorf("slept %d times, should not have slept since retry is not specified", int(sp)) } } func TestInvokeNeverRetry(t *testing.T) { apiErr := errors.New("foo error") apiCall := func(context.Context, CallSettings) error { return apiErr } var settings CallSettings WithRetry(func() Retryer { return boolRetryer(false) }).Resolve(&settings) var sp recordSleeper err := invoke(context.Background(), apiCall, settings, sp.sleep) if err != apiErr { t.Errorf("found error %s, want %s", err, apiErr) } if sp != 0 { t.Errorf("slept %d times, should not have slept since retry is not specified", int(sp)) } } func TestInvokeRetry(t *testing.T) { const target = 3 retryNum := 0 apiErr := errors.New("foo error") apiCall := func(context.Context, CallSettings) error { retryNum++ if retryNum < target { return apiErr } return nil } var settings CallSettings WithRetry(func() Retryer { return boolRetryer(true) }).Resolve(&settings) var sp recordSleeper err := invoke(context.Background(), apiCall, settings, sp.sleep) if err != nil { t.Errorf("found error %s, want nil, call should have succeeded after %d tries", err, target) } if sp != target-1 { t.Errorf("retried %d times, want %d", int(sp), int(target-1)) } } func TestInvokeRetryTimeout(t *testing.T) { apiErr := errors.New("foo error") apiCall := func(context.Context, CallSettings) error { return apiErr } var settings CallSettings WithRetry(func() Retryer { return boolRetryer(true) }).Resolve(&settings) var sp recordSleeper err := invoke(canceledContext, apiCall, settings, sp.sleep) if err != context.Canceled { t.Errorf("found error %s, want %s", err, context.Canceled) } }