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 }