github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/testing/clock.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "sort" 8 "sync" 9 "time" 10 11 "github.com/juju/testing" 12 "github.com/juju/utils/clock" 13 ) 14 15 // timerClock exposes the underlying Clock's capabilities to a Timer. 16 type timerClock interface { 17 reset(id int, d time.Duration) bool 18 stop(id int) bool 19 } 20 21 // Timer implements a mock clock.Timer for testing purposes. 22 type Timer struct { 23 ID int 24 clock timerClock 25 } 26 27 // Reset is part of the clock.Timer interface. 28 func (t *Timer) Reset(d time.Duration) bool { 29 return t.clock.reset(t.ID, d) 30 } 31 32 // Stop is part of the clock.Timer interface. 33 func (t *Timer) Stop() bool { 34 return t.clock.stop(t.ID) 35 } 36 37 // stoppedTimer is a no-op implementation of clock.Timer. 38 type stoppedTimer struct{} 39 40 // Reset is part of the clock.Timer interface. 41 func (stoppedTimer) Reset(time.Duration) bool { return false } 42 43 // Stop is part of the clock.Timer interface. 44 func (stoppedTimer) Stop() bool { return false } 45 46 // Clock implements a mock clock.Clock for testing purposes. 47 type Clock struct { 48 mu sync.Mutex 49 now time.Time 50 alarms []alarm 51 currentAlarmID int 52 notifyAlarms chan struct{} 53 } 54 55 // NewClock returns a new clock set to the supplied time. If your SUT needs to 56 // call After, AfterFunc, or Timer.Reset more than 1024 times: (1) you have 57 // probably written a bad test; and (2) you'll need to read from the Alarms 58 // chan to keep the buffer clear. 59 func NewClock(now time.Time) *Clock { 60 return &Clock{ 61 now: now, 62 notifyAlarms: make(chan struct{}, 1024), 63 } 64 } 65 66 // Now is part of the clock.Clock interface. 67 func (clock *Clock) Now() time.Time { 68 clock.mu.Lock() 69 defer clock.mu.Unlock() 70 return clock.now 71 } 72 73 // After is part of the clock.Clock interface. 74 func (clock *Clock) After(d time.Duration) <-chan time.Time { 75 defer clock.notifyAlarm() 76 clock.mu.Lock() 77 defer clock.mu.Unlock() 78 notify := make(chan time.Time, 1) 79 if d <= 0 { 80 notify <- clock.now 81 } else { 82 clock.setAlarm(clock.now.Add(d), func() { notify <- clock.now }) 83 } 84 return notify 85 } 86 87 // AfterFunc is part of the clock.Clock interface. 88 func (clock *Clock) AfterFunc(d time.Duration, f func()) clock.Timer { 89 defer clock.notifyAlarm() 90 clock.mu.Lock() 91 defer clock.mu.Unlock() 92 if d <= 0 { 93 f() 94 return &stoppedTimer{} 95 } 96 id := clock.setAlarm(clock.now.Add(d), f) 97 return &Timer{id, clock} 98 } 99 100 // Advance advances the result of Now by the supplied duration, and sends 101 // the "current" time on all alarms which are no longer "in the future". 102 func (clock *Clock) Advance(d time.Duration) { 103 clock.mu.Lock() 104 defer clock.mu.Unlock() 105 clock.now = clock.now.Add(d) 106 triggered := 0 107 for _, alarm := range clock.alarms { 108 if clock.now.Before(alarm.time) { 109 break 110 } 111 alarm.trigger() 112 triggered++ 113 } 114 clock.alarms = clock.alarms[triggered:] 115 } 116 117 // Alarms returns a channel on which you can read one value for every call to 118 // After and AfterFunc; and for every successful Timer.Reset backed by this 119 // Clock. It might not be elegant but it's necessary when testing time logic 120 // that runs on a goroutine other than that of the test. 121 func (clock *Clock) Alarms() <-chan struct{} { 122 return clock.notifyAlarms 123 } 124 125 // reset is the underlying implementation of clock.Timer.Reset, which may be 126 // called by any Timer backed by this Clock. 127 func (clock *Clock) reset(id int, d time.Duration) bool { 128 clock.mu.Lock() 129 defer clock.mu.Unlock() 130 131 for i, alarm := range clock.alarms { 132 if id == alarm.ID { 133 defer clock.notifyAlarm() 134 clock.alarms[i].time = clock.now.Add(d) 135 sort.Sort(byTime(clock.alarms)) 136 return true 137 } 138 } 139 return false 140 } 141 142 // stop is the underlying implementation of clock.Timer.Reset, which may be 143 // called by any Timer backed by this Clock. 144 func (clock *Clock) stop(id int) bool { 145 clock.mu.Lock() 146 defer clock.mu.Unlock() 147 148 for i, alarm := range clock.alarms { 149 if id == alarm.ID { 150 clock.alarms = removeFromSlice(clock.alarms, i) 151 return true 152 } 153 } 154 return false 155 } 156 157 // setAlarm adds an alarm at time t. 158 // It also sorts the alarms and increments the current ID by 1. 159 func (clock *Clock) setAlarm(t time.Time, trigger func()) int { 160 alarm := alarm{ 161 time: t, 162 trigger: trigger, 163 ID: clock.currentAlarmID, 164 } 165 clock.alarms = append(clock.alarms, alarm) 166 sort.Sort(byTime(clock.alarms)) 167 clock.currentAlarmID = clock.currentAlarmID + 1 168 return alarm.ID 169 } 170 171 // notifyAlarm sends a value on the channel exposed by Alarms(). 172 func (clock *Clock) notifyAlarm() { 173 select { 174 case clock.notifyAlarms <- struct{}{}: 175 default: 176 panic("alarm notification buffer full") 177 } 178 } 179 180 // alarm records the time at which we're expected to execute trigger. 181 type alarm struct { 182 ID int 183 time time.Time 184 trigger func() 185 } 186 187 // byTime is used to sort alarms by time. 188 type byTime []alarm 189 190 func (a byTime) Len() int { return len(a) } 191 func (a byTime) Less(i, j int) bool { return a[i].time.Before(a[j].time) } 192 func (a byTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 193 194 // removeFromSlice removes item at the specified index from the slice. 195 func removeFromSlice(sl []alarm, index int) []alarm { 196 return append(sl[:index], sl[index+1:]...) 197 } 198 199 type StubClock struct { 200 *testing.Stub 201 202 ReturnNow time.Time 203 ReturnAfter <-chan time.Time 204 ReturnAfterFunc clock.Timer 205 } 206 207 func NewStubClock(stub *testing.Stub) *StubClock { 208 return &StubClock{ 209 Stub: stub, 210 } 211 } 212 213 func (s *StubClock) Now() time.Time { 214 s.AddCall("Now") 215 s.NextErr() // pop one off 216 return s.ReturnNow 217 } 218 219 func (s *StubClock) After(d time.Duration) <-chan time.Time { 220 s.AddCall("After", d) 221 s.NextErr() // pop one off 222 return s.ReturnAfter 223 } 224 225 func (s *StubClock) AfterFunc(d time.Duration, f func()) clock.Timer { 226 s.AddCall("AfterFunc", d, f) 227 s.NextErr() // pop one off 228 return s.ReturnAfterFunc 229 }