...

Text file src/go.opentelemetry.io/otel/internal/shared/otlp/retry/retry_test.go.tmpl

Documentation: go.opentelemetry.io/otel/internal/shared/otlp/retry

     1// Code created by gotmpl. DO NOT MODIFY.
     2// source: internal/shared/otlp/retry/retry_test.go.tmpl
     3
     4// Copyright The OpenTelemetry Authors
     5//
     6// Licensed under the Apache License, Version 2.0 (the "License");
     7// you may not use this file except in compliance with the License.
     8// You may obtain a copy of the License at
     9//
    10//     http://www.apache.org/licenses/LICENSE-2.0
    11//
    12// Unless required by applicable law or agreed to in writing, software
    13// distributed under the License is distributed on an "AS IS" BASIS,
    14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15// See the License for the specific language governing permissions and
    16// limitations under the License.
    17
    18package retry
    19
    20import (
    21	"context"
    22	"errors"
    23	"math"
    24	"sync"
    25	"testing"
    26	"time"
    27
    28	"github.com/cenkalti/backoff/v4"
    29	"github.com/stretchr/testify/assert"
    30)
    31
    32func TestWait(t *testing.T) {
    33	tests := []struct {
    34		ctx      context.Context
    35		delay    time.Duration
    36		expected error
    37	}{
    38		{
    39			ctx:   context.Background(),
    40			delay: time.Duration(0),
    41		},
    42		{
    43			ctx:   context.Background(),
    44			delay: time.Duration(1),
    45		},
    46		{
    47			ctx:   context.Background(),
    48			delay: time.Duration(-1),
    49		},
    50		{
    51			ctx: func() context.Context {
    52				ctx, cancel := context.WithCancel(context.Background())
    53				cancel()
    54				return ctx
    55			}(),
    56			// Ensure the timer and context do not end simultaneously.
    57			delay:    1 * time.Hour,
    58			expected: context.Canceled,
    59		},
    60	}
    61
    62	for _, test := range tests {
    63		err := wait(test.ctx, test.delay)
    64		if test.expected == nil {
    65			assert.NoError(t, err)
    66		} else {
    67			assert.ErrorIs(t, err, test.expected)
    68		}
    69	}
    70}
    71
    72func TestNonRetryableError(t *testing.T) {
    73	ev := func(error) (bool, time.Duration) { return false, 0 }
    74
    75	reqFunc := Config{
    76		Enabled:         true,
    77		InitialInterval: 1 * time.Nanosecond,
    78		MaxInterval:     1 * time.Nanosecond,
    79		// Never stop retrying.
    80		MaxElapsedTime: 0,
    81	}.RequestFunc(ev)
    82	ctx := context.Background()
    83	assert.NoError(t, reqFunc(ctx, func(context.Context) error {
    84		return nil
    85	}))
    86	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
    87		return assert.AnError
    88	}), assert.AnError)
    89}
    90
    91func TestThrottledRetry(t *testing.T) {
    92	// Ensure the throttle delay is used by making longer than backoff delay.
    93	throttleDelay, backoffDelay := time.Second, time.Nanosecond
    94
    95	ev := func(error) (bool, time.Duration) {
    96		// Retry everything with a throttle delay.
    97		return true, throttleDelay
    98	}
    99
   100	reqFunc := Config{
   101		Enabled:         true,
   102		InitialInterval: backoffDelay,
   103		MaxInterval:     backoffDelay,
   104		// Never stop retrying.
   105		MaxElapsedTime: 0,
   106	}.RequestFunc(ev)
   107
   108	origWait := waitFunc
   109	var done bool
   110	waitFunc = func(_ context.Context, delay time.Duration) error {
   111		assert.Equal(t, throttleDelay, delay, "retry not throttled")
   112		// Try twice to ensure call is attempted again after delay.
   113		if done {
   114			return assert.AnError
   115		}
   116		done = true
   117		return nil
   118	}
   119	defer func() { waitFunc = origWait }()
   120
   121	ctx := context.Background()
   122	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
   123		return errors.New("not this error")
   124	}), assert.AnError)
   125}
   126
   127func TestBackoffRetry(t *testing.T) {
   128	ev := func(error) (bool, time.Duration) { return true, 0 }
   129
   130	delay := time.Nanosecond
   131	reqFunc := Config{
   132		Enabled:         true,
   133		InitialInterval: delay,
   134		MaxInterval:     delay,
   135		// Never stop retrying.
   136		MaxElapsedTime: 0,
   137	}.RequestFunc(ev)
   138
   139	origWait := waitFunc
   140	var done bool
   141	waitFunc = func(_ context.Context, d time.Duration) error {
   142		delta := math.Ceil(float64(delay) * backoff.DefaultRandomizationFactor)
   143		assert.InDelta(t, delay, d, delta, "retry not backoffed")
   144		// Try twice to ensure call is attempted again after delay.
   145		if done {
   146			return assert.AnError
   147		}
   148		done = true
   149		return nil
   150	}
   151	t.Cleanup(func() { waitFunc = origWait })
   152
   153	ctx := context.Background()
   154	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
   155		return errors.New("not this error")
   156	}), assert.AnError)
   157}
   158
   159func TestBackoffRetryCanceledContext(t *testing.T) {
   160	ev := func(error) (bool, time.Duration) { return true, 0 }
   161
   162	delay := time.Millisecond
   163	reqFunc := Config{
   164		Enabled:         true,
   165		InitialInterval: delay,
   166		MaxInterval:     delay,
   167		// Never stop retrying.
   168		MaxElapsedTime: 10 * time.Millisecond,
   169	}.RequestFunc(ev)
   170
   171	ctx, cancel := context.WithCancel(context.Background())
   172	count := 0
   173	cancel()
   174	err := reqFunc(ctx, func(context.Context) error {
   175		count++
   176		return assert.AnError
   177	})
   178
   179	assert.ErrorIs(t, err, context.Canceled)
   180	assert.Contains(t, err.Error(), assert.AnError.Error())
   181	assert.Equal(t, 1, count)
   182}
   183
   184func TestThrottledRetryGreaterThanMaxElapsedTime(t *testing.T) {
   185	// Ensure the throttle delay is used by making longer than backoff delay.
   186	tDelay, bDelay := time.Hour, time.Nanosecond
   187	ev := func(error) (bool, time.Duration) { return true, tDelay }
   188	reqFunc := Config{
   189		Enabled:         true,
   190		InitialInterval: bDelay,
   191		MaxInterval:     bDelay,
   192		MaxElapsedTime:  tDelay - (time.Nanosecond),
   193	}.RequestFunc(ev)
   194
   195	ctx := context.Background()
   196	assert.Contains(t, reqFunc(ctx, func(context.Context) error {
   197		return assert.AnError
   198	}).Error(), "max retry time would elapse: ")
   199}
   200
   201func TestMaxElapsedTime(t *testing.T) {
   202	ev := func(error) (bool, time.Duration) { return true, 0 }
   203	delay := time.Nanosecond
   204	reqFunc := Config{
   205		Enabled: true,
   206		// InitialInterval > MaxElapsedTime means immediate return.
   207		InitialInterval: 2 * delay,
   208		MaxElapsedTime:  delay,
   209	}.RequestFunc(ev)
   210
   211	ctx := context.Background()
   212	assert.Contains(t, reqFunc(ctx, func(context.Context) error {
   213		return assert.AnError
   214	}).Error(), "max retry time elapsed: ")
   215}
   216
   217func TestRetryNotEnabled(t *testing.T) {
   218	ev := func(error) (bool, time.Duration) {
   219		t.Error("evaluated retry when not enabled")
   220		return false, 0
   221	}
   222
   223	reqFunc := Config{}.RequestFunc(ev)
   224	ctx := context.Background()
   225	assert.NoError(t, reqFunc(ctx, func(context.Context) error {
   226		return nil
   227	}))
   228	assert.ErrorIs(t, reqFunc(ctx, func(context.Context) error {
   229		return assert.AnError
   230	}), assert.AnError)
   231}
   232
   233func TestRetryConcurrentSafe(t *testing.T) {
   234	ev := func(error) (bool, time.Duration) { return true, 0 }
   235	reqFunc := Config{
   236		Enabled: true,
   237	}.RequestFunc(ev)
   238
   239	var wg sync.WaitGroup
   240	ctx := context.Background()
   241
   242	for i := 1; i < 5; i++ {
   243		wg.Add(1)
   244
   245		go func() {
   246			defer wg.Done()
   247
   248			var done bool
   249			assert.NoError(t, reqFunc(ctx, func(context.Context) error {
   250				if !done {
   251					done = true
   252					return assert.AnError
   253				}
   254
   255				return nil
   256			}))
   257		}()
   258	}
   259
   260	wg.Wait()
   261}

View as plain text