github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/wait/wait_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package wait
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math/rand"
    24  	"sync"
    25  	"sync/atomic"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/runtime"
    30  	"k8s.io/utils/clock"
    31  	testingclock "k8s.io/utils/clock/testing"
    32  )
    33  
    34  func TestUntil(t *testing.T) {
    35  	ch := make(chan struct{})
    36  	close(ch)
    37  	Until(func() {
    38  		t.Fatal("should not have been invoked")
    39  	}, 0, ch)
    40  
    41  	ch = make(chan struct{})
    42  	called := make(chan struct{})
    43  	go func() {
    44  		Until(func() {
    45  			called <- struct{}{}
    46  		}, 0, ch)
    47  		close(called)
    48  	}()
    49  	<-called
    50  	close(ch)
    51  	<-called
    52  }
    53  
    54  func TestUntilWithContext(t *testing.T) {
    55  	ctx, cancel := context.WithCancel(context.TODO())
    56  	cancel()
    57  	UntilWithContext(ctx, func(context.Context) {
    58  		t.Fatal("should not have been invoked")
    59  	}, 0)
    60  
    61  	ctx, cancel = context.WithCancel(context.TODO())
    62  	called := make(chan struct{})
    63  	go func() {
    64  		UntilWithContext(ctx, func(context.Context) {
    65  			called <- struct{}{}
    66  		}, 0)
    67  		close(called)
    68  	}()
    69  	<-called
    70  	cancel()
    71  	<-called
    72  }
    73  
    74  func TestNonSlidingUntil(t *testing.T) {
    75  	ch := make(chan struct{})
    76  	close(ch)
    77  	NonSlidingUntil(func() {
    78  		t.Fatal("should not have been invoked")
    79  	}, 0, ch)
    80  
    81  	ch = make(chan struct{})
    82  	called := make(chan struct{})
    83  	go func() {
    84  		NonSlidingUntil(func() {
    85  			called <- struct{}{}
    86  		}, 0, ch)
    87  		close(called)
    88  	}()
    89  	<-called
    90  	close(ch)
    91  	<-called
    92  }
    93  
    94  func TestNonSlidingUntilWithContext(t *testing.T) {
    95  	ctx, cancel := context.WithCancel(context.TODO())
    96  	cancel()
    97  	NonSlidingUntilWithContext(ctx, func(context.Context) {
    98  		t.Fatal("should not have been invoked")
    99  	}, 0)
   100  
   101  	ctx, cancel = context.WithCancel(context.TODO())
   102  	called := make(chan struct{})
   103  	go func() {
   104  		NonSlidingUntilWithContext(ctx, func(context.Context) {
   105  			called <- struct{}{}
   106  		}, 0)
   107  		close(called)
   108  	}()
   109  	<-called
   110  	cancel()
   111  	<-called
   112  }
   113  
   114  func TestUntilReturnsImmediately(t *testing.T) {
   115  	now := time.Now()
   116  	ch := make(chan struct{})
   117  	Until(func() {
   118  		close(ch)
   119  	}, 30*time.Second, ch)
   120  	if now.Add(25 * time.Second).Before(time.Now()) {
   121  		t.Errorf("Until did not return immediately when the stop chan was closed inside the func")
   122  	}
   123  }
   124  
   125  func TestJitterUntil(t *testing.T) {
   126  	ch := make(chan struct{})
   127  	// if a channel is closed JitterUntil never calls function f
   128  	// and returns immediately
   129  	close(ch)
   130  	JitterUntil(func() {
   131  		t.Fatal("should not have been invoked")
   132  	}, 0, 1.0, true, ch)
   133  
   134  	ch = make(chan struct{})
   135  	called := make(chan struct{})
   136  	go func() {
   137  		JitterUntil(func() {
   138  			called <- struct{}{}
   139  		}, 0, 1.0, true, ch)
   140  		close(called)
   141  	}()
   142  	<-called
   143  	close(ch)
   144  	<-called
   145  }
   146  
   147  func TestJitterUntilWithContext(t *testing.T) {
   148  	ctx, cancel := context.WithCancel(context.TODO())
   149  	cancel()
   150  	JitterUntilWithContext(ctx, func(context.Context) {
   151  		t.Fatal("should not have been invoked")
   152  	}, 0, 1.0, true)
   153  
   154  	ctx, cancel = context.WithCancel(context.TODO())
   155  	called := make(chan struct{})
   156  	go func() {
   157  		JitterUntilWithContext(ctx, func(context.Context) {
   158  			called <- struct{}{}
   159  		}, 0, 1.0, true)
   160  		close(called)
   161  	}()
   162  	<-called
   163  	cancel()
   164  	<-called
   165  }
   166  
   167  func TestJitterUntilReturnsImmediately(t *testing.T) {
   168  	now := time.Now()
   169  	ch := make(chan struct{})
   170  	JitterUntil(func() {
   171  		close(ch)
   172  	}, 30*time.Second, 1.0, true, ch)
   173  	if now.Add(25 * time.Second).Before(time.Now()) {
   174  		t.Errorf("JitterUntil did not return immediately when the stop chan was closed inside the func")
   175  	}
   176  }
   177  
   178  func TestJitterUntilRecoversPanic(t *testing.T) {
   179  	// Save and restore crash handlers
   180  	originalReallyCrash := runtime.ReallyCrash
   181  	originalHandlers := runtime.PanicHandlers
   182  	defer func() {
   183  		runtime.ReallyCrash = originalReallyCrash
   184  		runtime.PanicHandlers = originalHandlers
   185  	}()
   186  
   187  	called := 0
   188  	handled := 0
   189  
   190  	// Hook up a custom crash handler to ensure it is called when a jitter function panics
   191  	runtime.ReallyCrash = false
   192  	runtime.PanicHandlers = []func(interface{}){
   193  		func(p interface{}) {
   194  			handled++
   195  		},
   196  	}
   197  
   198  	ch := make(chan struct{})
   199  	JitterUntil(func() {
   200  		called++
   201  		if called > 2 {
   202  			close(ch)
   203  			return
   204  		}
   205  		panic("TestJitterUntilRecoversPanic")
   206  	}, time.Millisecond, 1.0, true, ch)
   207  
   208  	if called != 3 {
   209  		t.Errorf("Expected panic recovers")
   210  	}
   211  }
   212  
   213  func TestJitterUntilNegativeFactor(t *testing.T) {
   214  	now := time.Now()
   215  	ch := make(chan struct{})
   216  	called := make(chan struct{})
   217  	received := make(chan struct{})
   218  	go func() {
   219  		JitterUntil(func() {
   220  			called <- struct{}{}
   221  			<-received
   222  		}, time.Second, -30.0, true, ch)
   223  	}()
   224  	// first loop
   225  	<-called
   226  	received <- struct{}{}
   227  	// second loop
   228  	<-called
   229  	close(ch)
   230  	received <- struct{}{}
   231  
   232  	// it should take at most 2 seconds + some overhead, not 3
   233  	if now.Add(3 * time.Second).Before(time.Now()) {
   234  		t.Errorf("JitterUntil did not returned after predefined period with negative jitter factor when the stop chan was closed inside the func")
   235  	}
   236  
   237  }
   238  
   239  func TestExponentialBackoff(t *testing.T) {
   240  	opts := Backoff{Factor: 1.0, Steps: 3}
   241  
   242  	// waits up to steps
   243  	i := 0
   244  	err := ExponentialBackoff(opts, func() (bool, error) {
   245  		i++
   246  		return false, nil
   247  	})
   248  	if err != ErrWaitTimeout || i != opts.Steps {
   249  		t.Errorf("unexpected error: %v", err)
   250  	}
   251  
   252  	// returns immediately
   253  	i = 0
   254  	err = ExponentialBackoff(opts, func() (bool, error) {
   255  		i++
   256  		return true, nil
   257  	})
   258  	if err != nil || i != 1 {
   259  		t.Errorf("unexpected error: %v", err)
   260  	}
   261  
   262  	// returns immediately on error
   263  	testErr := fmt.Errorf("some other error")
   264  	err = ExponentialBackoff(opts, func() (bool, error) {
   265  		return false, testErr
   266  	})
   267  	if err != testErr {
   268  		t.Errorf("unexpected error: %v", err)
   269  	}
   270  
   271  	// invoked multiple times
   272  	i = 1
   273  	err = ExponentialBackoff(opts, func() (bool, error) {
   274  		if i < opts.Steps {
   275  			i++
   276  			return false, nil
   277  		}
   278  		return true, nil
   279  	})
   280  	if err != nil || i != opts.Steps {
   281  		t.Errorf("unexpected error: %v", err)
   282  	}
   283  }
   284  
   285  func TestPoller(t *testing.T) {
   286  	ctx, cancel := context.WithCancel(context.Background())
   287  	defer cancel()
   288  	w := poller(time.Millisecond, 2*time.Millisecond)
   289  	ch := w(ctx)
   290  	count := 0
   291  DRAIN:
   292  	for {
   293  		select {
   294  		case _, open := <-ch:
   295  			if !open {
   296  				break DRAIN
   297  			}
   298  			count++
   299  		case <-time.After(ForeverTestTimeout):
   300  			t.Errorf("unexpected timeout after poll")
   301  		}
   302  	}
   303  	if count > 3 {
   304  		t.Errorf("expected up to three values, got %d", count)
   305  	}
   306  }
   307  
   308  type fakePoller struct {
   309  	max  int
   310  	used int32 // accessed with atomics
   311  	wg   sync.WaitGroup
   312  }
   313  
   314  func fakeTicker(max int, used *int32, doneFunc func()) WaitFunc {
   315  	return func(done <-chan struct{}) <-chan struct{} {
   316  		ch := make(chan struct{})
   317  		go func() {
   318  			defer doneFunc()
   319  			defer close(ch)
   320  			for i := 0; i < max; i++ {
   321  				select {
   322  				case ch <- struct{}{}:
   323  				case <-done:
   324  					return
   325  				}
   326  				if used != nil {
   327  					atomic.AddInt32(used, 1)
   328  				}
   329  			}
   330  		}()
   331  		return ch
   332  	}
   333  }
   334  
   335  func (fp *fakePoller) GetWaitFunc() WaitFunc {
   336  	fp.wg.Add(1)
   337  	return fakeTicker(fp.max, &fp.used, fp.wg.Done)
   338  }
   339  
   340  func TestPoll(t *testing.T) {
   341  	invocations := 0
   342  	f := ConditionFunc(func() (bool, error) {
   343  		invocations++
   344  		return true, nil
   345  	})
   346  	fp := fakePoller{max: 1}
   347  
   348  	ctx, cancel := context.WithCancel(context.Background())
   349  	defer cancel()
   350  	if err := poll(ctx, false, fp.GetWaitFunc().WithContext(), f.WithContext()); err != nil {
   351  		t.Fatalf("unexpected error %v", err)
   352  	}
   353  	fp.wg.Wait()
   354  	if invocations != 1 {
   355  		t.Errorf("Expected exactly one invocation, got %d", invocations)
   356  	}
   357  	used := atomic.LoadInt32(&fp.used)
   358  	if used != 1 {
   359  		t.Errorf("Expected exactly one tick, got %d", used)
   360  	}
   361  }
   362  
   363  func TestPollError(t *testing.T) {
   364  	expectedError := errors.New("Expected error")
   365  	f := ConditionFunc(func() (bool, error) {
   366  		return false, expectedError
   367  	})
   368  	fp := fakePoller{max: 1}
   369  
   370  	ctx, cancel := context.WithCancel(context.Background())
   371  	defer cancel()
   372  	if err := poll(ctx, false, fp.GetWaitFunc().WithContext(), f.WithContext()); err == nil || err != expectedError {
   373  		t.Fatalf("Expected error %v, got none %v", expectedError, err)
   374  	}
   375  	fp.wg.Wait()
   376  	used := atomic.LoadInt32(&fp.used)
   377  	if used != 1 {
   378  		t.Errorf("Expected exactly one tick, got %d", used)
   379  	}
   380  }
   381  
   382  func TestPollImmediate(t *testing.T) {
   383  	invocations := 0
   384  	f := ConditionFunc(func() (bool, error) {
   385  		invocations++
   386  		return true, nil
   387  	})
   388  	fp := fakePoller{max: 0}
   389  
   390  	ctx, cancel := context.WithCancel(context.Background())
   391  	defer cancel()
   392  	if err := poll(ctx, true, fp.GetWaitFunc().WithContext(), f.WithContext()); err != nil {
   393  		t.Fatalf("unexpected error %v", err)
   394  	}
   395  	// We don't need to wait for fp.wg, as pollImmediate shouldn't call WaitFunc at all.
   396  	if invocations != 1 {
   397  		t.Errorf("Expected exactly one invocation, got %d", invocations)
   398  	}
   399  	used := atomic.LoadInt32(&fp.used)
   400  	if used != 0 {
   401  		t.Errorf("Expected exactly zero ticks, got %d", used)
   402  	}
   403  }
   404  
   405  func TestPollImmediateError(t *testing.T) {
   406  	expectedError := errors.New("Expected error")
   407  	f := ConditionFunc(func() (bool, error) {
   408  		return false, expectedError
   409  	})
   410  	fp := fakePoller{max: 0}
   411  
   412  	ctx, cancel := context.WithCancel(context.Background())
   413  	defer cancel()
   414  	if err := poll(ctx, true, fp.GetWaitFunc().WithContext(), f.WithContext()); err == nil || err != expectedError {
   415  		t.Fatalf("Expected error %v, got none %v", expectedError, err)
   416  	}
   417  	// We don't need to wait for fp.wg, as pollImmediate shouldn't call WaitFunc at all.
   418  	used := atomic.LoadInt32(&fp.used)
   419  	if used != 0 {
   420  		t.Errorf("Expected exactly zero ticks, got %d", used)
   421  	}
   422  }
   423  
   424  func TestPollForever(t *testing.T) {
   425  	ch := make(chan struct{})
   426  	errc := make(chan error, 1)
   427  	done := make(chan struct{}, 1)
   428  	complete := make(chan struct{})
   429  	go func() {
   430  		f := ConditionFunc(func() (bool, error) {
   431  			ch <- struct{}{}
   432  			select {
   433  			case <-done:
   434  				return true, nil
   435  			default:
   436  			}
   437  			return false, nil
   438  		})
   439  
   440  		if err := PollInfinite(time.Microsecond, f); err != nil {
   441  			errc <- fmt.Errorf("unexpected error %v", err)
   442  		}
   443  
   444  		close(ch)
   445  		complete <- struct{}{}
   446  	}()
   447  
   448  	// ensure the condition is opened
   449  	<-ch
   450  
   451  	// ensure channel sends events
   452  	for i := 0; i < 10; i++ {
   453  		select {
   454  		case _, open := <-ch:
   455  			if !open {
   456  				if len(errc) != 0 {
   457  					t.Fatalf("did not expect channel to be closed, %v", <-errc)
   458  				}
   459  				t.Fatal("did not expect channel to be closed")
   460  			}
   461  		case <-time.After(ForeverTestTimeout):
   462  			t.Fatalf("channel did not return at least once within the poll interval")
   463  		}
   464  	}
   465  
   466  	// at most one poll notification should be sent once we return from the condition
   467  	done <- struct{}{}
   468  	go func() {
   469  		for i := 0; i < 2; i++ {
   470  			_, open := <-ch
   471  			if !open {
   472  				return
   473  			}
   474  		}
   475  		t.Error("expected closed channel after two iterations")
   476  	}()
   477  	<-complete
   478  
   479  	if len(errc) != 0 {
   480  		t.Fatal(<-errc)
   481  	}
   482  }
   483  
   484  func TestWaitFor(t *testing.T) {
   485  	var invocations int
   486  	testCases := map[string]struct {
   487  		F       ConditionFunc
   488  		Ticks   int
   489  		Invoked int
   490  		Err     bool
   491  	}{
   492  		"invoked once": {
   493  			ConditionFunc(func() (bool, error) {
   494  				invocations++
   495  				return true, nil
   496  			}),
   497  			2,
   498  			1,
   499  			false,
   500  		},
   501  		"invoked and returns a timeout": {
   502  			ConditionFunc(func() (bool, error) {
   503  				invocations++
   504  				return false, nil
   505  			}),
   506  			2,
   507  			3, // the contract of WaitFor() says the func is called once more at the end of the wait
   508  			true,
   509  		},
   510  		"returns immediately on error": {
   511  			ConditionFunc(func() (bool, error) {
   512  				invocations++
   513  				return false, errors.New("test")
   514  			}),
   515  			2,
   516  			1,
   517  			true,
   518  		},
   519  	}
   520  	for k, c := range testCases {
   521  		invocations = 0
   522  		ticker := fakeTicker(c.Ticks, nil, func() {})
   523  		err := func() error {
   524  			done := make(chan struct{})
   525  			defer close(done)
   526  			return WaitFor(ticker, c.F, done)
   527  		}()
   528  		switch {
   529  		case c.Err && err == nil:
   530  			t.Errorf("%s: Expected error, got nil", k)
   531  			continue
   532  		case !c.Err && err != nil:
   533  			t.Errorf("%s: Expected no error, got: %#v", k, err)
   534  			continue
   535  		}
   536  		if invocations != c.Invoked {
   537  			t.Errorf("%s: Expected %d invocations, got %d", k, c.Invoked, invocations)
   538  		}
   539  	}
   540  }
   541  
   542  // TestWaitForWithEarlyClosingWaitFunc tests WaitFor when the WaitFunc closes its channel. The WaitFor should
   543  // always return ErrWaitTimeout.
   544  func TestWaitForWithEarlyClosingWaitFunc(t *testing.T) {
   545  	stopCh := make(chan struct{})
   546  	defer close(stopCh)
   547  
   548  	start := time.Now()
   549  	err := WaitFor(func(done <-chan struct{}) <-chan struct{} {
   550  		c := make(chan struct{})
   551  		close(c)
   552  		return c
   553  	}, func() (bool, error) {
   554  		return false, nil
   555  	}, stopCh)
   556  	duration := time.Since(start)
   557  
   558  	// The WaitFor should return immediately, so the duration is close to 0s.
   559  	if duration >= ForeverTestTimeout/2 {
   560  		t.Errorf("expected short timeout duration")
   561  	}
   562  	if err != ErrWaitTimeout {
   563  		t.Errorf("expected ErrWaitTimeout from WaitFunc")
   564  	}
   565  }
   566  
   567  // TestWaitForWithClosedChannel tests WaitFor when it receives a closed channel. The WaitFor should
   568  // always return ErrWaitTimeout.
   569  func TestWaitForWithClosedChannel(t *testing.T) {
   570  	stopCh := make(chan struct{})
   571  	close(stopCh)
   572  	c := make(chan struct{})
   573  	defer close(c)
   574  	start := time.Now()
   575  	err := WaitFor(func(done <-chan struct{}) <-chan struct{} {
   576  		return c
   577  	}, func() (bool, error) {
   578  		return false, nil
   579  	}, stopCh)
   580  	duration := time.Since(start)
   581  	// The WaitFor should return immediately, so the duration is close to 0s.
   582  	if duration >= ForeverTestTimeout/2 {
   583  		t.Errorf("expected short timeout duration")
   584  	}
   585  	// The interval of the poller is ForeverTestTimeout, so the WaitFor should always return ErrWaitTimeout.
   586  	if err != ErrWaitTimeout {
   587  		t.Errorf("expected ErrWaitTimeout from WaitFunc")
   588  	}
   589  }
   590  
   591  // TestWaitForWithContextCancelsContext verifies that after the condition func returns true,
   592  // WaitForWithContext cancels the context it supplies to the WaitWithContextFunc.
   593  func TestWaitForWithContextCancelsContext(t *testing.T) {
   594  	ctx, cancel := context.WithCancel(context.Background())
   595  	defer cancel()
   596  	waitFunc := poller(time.Millisecond, ForeverTestTimeout)
   597  
   598  	var ctxPassedToWait context.Context
   599  	WaitForWithContext(ctx, func(ctx context.Context) <-chan struct{} {
   600  		ctxPassedToWait = ctx
   601  		return waitFunc(ctx)
   602  	}, func(ctx context.Context) (bool, error) {
   603  		time.Sleep(10 * time.Millisecond)
   604  		return true, nil
   605  	})
   606  	// The polling goroutine should be closed after WaitForWithContext returning.
   607  	if ctxPassedToWait.Err() != context.Canceled {
   608  		t.Errorf("expected the context passed to WaitForWithContext to be closed with: %v, but got: %v", context.Canceled, ctxPassedToWait.Err())
   609  	}
   610  }
   611  
   612  func TestPollUntil(t *testing.T) {
   613  	stopCh := make(chan struct{})
   614  	called := make(chan bool)
   615  	pollDone := make(chan struct{})
   616  
   617  	go func() {
   618  		PollUntil(time.Microsecond, ConditionFunc(func() (bool, error) {
   619  			called <- true
   620  			return false, nil
   621  		}), stopCh)
   622  
   623  		close(pollDone)
   624  	}()
   625  
   626  	// make sure we're called once
   627  	<-called
   628  	// this should trigger a "done"
   629  	close(stopCh)
   630  
   631  	go func() {
   632  		// release the condition func  if needed
   633  		for {
   634  			<-called
   635  		}
   636  	}()
   637  
   638  	// make sure we finished the poll
   639  	<-pollDone
   640  }
   641  
   642  func TestBackoff_Step(t *testing.T) {
   643  	tests := []struct {
   644  		initial *Backoff
   645  		want    []time.Duration
   646  	}{
   647  		{initial: &Backoff{Duration: time.Second, Steps: 0}, want: []time.Duration{time.Second, time.Second, time.Second}},
   648  		{initial: &Backoff{Duration: time.Second, Steps: 1}, want: []time.Duration{time.Second, time.Second, time.Second}},
   649  		{initial: &Backoff{Duration: time.Second, Factor: 1.0, Steps: 1}, want: []time.Duration{time.Second, time.Second, time.Second}},
   650  		{initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 3}, want: []time.Duration{1 * time.Second, 2 * time.Second, 4 * time.Second}},
   651  		{initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 3, Cap: 3 * time.Second}, want: []time.Duration{1 * time.Second, 2 * time.Second, 3 * time.Second}},
   652  		{initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 2, Cap: 3 * time.Second, Jitter: 0.5}, want: []time.Duration{2 * time.Second, 3 * time.Second, 3 * time.Second}},
   653  		{initial: &Backoff{Duration: time.Second, Factor: 2, Steps: 6, Jitter: 4}, want: []time.Duration{1 * time.Second, 2 * time.Second, 4 * time.Second, 8 * time.Second, 16 * time.Second, 32 * time.Second}},
   654  	}
   655  	for seed := int64(0); seed < 5; seed++ {
   656  		for _, tt := range tests {
   657  			initial := *tt.initial
   658  			t.Run(fmt.Sprintf("%#v seed=%d", initial, seed), func(t *testing.T) {
   659  				rand.Seed(seed)
   660  				for i := 0; i < len(tt.want); i++ {
   661  					got := initial.Step()
   662  					t.Logf("[%d]=%s", i, got)
   663  					if initial.Jitter > 0 {
   664  						if got == tt.want[i] {
   665  							// this is statistically unlikely to happen by chance
   666  							t.Errorf("Backoff.Step(%d) = %v, no jitter", i, got)
   667  							continue
   668  						}
   669  						diff := float64(tt.want[i]-got) / float64(tt.want[i])
   670  						if diff > initial.Jitter {
   671  							t.Errorf("Backoff.Step(%d) = %v, want %v, outside range", i, got, tt.want)
   672  							continue
   673  						}
   674  					} else {
   675  						if got != tt.want[i] {
   676  							t.Errorf("Backoff.Step(%d) = %v, want %v", i, got, tt.want)
   677  							continue
   678  						}
   679  					}
   680  				}
   681  			})
   682  		}
   683  	}
   684  }
   685  
   686  func TestContextForChannel(t *testing.T) {
   687  	var wg sync.WaitGroup
   688  	parentCh := make(chan struct{})
   689  	done := make(chan struct{})
   690  
   691  	for i := 0; i < 3; i++ {
   692  		wg.Add(1)
   693  		go func() {
   694  			defer wg.Done()
   695  			ctx, cancel := ContextForChannel(parentCh)
   696  			defer cancel()
   697  			<-ctx.Done()
   698  		}()
   699  	}
   700  
   701  	go func() {
   702  		wg.Wait()
   703  		close(done)
   704  	}()
   705  
   706  	// Closing parent channel should cancel all children contexts
   707  	close(parentCh)
   708  
   709  	select {
   710  	case <-done:
   711  	case <-time.After(ForeverTestTimeout):
   712  		t.Errorf("unexpected timeout waiting for parent to cancel child contexts")
   713  	}
   714  }
   715  
   716  func TestExponentialBackoffManagerGetNextBackoff(t *testing.T) {
   717  	fc := testingclock.NewFakeClock(time.Now())
   718  	backoff := NewExponentialBackoffManager(1, 10, 10, 2.0, 0.0, fc)
   719  	durations := []time.Duration{1, 2, 4, 8, 10, 10, 10}
   720  	for i := 0; i < len(durations); i++ {
   721  		generatedBackoff := backoff.(*exponentialBackoffManagerImpl).getNextBackoff()
   722  		if generatedBackoff != durations[i] {
   723  			t.Errorf("unexpected %d-th backoff: %d, expecting %d", i, generatedBackoff, durations[i])
   724  		}
   725  	}
   726  
   727  	fc.Step(11)
   728  	resetDuration := backoff.(*exponentialBackoffManagerImpl).getNextBackoff()
   729  	if resetDuration != 1 {
   730  		t.Errorf("after reset, backoff should be 1, but got %d", resetDuration)
   731  	}
   732  }
   733  
   734  func TestJitteredBackoffManagerGetNextBackoff(t *testing.T) {
   735  	// positive jitter
   736  	backoffMgr := NewJitteredBackoffManager(1, 1, testingclock.NewFakeClock(time.Now()))
   737  	for i := 0; i < 5; i++ {
   738  		backoff := backoffMgr.(*jitteredBackoffManagerImpl).getNextBackoff()
   739  		if backoff < 1 || backoff > 2 {
   740  			t.Errorf("backoff out of range: %d", backoff)
   741  		}
   742  	}
   743  
   744  	// negative jitter, shall be a fixed backoff
   745  	backoffMgr = NewJitteredBackoffManager(1, -1, testingclock.NewFakeClock(time.Now()))
   746  	backoff := backoffMgr.(*jitteredBackoffManagerImpl).getNextBackoff()
   747  	if backoff != 1 {
   748  		t.Errorf("backoff should be 1, but got %d", backoff)
   749  	}
   750  }
   751  
   752  func TestJitterBackoffManagerWithRealClock(t *testing.T) {
   753  	backoffMgr := NewJitteredBackoffManager(1*time.Millisecond, 0, &clock.RealClock{})
   754  	for i := 0; i < 5; i++ {
   755  		start := time.Now()
   756  		<-backoffMgr.Backoff().C()
   757  		passed := time.Since(start)
   758  		if passed < 1*time.Millisecond {
   759  			t.Errorf("backoff should be at least 1ms, but got %s", passed.String())
   760  		}
   761  	}
   762  }
   763  
   764  func TestExponentialBackoffManagerWithRealClock(t *testing.T) {
   765  	// backoff at least 1ms, 2ms, 4ms, 8ms, 10ms, 10ms, 10ms
   766  	durationFactors := []time.Duration{1, 2, 4, 8, 10, 10, 10}
   767  	backoffMgr := NewExponentialBackoffManager(1*time.Millisecond, 10*time.Millisecond, 1*time.Hour, 2.0, 0.0, &clock.RealClock{})
   768  
   769  	for i := range durationFactors {
   770  		start := time.Now()
   771  		<-backoffMgr.Backoff().C()
   772  		passed := time.Since(start)
   773  		if passed < durationFactors[i]*time.Millisecond {
   774  			t.Errorf("backoff should be at least %d ms, but got %s", durationFactors[i], passed.String())
   775  		}
   776  	}
   777  }
   778  
   779  func TestExponentialBackoffWithContext(t *testing.T) {
   780  	defaultCtx := func() context.Context {
   781  		return context.Background()
   782  	}
   783  
   784  	defaultCallback := func(_ int) (bool, error) {
   785  		return false, nil
   786  	}
   787  
   788  	conditionErr := errors.New("condition failed")
   789  
   790  	tests := []struct {
   791  		name             string
   792  		steps            int
   793  		ctxGetter        func() context.Context
   794  		callback         func(calls int) (bool, error)
   795  		attemptsExpected int
   796  		errExpected      error
   797  	}{
   798  		{
   799  			name:             "no attempts expected with zero backoff steps",
   800  			steps:            0,
   801  			ctxGetter:        defaultCtx,
   802  			callback:         defaultCallback,
   803  			attemptsExpected: 0,
   804  			errExpected:      ErrWaitTimeout,
   805  		},
   806  		{
   807  			name:             "condition returns false with single backoff step",
   808  			steps:            1,
   809  			ctxGetter:        defaultCtx,
   810  			callback:         defaultCallback,
   811  			attemptsExpected: 1,
   812  			errExpected:      ErrWaitTimeout,
   813  		},
   814  		{
   815  			name:      "condition returns true with single backoff step",
   816  			steps:     1,
   817  			ctxGetter: defaultCtx,
   818  			callback: func(_ int) (bool, error) {
   819  				return true, nil
   820  			},
   821  			attemptsExpected: 1,
   822  			errExpected:      nil,
   823  		},
   824  		{
   825  			name:             "condition always returns false with multiple backoff steps",
   826  			steps:            5,
   827  			ctxGetter:        defaultCtx,
   828  			callback:         defaultCallback,
   829  			attemptsExpected: 5,
   830  			errExpected:      ErrWaitTimeout,
   831  		},
   832  		{
   833  			name:      "condition returns true after certain attempts with multiple backoff steps",
   834  			steps:     5,
   835  			ctxGetter: defaultCtx,
   836  			callback: func(attempts int) (bool, error) {
   837  				if attempts == 3 {
   838  					return true, nil
   839  				}
   840  				return false, nil
   841  			},
   842  			attemptsExpected: 3,
   843  			errExpected:      nil,
   844  		},
   845  		{
   846  			name:      "condition returns error no further attempts expected",
   847  			steps:     5,
   848  			ctxGetter: defaultCtx,
   849  			callback: func(_ int) (bool, error) {
   850  				return true, conditionErr
   851  			},
   852  			attemptsExpected: 1,
   853  			errExpected:      conditionErr,
   854  		},
   855  		{
   856  			name:  "context already canceled no attempts expected",
   857  			steps: 5,
   858  			ctxGetter: func() context.Context {
   859  				ctx, cancel := context.WithCancel(context.Background())
   860  				defer cancel()
   861  				return ctx
   862  			},
   863  			callback:         defaultCallback,
   864  			attemptsExpected: 0,
   865  			errExpected:      context.Canceled,
   866  		},
   867  	}
   868  
   869  	for _, test := range tests {
   870  		t.Run(test.name, func(t *testing.T) {
   871  			backoff := Backoff{
   872  				Duration: 1 * time.Millisecond,
   873  				Factor:   1.0,
   874  				Steps:    test.steps,
   875  			}
   876  
   877  			attempts := 0
   878  			err := ExponentialBackoffWithContext(test.ctxGetter(), backoff, func() (bool, error) {
   879  				attempts++
   880  				return test.callback(attempts)
   881  			})
   882  
   883  			if test.errExpected != err {
   884  				t.Errorf("expected error: %v but got: %v", test.errExpected, err)
   885  			}
   886  
   887  			if test.attemptsExpected != attempts {
   888  				t.Errorf("expected attempts count: %d but got: %d", test.attemptsExpected, attempts)
   889  			}
   890  		})
   891  	}
   892  }
   893  
   894  func TestPollImmediateUntilWithContext(t *testing.T) {
   895  	fakeErr := errors.New("my error")
   896  	tests := []struct {
   897  		name                         string
   898  		condition                    func(int) ConditionWithContextFunc
   899  		context                      func() (context.Context, context.CancelFunc)
   900  		cancelContextAfterNthAttempt int
   901  		errExpected                  error
   902  		attemptsExpected             int
   903  	}{
   904  		{
   905  			name: "condition throws error on immediate attempt, no retry is attempted",
   906  			condition: func(int) ConditionWithContextFunc {
   907  				return func(context.Context) (done bool, err error) {
   908  					return false, fakeErr
   909  				}
   910  			},
   911  			context: func() (context.Context, context.CancelFunc) {
   912  				return context.WithCancel(context.Background())
   913  			},
   914  			errExpected:      fakeErr,
   915  			attemptsExpected: 1,
   916  		},
   917  		{
   918  			name: "condition returns done=true on immediate attempt, no retry is attempted",
   919  			condition: func(int) ConditionWithContextFunc {
   920  				return func(context.Context) (done bool, err error) {
   921  					return true, nil
   922  				}
   923  			},
   924  			context: func() (context.Context, context.CancelFunc) {
   925  				return context.WithCancel(context.Background())
   926  			},
   927  			errExpected:      nil,
   928  			attemptsExpected: 1,
   929  		},
   930  		{
   931  			name: "condition returns done=false on immediate attempt, context is already cancelled, no retry is attempted",
   932  			condition: func(int) ConditionWithContextFunc {
   933  				return func(context.Context) (done bool, err error) {
   934  					return false, nil
   935  				}
   936  			},
   937  			context: func() (context.Context, context.CancelFunc) {
   938  				ctx, cancel := context.WithCancel(context.Background())
   939  				defer cancel()
   940  				return ctx, cancel
   941  			},
   942  			errExpected:      ErrWaitTimeout,
   943  			attemptsExpected: 1,
   944  		},
   945  		{
   946  			name: "condition returns done=false on immediate attempt, context is not cancelled, retry is attempted",
   947  			condition: func(attempts int) ConditionWithContextFunc {
   948  				return func(context.Context) (done bool, err error) {
   949  					// let first 3 attempts fail and the last one succeed
   950  					if attempts <= 3 {
   951  						return false, nil
   952  					}
   953  					return true, nil
   954  				}
   955  			},
   956  			context: func() (context.Context, context.CancelFunc) {
   957  				return context.WithCancel(context.Background())
   958  			},
   959  			errExpected:      nil,
   960  			attemptsExpected: 4,
   961  		},
   962  		{
   963  			name: "condition always returns done=false, context gets cancelled after N attempts",
   964  			condition: func(attempts int) ConditionWithContextFunc {
   965  				return func(ctx context.Context) (done bool, err error) {
   966  					return false, nil
   967  				}
   968  			},
   969  			context: func() (context.Context, context.CancelFunc) {
   970  				return context.WithCancel(context.Background())
   971  			},
   972  			cancelContextAfterNthAttempt: 4,
   973  			errExpected:                  ErrWaitTimeout,
   974  			attemptsExpected:             4,
   975  		},
   976  	}
   977  
   978  	for _, test := range tests {
   979  		t.Run(test.name, func(t *testing.T) {
   980  			ctx, cancel := test.context()
   981  			defer cancel()
   982  
   983  			var attempts int
   984  			conditionWrapper := func(ctx context.Context) (done bool, err error) {
   985  				attempts++
   986  				defer func() {
   987  					if test.cancelContextAfterNthAttempt == attempts {
   988  						cancel()
   989  					}
   990  				}()
   991  
   992  				c := test.condition(attempts)
   993  				return c(ctx)
   994  			}
   995  
   996  			err := PollImmediateUntilWithContext(ctx, time.Millisecond, conditionWrapper)
   997  			if test.errExpected != err {
   998  				t.Errorf("Expected error: %v, but got: %v", test.errExpected, err)
   999  			}
  1000  			if test.attemptsExpected != attempts {
  1001  				t.Errorf("Expected ConditionFunc to be invoked: %d times, but got: %d", test.attemptsExpected, attempts)
  1002  			}
  1003  		})
  1004  	}
  1005  }
  1006  
  1007  func TestWaitForWithContext(t *testing.T) {
  1008  	fakeErr := errors.New("fake error")
  1009  	tests := []struct {
  1010  		name             string
  1011  		context          func() (context.Context, context.CancelFunc)
  1012  		condition        ConditionWithContextFunc
  1013  		waitFunc         func() WaitFunc
  1014  		attemptsExpected int
  1015  		errExpected      error
  1016  	}{
  1017  		{
  1018  			name: "condition returns done=true on first attempt, no retry is attempted",
  1019  			context: func() (context.Context, context.CancelFunc) {
  1020  				return context.WithCancel(context.Background())
  1021  			},
  1022  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1023  				return true, nil
  1024  			}),
  1025  			waitFunc:         func() WaitFunc { return fakeTicker(2, nil, func() {}) },
  1026  			attemptsExpected: 1,
  1027  			errExpected:      nil,
  1028  		},
  1029  		{
  1030  			name: "condition always returns done=false, timeout error expected",
  1031  			context: func() (context.Context, context.CancelFunc) {
  1032  				return context.WithCancel(context.Background())
  1033  			},
  1034  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1035  				return false, nil
  1036  			}),
  1037  			waitFunc: func() WaitFunc { return fakeTicker(2, nil, func() {}) },
  1038  			// the contract of WaitForWithContext() says the func is called once more at the end of the wait
  1039  			attemptsExpected: 3,
  1040  			errExpected:      ErrWaitTimeout,
  1041  		},
  1042  		{
  1043  			name: "condition returns an error on first attempt, the error is returned",
  1044  			context: func() (context.Context, context.CancelFunc) {
  1045  				return context.WithCancel(context.Background())
  1046  			},
  1047  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1048  				return false, fakeErr
  1049  			}),
  1050  			waitFunc:         func() WaitFunc { return fakeTicker(2, nil, func() {}) },
  1051  			attemptsExpected: 1,
  1052  			errExpected:      fakeErr,
  1053  		},
  1054  		{
  1055  			name: "context is cancelled, context cancelled error expected",
  1056  			context: func() (context.Context, context.CancelFunc) {
  1057  				ctx, cancel := context.WithCancel(context.Background())
  1058  				cancel()
  1059  				return ctx, cancel
  1060  			},
  1061  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1062  				return false, nil
  1063  			}),
  1064  			waitFunc: func() WaitFunc {
  1065  				return func(done <-chan struct{}) <-chan struct{} {
  1066  					ch := make(chan struct{})
  1067  					// never tick on this channel
  1068  					return ch
  1069  				}
  1070  			},
  1071  			attemptsExpected: 0,
  1072  			errExpected:      ErrWaitTimeout,
  1073  		},
  1074  	}
  1075  
  1076  	for _, test := range tests {
  1077  		t.Run(test.name, func(t *testing.T) {
  1078  			var attempts int
  1079  			conditionWrapper := func(ctx context.Context) (done bool, err error) {
  1080  				attempts++
  1081  				return test.condition(ctx)
  1082  			}
  1083  
  1084  			ticker := test.waitFunc()
  1085  			err := func() error {
  1086  				ctx, cancel := test.context()
  1087  				defer cancel()
  1088  
  1089  				return WaitForWithContext(ctx, ticker.WithContext(), conditionWrapper)
  1090  			}()
  1091  
  1092  			if test.errExpected != err {
  1093  				t.Errorf("Expected error: %v, but got: %v", test.errExpected, err)
  1094  			}
  1095  			if test.attemptsExpected != attempts {
  1096  				t.Errorf("Expected %d invocations, got %d", test.attemptsExpected, attempts)
  1097  			}
  1098  		})
  1099  	}
  1100  }
  1101  
  1102  func TestPollInternal(t *testing.T) {
  1103  	fakeErr := errors.New("fake error")
  1104  	tests := []struct {
  1105  		name               string
  1106  		context            func() (context.Context, context.CancelFunc)
  1107  		immediate          bool
  1108  		waitFunc           func() WaitFunc
  1109  		condition          ConditionWithContextFunc
  1110  		cancelContextAfter int
  1111  		attemptsExpected   int
  1112  		errExpected        error
  1113  	}{
  1114  		{
  1115  			name:      "immediate is true, condition returns an error",
  1116  			immediate: true,
  1117  			context: func() (context.Context, context.CancelFunc) {
  1118  				// use a cancelled context, we want to make sure the
  1119  				// condition is expected to be invoked immediately.
  1120  				ctx, cancel := context.WithCancel(context.Background())
  1121  				defer cancel()
  1122  				return ctx, cancel
  1123  			},
  1124  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1125  				return false, fakeErr
  1126  			}),
  1127  			waitFunc:         nil,
  1128  			attemptsExpected: 1,
  1129  			errExpected:      fakeErr,
  1130  		},
  1131  		{
  1132  			name:      "immediate is true, condition returns true",
  1133  			immediate: true,
  1134  			context: func() (context.Context, context.CancelFunc) {
  1135  				// use a cancelled context, we want to make sure the
  1136  				// condition is expected to be invoked immediately.
  1137  				ctx, cancel := context.WithCancel(context.Background())
  1138  				defer cancel()
  1139  				return ctx, cancel
  1140  			},
  1141  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1142  				return true, nil
  1143  			}),
  1144  			waitFunc:         nil,
  1145  			attemptsExpected: 1,
  1146  			errExpected:      nil,
  1147  		},
  1148  		{
  1149  			name:      "immediate is true, context is cancelled, condition return false",
  1150  			immediate: true,
  1151  			context: func() (context.Context, context.CancelFunc) {
  1152  				// use a cancelled context, we want to make sure the
  1153  				// condition is expected to be invoked immediately.
  1154  				ctx, cancel := context.WithCancel(context.Background())
  1155  				defer cancel()
  1156  				return ctx, cancel
  1157  			},
  1158  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1159  				return false, nil
  1160  			}),
  1161  			waitFunc:         nil,
  1162  			attemptsExpected: 1,
  1163  			errExpected:      ErrWaitTimeout,
  1164  		},
  1165  		{
  1166  			name:      "immediate is false, context is cancelled",
  1167  			immediate: false,
  1168  			context: func() (context.Context, context.CancelFunc) {
  1169  				// use a cancelled context, we want to make sure the
  1170  				// condition is expected to be invoked immediately.
  1171  				ctx, cancel := context.WithCancel(context.Background())
  1172  				defer cancel()
  1173  				return ctx, cancel
  1174  			},
  1175  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1176  				return false, nil
  1177  			}),
  1178  			waitFunc:         nil,
  1179  			attemptsExpected: 0,
  1180  			errExpected:      ErrWaitTimeout,
  1181  		},
  1182  		{
  1183  			name:      "immediate is false, condition returns an error",
  1184  			immediate: false,
  1185  			context: func() (context.Context, context.CancelFunc) {
  1186  				return context.WithCancel(context.Background())
  1187  			},
  1188  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1189  				return false, fakeErr
  1190  			}),
  1191  			waitFunc:         func() WaitFunc { return fakeTicker(5, nil, func() {}) },
  1192  			attemptsExpected: 1,
  1193  			errExpected:      fakeErr,
  1194  		},
  1195  		{
  1196  			name:      "immediate is false, condition returns true",
  1197  			immediate: false,
  1198  			context: func() (context.Context, context.CancelFunc) {
  1199  				return context.WithCancel(context.Background())
  1200  			},
  1201  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1202  				return true, nil
  1203  			}),
  1204  			waitFunc:         func() WaitFunc { return fakeTicker(5, nil, func() {}) },
  1205  			attemptsExpected: 1,
  1206  			errExpected:      nil,
  1207  		},
  1208  		{
  1209  			name:      "immediate is false, ticker channel is closed, condition returns true",
  1210  			immediate: false,
  1211  			context: func() (context.Context, context.CancelFunc) {
  1212  				return context.WithCancel(context.Background())
  1213  			},
  1214  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1215  				return true, nil
  1216  			}),
  1217  			waitFunc: func() WaitFunc {
  1218  				return func(done <-chan struct{}) <-chan struct{} {
  1219  					ch := make(chan struct{})
  1220  					close(ch)
  1221  					return ch
  1222  				}
  1223  			},
  1224  			attemptsExpected: 1,
  1225  			errExpected:      nil,
  1226  		},
  1227  		{
  1228  			name:      "immediate is false, ticker channel is closed, condition returns error",
  1229  			immediate: false,
  1230  			context: func() (context.Context, context.CancelFunc) {
  1231  				return context.WithCancel(context.Background())
  1232  			},
  1233  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1234  				return false, fakeErr
  1235  			}),
  1236  			waitFunc: func() WaitFunc {
  1237  				return func(done <-chan struct{}) <-chan struct{} {
  1238  					ch := make(chan struct{})
  1239  					close(ch)
  1240  					return ch
  1241  				}
  1242  			},
  1243  			attemptsExpected: 1,
  1244  			errExpected:      fakeErr,
  1245  		},
  1246  		{
  1247  			name:      "immediate is false, ticker channel is closed, condition returns false",
  1248  			immediate: false,
  1249  			context: func() (context.Context, context.CancelFunc) {
  1250  				return context.WithCancel(context.Background())
  1251  			},
  1252  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1253  				return false, nil
  1254  			}),
  1255  			waitFunc: func() WaitFunc {
  1256  				return func(done <-chan struct{}) <-chan struct{} {
  1257  					ch := make(chan struct{})
  1258  					close(ch)
  1259  					return ch
  1260  				}
  1261  			},
  1262  			attemptsExpected: 1,
  1263  			errExpected:      ErrWaitTimeout,
  1264  		},
  1265  		{
  1266  			name:      "condition always returns false, timeout error expected",
  1267  			immediate: false,
  1268  			context: func() (context.Context, context.CancelFunc) {
  1269  				return context.WithCancel(context.Background())
  1270  			},
  1271  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1272  				return false, nil
  1273  			}),
  1274  			waitFunc: func() WaitFunc { return fakeTicker(2, nil, func() {}) },
  1275  			// the contract of WaitForWithContext() says the func is called once more at the end of the wait
  1276  			attemptsExpected: 3,
  1277  			errExpected:      ErrWaitTimeout,
  1278  		},
  1279  		{
  1280  			name:      "context is cancelled after N attempts, timeout error expected",
  1281  			immediate: false,
  1282  			context: func() (context.Context, context.CancelFunc) {
  1283  				return context.WithCancel(context.Background())
  1284  			},
  1285  			condition: ConditionWithContextFunc(func(context.Context) (bool, error) {
  1286  				return false, nil
  1287  			}),
  1288  			waitFunc: func() WaitFunc {
  1289  				return func(done <-chan struct{}) <-chan struct{} {
  1290  					ch := make(chan struct{})
  1291  					// just tick twice
  1292  					go func() {
  1293  						ch <- struct{}{}
  1294  						ch <- struct{}{}
  1295  					}()
  1296  					return ch
  1297  				}
  1298  			},
  1299  			cancelContextAfter: 2,
  1300  			attemptsExpected:   2,
  1301  			errExpected:        ErrWaitTimeout,
  1302  		},
  1303  	}
  1304  
  1305  	for _, test := range tests {
  1306  		t.Run(test.name, func(t *testing.T) {
  1307  			var attempts int
  1308  			ticker := WaitFunc(func(done <-chan struct{}) <-chan struct{} {
  1309  				return nil
  1310  			})
  1311  			if test.waitFunc != nil {
  1312  				ticker = test.waitFunc()
  1313  			}
  1314  			err := func() error {
  1315  				ctx, cancel := test.context()
  1316  				defer cancel()
  1317  
  1318  				conditionWrapper := func(ctx context.Context) (done bool, err error) {
  1319  					attempts++
  1320  
  1321  					defer func() {
  1322  						if test.cancelContextAfter == attempts {
  1323  							cancel()
  1324  						}
  1325  					}()
  1326  
  1327  					return test.condition(ctx)
  1328  				}
  1329  
  1330  				return poll(ctx, test.immediate, ticker.WithContext(), conditionWrapper)
  1331  			}()
  1332  
  1333  			if test.errExpected != err {
  1334  				t.Errorf("Expected error: %v, but got: %v", test.errExpected, err)
  1335  			}
  1336  			if test.attemptsExpected != attempts {
  1337  				t.Errorf("Expected %d invocations, got %d", test.attemptsExpected, attempts)
  1338  			}
  1339  		})
  1340  	}
  1341  }