github.com/juju/clock@v1.0.3/testclock/clock.go (about) 1 // Copyright 2015-2018 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package testclock 5 6 import ( 7 "fmt" 8 "runtime/debug" 9 "sort" 10 "sync" 11 "time" 12 13 "github.com/juju/clock" 14 "github.com/juju/errors" 15 "github.com/juju/loggo" 16 ) 17 18 // timer implements a mock clock.Timer for testing purposes. 19 type timer struct { 20 deadline time.Time 21 clock *Clock 22 c chan time.Time 23 // trigger is called when the timer expires. It is 24 // called with the clock mutex held and will not block. 25 trigger func() 26 stack []byte 27 } 28 29 // Reset is part of the clock.Timer interface. 30 func (t *timer) Reset(d time.Duration) bool { 31 return t.clock.reset(t, d) 32 } 33 34 // Stop is part of the clock.Timer interface. 35 func (t *timer) Stop() bool { 36 return t.clock.stop(t) 37 } 38 39 // Chan is part of the clock.Timer interface. 40 func (t *timer) Chan() <-chan time.Time { 41 return t.c 42 } 43 44 // Clock implements a mock clock.Clock for testing purposes. 45 type Clock struct { 46 mu sync.Mutex 47 now time.Time 48 waiting []*timer // timers waiting to fire, sorted by deadline. 49 notifyAlarms chan struct{} 50 } 51 52 var _ AdvanceableClock = (*Clock)(nil) 53 54 // NewClock returns a new clock set to the supplied time. If your SUT needs to 55 // call After, AfterFunc, NewTimer or Timer.Reset more than 10000 times: (1) 56 // you have probably written a bad test; and (2) you'll need to read from the 57 // Alarms chan to keep the buffer clear. 58 func NewClock(now time.Time) *Clock { 59 return &Clock{ 60 now: now, 61 notifyAlarms: make(chan struct{}, 10000), 62 } 63 } 64 65 // Now is part of the clock.Clock interface. 66 func (clock *Clock) Now() time.Time { 67 clock.mu.Lock() 68 defer clock.mu.Unlock() 69 return clock.now 70 } 71 72 // After is part of the clock.Clock interface. 73 func (clock *Clock) After(d time.Duration) <-chan time.Time { 74 return clock.NewTimer(d).Chan() 75 } 76 77 func (clock *Clock) NewTimer(d time.Duration) clock.Timer { 78 c := make(chan time.Time, 1) 79 return clock.addAlarm(d, c, func() { 80 c <- clock.now 81 }) 82 } 83 84 // AfterFunc is part of the clock.Clock interface. 85 func (clock *Clock) AfterFunc(d time.Duration, f func()) clock.Timer { 86 return clock.addAlarm(d, nil, func() { 87 go f() 88 }) 89 } 90 91 func (clock *Clock) addAlarm(d time.Duration, c chan time.Time, trigger func()) *timer { 92 defer clock.notifyAlarm() 93 clock.mu.Lock() 94 defer clock.mu.Unlock() 95 t := &timer{ 96 c: c, 97 deadline: clock.now.Add(d), 98 clock: clock, 99 trigger: trigger, 100 stack: debug.Stack(), 101 } 102 clock.addTimer(t) 103 clock.triggerAll() 104 return t 105 } 106 107 // Advance advances the result of Now by the supplied duration, and sends 108 // the "current" time on all alarms which are no longer "in the future". 109 func (clock *Clock) Advance(d time.Duration) { 110 clock.mu.Lock() 111 defer clock.mu.Unlock() 112 clock.now = clock.now.Add(d) 113 if len(clock.waiting) == 0 { 114 loggo.GetLogger("juju.clock").Debugf("advancing a clock that has nothing waiting: cf. https://github.com/juju/juju/wiki/Intermittent-failures") 115 } 116 clock.triggerAll() 117 } 118 119 // WaitAdvance functions the same as Advance, but only if there is n timers in 120 // clock.waiting. This came about while fixing lp:1607044 intermittent 121 // failures. It turns out that testing.Clock.Advance might advance the time 122 // and trigger notifications before triggers are set. So we wait a limited time 123 // 'w' for 'n' timers to show up in clock.waiting, and if they do we advance 124 // 'd'. 125 func (clock *Clock) WaitAdvance(d, w time.Duration, n int) error { 126 pause := w / 10 127 if pause > 10*time.Millisecond { 128 pause = 10 * time.Millisecond 129 } 130 finalTimeout := time.After(w) 131 next := time.After(0) 132 for { 133 select { 134 case <-finalTimeout: 135 if clock.hasNWaiters(n) { 136 clock.Advance(d) 137 return nil 138 } 139 clock.mu.Lock() 140 got := len(clock.waiting) 141 var stacks string 142 for _, t := range clock.waiting { 143 stacks += fmt.Sprintf("timer deadline: %v\n%s", t.deadline, string(t.stack)) 144 } 145 clock.mu.Unlock() 146 return errors.Errorf( 147 "got %d timers added after waiting %s: wanted %d, stacks:\n%s", 148 got, w.String(), n, stacks) 149 case <-next: 150 if clock.hasNWaiters(n) { 151 clock.Advance(d) 152 return nil 153 } 154 next = time.After(pause) 155 } 156 } 157 } 158 159 // hasNWaiters checks if the clock currently has 'n' timers waiting to fire. 160 func (clock *Clock) hasNWaiters(n int) bool { 161 clock.mu.Lock() 162 hasWaiters := len(clock.waiting) == n 163 clock.mu.Unlock() 164 return hasWaiters 165 } 166 167 // Alarms returns a channel on which you can read one value for every call to 168 // After and AfterFunc; and for every successful Timer.Reset backed by this 169 // Clock. It might not be elegant but it's necessary when testing time logic 170 // that runs on a goroutine other than that of the test. 171 func (clock *Clock) Alarms() <-chan struct{} { 172 return clock.notifyAlarms 173 } 174 175 // triggerAll triggers any alarms that are currently due and removes them 176 // from clock.waiting. 177 func (clock *Clock) triggerAll() { 178 triggered := 0 179 for _, t := range clock.waiting { 180 if clock.now.Before(t.deadline) { 181 break 182 } 183 t.trigger() 184 triggered++ 185 } 186 clock.waiting = clock.waiting[triggered:] 187 } 188 189 // reset is the underlying implementation of clock.Timer.Reset, which may be 190 // called by any Timer backed by this Clock. 191 func (clock *Clock) reset(t *timer, d time.Duration) bool { 192 defer clock.notifyAlarm() 193 clock.mu.Lock() 194 defer clock.mu.Unlock() 195 196 found := false 197 for _, wt := range clock.waiting { 198 if wt == t { 199 found = true 200 } 201 } 202 if !found { 203 clock.waiting = append(clock.waiting, t) 204 } 205 t.deadline = clock.now.Add(d) 206 sort.Sort(byDeadline(clock.waiting)) 207 if d <= 0 { 208 // If duration is <= 0, that means we should be triggering the 209 // Timer right away, as "now" has already occured. 210 clock.triggerAll() 211 } 212 return found 213 } 214 215 // stop is the underlying implementation of clock.Timer.Reset, which may be 216 // called by any Timer backed by this Clock. 217 func (clock *Clock) stop(t *timer) bool { 218 clock.mu.Lock() 219 defer clock.mu.Unlock() 220 221 for i, wt := range clock.waiting { 222 if wt == t { 223 clock.waiting = removeFromSlice(clock.waiting, i) 224 return true 225 } 226 } 227 return false 228 } 229 230 // addTimer adds an alarm at time t. 231 func (clock *Clock) addTimer(t *timer) { 232 clock.waiting = append(clock.waiting, t) 233 sort.Sort(byDeadline(clock.waiting)) 234 } 235 236 // notifyAlarm sends a value on the channel exposed by Alarms(). 237 func (clock *Clock) notifyAlarm() { 238 select { 239 case clock.notifyAlarms <- struct{}{}: 240 default: 241 panic("alarm notification buffer full") 242 } 243 } 244 245 // byDeadline is used to sort alarms by time. 246 type byDeadline []*timer 247 248 func (a byDeadline) Len() int { return len(a) } 249 func (a byDeadline) Less(i, j int) bool { return a[i].deadline.Before(a[j].deadline) } 250 func (a byDeadline) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 251 252 // removeFromSlice removes item at the specified index from the slice. 253 func removeFromSlice(sl []*timer, index int) []*timer { 254 return append(sl[:index], sl[index+1:]...) 255 } 256 257 // AutoAdvancingClock wraps a clock.Clock, calling the Advance 258 // function whenever After or AfterFunc are called. 259 type AutoAdvancingClock struct { 260 clock.Clock 261 Advance func(time.Duration) 262 } 263 264 func (c *AutoAdvancingClock) After(d time.Duration) <-chan time.Time { 265 ch := c.Clock.After(d) 266 c.Advance(d) 267 return ch 268 } 269 270 func (c *AutoAdvancingClock) AfterFunc(d time.Duration, f func()) clock.Timer { 271 t := c.Clock.AfterFunc(d, f) 272 c.Advance(d) 273 return t 274 }