github.com/gofunct/common@v0.0.0-20190131174352-fd058c7fbf22/pkg/clock/testing/fake_clock.go (about) 1 package testing 2 3 import ( 4 "sync" 5 "time" 6 7 "k8s.io/utils/clock" 8 ) 9 10 var ( 11 _ = clock.Clock(&FakeClock{}) 12 _ = clock.Clock(&IntervalClock{}) 13 ) 14 15 // FakeClock implements clock.Clock, but returns an arbitrary time. 16 type FakeClock struct { 17 lock sync.RWMutex 18 time time.Time 19 20 // waiters are waiting for the fake time to pass their specified time 21 waiters []*fakeClockWaiter 22 } 23 24 type fakeClockWaiter struct { 25 targetTime time.Time 26 stepInterval time.Duration 27 skipIfBlocked bool 28 destChan chan time.Time 29 fired bool 30 } 31 32 // NewFakeClock constructs a fake clock set to the provided time. 33 func NewFakeClock(t time.Time) *FakeClock { 34 return &FakeClock{ 35 time: t, 36 } 37 } 38 39 // Now returns f's time. 40 func (f *FakeClock) Now() time.Time { 41 f.lock.RLock() 42 defer f.lock.RUnlock() 43 return f.time 44 } 45 46 // Since returns time since the time in f. 47 func (f *FakeClock) Since(ts time.Time) time.Duration { 48 f.lock.RLock() 49 defer f.lock.RUnlock() 50 return f.time.Sub(ts) 51 } 52 53 // After is the fake version of time.After(d). 54 func (f *FakeClock) After(d time.Duration) <-chan time.Time { 55 f.lock.Lock() 56 defer f.lock.Unlock() 57 stopTime := f.time.Add(d) 58 ch := make(chan time.Time, 1) // Don't block! 59 f.waiters = append(f.waiters, &fakeClockWaiter{ 60 targetTime: stopTime, 61 destChan: ch, 62 }) 63 return ch 64 } 65 66 // NewTimer constructs a fake timer, akin to time.NewTimer(d). 67 func (f *FakeClock) NewTimer(d time.Duration) clock.Timer { 68 f.lock.Lock() 69 defer f.lock.Unlock() 70 stopTime := f.time.Add(d) 71 ch := make(chan time.Time, 1) // Don't block! 72 timer := &fakeTimer{ 73 fakeClock: f, 74 waiter: fakeClockWaiter{ 75 targetTime: stopTime, 76 destChan: ch, 77 }, 78 } 79 f.waiters = append(f.waiters, &timer.waiter) 80 return timer 81 } 82 83 // Tick constructs a fake ticker, akin to time.Tick 84 func (f *FakeClock) Tick(d time.Duration) <-chan time.Time { 85 if d <= 0 { 86 return nil 87 } 88 f.lock.Lock() 89 defer f.lock.Unlock() 90 tickTime := f.time.Add(d) 91 ch := make(chan time.Time, 1) // hold one tick 92 f.waiters = append(f.waiters, &fakeClockWaiter{ 93 targetTime: tickTime, 94 stepInterval: d, 95 skipIfBlocked: true, 96 destChan: ch, 97 }) 98 99 return ch 100 } 101 102 // Step moves the clock by Duration and notifies anyone that's called After, 103 // Tick, or NewTimer. 104 func (f *FakeClock) Step(d time.Duration) { 105 f.lock.Lock() 106 defer f.lock.Unlock() 107 f.setTimeLocked(f.time.Add(d)) 108 } 109 110 // SetTime sets the time. 111 func (f *FakeClock) SetTime(t time.Time) { 112 f.lock.Lock() 113 defer f.lock.Unlock() 114 f.setTimeLocked(t) 115 } 116 117 // Actually changes the time and checks any waiters. f must be write-locked. 118 func (f *FakeClock) setTimeLocked(t time.Time) { 119 f.time = t 120 newWaiters := make([]*fakeClockWaiter, 0, len(f.waiters)) 121 for i := range f.waiters { 122 w := f.waiters[i] 123 if !w.targetTime.After(t) { 124 125 if w.skipIfBlocked { 126 select { 127 case w.destChan <- t: 128 w.fired = true 129 default: 130 } 131 } else { 132 w.destChan <- t 133 w.fired = true 134 } 135 136 if w.stepInterval > 0 { 137 for !w.targetTime.After(t) { 138 w.targetTime = w.targetTime.Add(w.stepInterval) 139 } 140 newWaiters = append(newWaiters, w) 141 } 142 143 } else { 144 newWaiters = append(newWaiters, f.waiters[i]) 145 } 146 } 147 f.waiters = newWaiters 148 } 149 150 // HasWaiters returns true if After has been called on f but not yet satisfied (so you can 151 // write race-free tests). 152 func (f *FakeClock) HasWaiters() bool { 153 f.lock.RLock() 154 defer f.lock.RUnlock() 155 return len(f.waiters) > 0 156 } 157 158 // Sleep is akin to time.Sleep 159 func (f *FakeClock) Sleep(d time.Duration) { 160 f.Step(d) 161 } 162 163 // IntervalClock implements clock.Clock, but each invocation of Now steps the clock forward the specified duration 164 type IntervalClock struct { 165 Time time.Time 166 Duration time.Duration 167 } 168 169 // Now returns i's time. 170 func (i *IntervalClock) Now() time.Time { 171 i.Time = i.Time.Add(i.Duration) 172 return i.Time 173 } 174 175 // Since returns time since the time in i. 176 func (i *IntervalClock) Since(ts time.Time) time.Duration { 177 return i.Time.Sub(ts) 178 } 179 180 // After is unimplemented, will panic. 181 // TODO: make interval clock use FakeClock so this can be implemented. 182 func (*IntervalClock) After(d time.Duration) <-chan time.Time { 183 panic("IntervalClock doesn't implement After") 184 } 185 186 // NewTimer is unimplemented, will panic. 187 // TODO: make interval clock use FakeClock so this can be implemented. 188 func (*IntervalClock) NewTimer(d time.Duration) clock.Timer { 189 panic("IntervalClock doesn't implement NewTimer") 190 } 191 192 // Tick is unimplemented, will panic. 193 // TODO: make interval clock use FakeClock so this can be implemented. 194 func (*IntervalClock) Tick(d time.Duration) <-chan time.Time { 195 panic("IntervalClock doesn't implement Tick") 196 } 197 198 // Sleep is unimplemented, will panic. 199 func (*IntervalClock) Sleep(d time.Duration) { 200 panic("IntervalClock doesn't implement Sleep") 201 } 202 203 var _ = clock.Timer(&fakeTimer{}) 204 205 // fakeTimer implements clock.Timer based on a FakeClock. 206 type fakeTimer struct { 207 fakeClock *FakeClock 208 waiter fakeClockWaiter 209 } 210 211 // C returns the channel that notifies when this timer has fired. 212 func (f *fakeTimer) C() <-chan time.Time { 213 return f.waiter.destChan 214 } 215 216 // Stop stops the timer and returns true if the timer has not yet fired, or false otherwise. 217 func (f *fakeTimer) Stop() bool { 218 f.fakeClock.lock.Lock() 219 defer f.fakeClock.lock.Unlock() 220 221 newWaiters := make([]*fakeClockWaiter, 0, len(f.fakeClock.waiters)) 222 for i := range f.fakeClock.waiters { 223 w := f.fakeClock.waiters[i] 224 if w != &f.waiter { 225 newWaiters = append(newWaiters, w) 226 } 227 } 228 229 f.fakeClock.waiters = newWaiters 230 231 return !f.waiter.fired 232 } 233 234 // Reset resets the timer to the fake clock's "now" + d. It returns true if the timer has not yet 235 // fired, or false otherwise. 236 func (f *fakeTimer) Reset(d time.Duration) bool { 237 f.fakeClock.lock.Lock() 238 defer f.fakeClock.lock.Unlock() 239 240 active := !f.waiter.fired 241 242 f.waiter.fired = false 243 f.waiter.targetTime = f.fakeClock.time.Add(d) 244 245 var isWaiting bool 246 for i := range f.fakeClock.waiters { 247 w := f.fakeClock.waiters[i] 248 if w == &f.waiter { 249 isWaiting = true 250 break 251 } 252 } 253 if !isWaiting { 254 f.fakeClock.waiters = append(f.fakeClock.waiters, &f.waiter) 255 } 256 257 return active 258 }