k8s.io/apiserver@v0.31.1/pkg/storage/cacher/watch_progress_test.go (about) 1 /* 2 Copyright 2023 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 cacher 18 19 import ( 20 "context" 21 "sync/atomic" 22 "testing" 23 "time" 24 25 "k8s.io/apimachinery/pkg/util/wait" 26 27 "k8s.io/klog/v2" 28 "k8s.io/klog/v2/ktesting" 29 "k8s.io/utils/clock" 30 testingclock "k8s.io/utils/clock/testing" 31 ) 32 33 var ( 34 pollPeriod = time.Millisecond 35 minimalNoChange = 20 * time.Millisecond 36 pollTimeout = 5 * time.Second 37 ) 38 39 func TestConditionalProgressRequester(t *testing.T) { 40 _, ctx := ktesting.NewTestContext(t) 41 logger := klog.FromContext(ctx) 42 43 clock := testingclock.NewFakeClock(time.Now()) 44 pr := newTestConditionalProgressRequester(clock) 45 stopCh := make(chan struct{}) 46 go pr.Run(stopCh) 47 var wantRequestsSent int32 48 var requestsSent int32 49 50 logger.Info("Wait for ticker to be created") 51 for !clock.HasWaiters() { 52 time.Sleep(pollPeriod) 53 } 54 55 logger.Info("No progress requests if no-one is waiting") 56 clock.Step(progressRequestPeriod * 2) 57 58 if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool { 59 requestsSent = pr.progressRequestsSentCount.Load() 60 return requestsSent == wantRequestsSent 61 }); err != nil { 62 t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent) 63 } 64 65 logger.Info("Adding waiters allows progress request to be sent") 66 pr.Add() 67 wantRequestsSent++ 68 if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool { 69 requestsSent = pr.progressRequestsSentCount.Load() 70 return requestsSent == wantRequestsSent 71 }); err != nil { 72 t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent) 73 } 74 75 logger.Info("Periodically request progress to be sent every period") 76 for wantRequestsSent < 10 { 77 clock.Step(progressRequestPeriod) 78 wantRequestsSent++ 79 80 if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool { 81 requestsSent = pr.progressRequestsSentCount.Load() 82 return requestsSent == wantRequestsSent 83 }); err != nil { 84 t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent) 85 } 86 } 87 pr.Remove() 88 89 logger.Info("No progress requests if no-one is waiting") 90 clock.Step(progressRequestPeriod * 2) 91 if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool { 92 requestsSent = pr.progressRequestsSentCount.Load() 93 return requestsSent == wantRequestsSent 94 }); err != nil { 95 t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent) 96 } 97 98 logger.Info("No progress after stopping") 99 close(stopCh) 100 if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool { 101 requestsSent = pr.progressRequestsSentCount.Load() 102 return requestsSent == wantRequestsSent 103 }); err != nil { 104 t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent) 105 } 106 pr.Add() 107 clock.Step(progressRequestPeriod * 2) 108 if err := pollConditionNoChange(pollPeriod, minimalNoChange, pollTimeout, func() bool { 109 requestsSent = pr.progressRequestsSentCount.Load() 110 return requestsSent == wantRequestsSent 111 }); err != nil { 112 t.Fatalf("Failed to wait progress requests, err: %s, want: %d , got %d", err, wantRequestsSent, requestsSent) 113 } 114 } 115 116 func newTestConditionalProgressRequester(clock clock.WithTicker) *testConditionalProgressRequester { 117 pr := &testConditionalProgressRequester{} 118 pr.conditionalProgressRequester = newConditionalProgressRequester(pr.RequestWatchProgress, clock, nil) 119 return pr 120 } 121 122 type testConditionalProgressRequester struct { 123 *conditionalProgressRequester 124 progressRequestsSentCount atomic.Int32 125 } 126 127 func (pr *testConditionalProgressRequester) RequestWatchProgress(ctx context.Context) error { 128 pr.progressRequestsSentCount.Add(1) 129 return nil 130 } 131 132 func pollConditionNoChange(interval, stable, timeout time.Duration, condition func() bool) error { 133 passCounter := 0 134 requiredNumberOfPasses := int(stable/interval) + 1 135 return wait.Poll(interval, timeout, func() (done bool, err error) { 136 if condition() { 137 passCounter++ 138 } else { 139 passCounter = 0 140 } 141 return passCounter >= requiredNumberOfPasses, nil 142 }) 143 }