1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
|
// Package clock provides an abstraction for system time that enables
// testing of time-sensitive code.
//
// Where you'd use time.Now, instead use clk.Now where clk is an
// instance of Clock.
//
// When running your code in production, pass it a Clock given by
// Default() and when you're running it in your tests, pass it an
// instance of Clock from NewFake().
//
// When you do that, you can use FakeClock's Add and Set methods to
// control how time behaves in your code making them more reliable
// while also expanding the space of problems you can test.
//
// This code intentionally does not attempt to provide an abstraction
// over time.Ticker and time.Timer because Go does not have the
// runtime or API hooks available to do reliably. See
// https://github.com/golang/go/issues/8869
//
// Be sure to test Time equality with time.Time#Equal, not ==.
package clock
import (
"sort"
"sync"
"time"
)
var systemClock Clock = sysClock{}
// Default returns a Clock that matches the actual system time.
func Default() Clock {
// This is a method instead of a public var to prevent folks from
// "making things work" by writing to the var instead of passing
// in a Clock.
return systemClock
}
// Clock is an abstraction over system time. New instances of it can
// be made with Default and NewFake.
type Clock interface {
// Now returns the Clock's current view of the time. Mutating the
// returned Time will not mutate the clock's time.
Now() time.Time
// Sleep causes the current goroutine to sleep for the given duration.
Sleep(time.Duration)
// After returns a channel that fires after the given duration.
After(time.Duration) <-chan time.Time
// NewTimer makes a Timer based on this clock's time. Using Timers and
// negative durations in the Clock or Timer API is undefined behavior and
// may be changed.
NewTimer(time.Duration) *Timer
}
type sysClock struct{}
func (s sysClock) Now() time.Time {
return time.Now()
}
func (s sysClock) Sleep(d time.Duration) {
time.Sleep(d)
}
func (s sysClock) After(d time.Duration) <-chan time.Time {
return time.After(d)
}
func (s sysClock) NewTimer(d time.Duration) *Timer {
tt := time.NewTimer(d)
return &Timer{C: tt.C, timer: tt}
}
// NewFake returns a FakeClock to be used in tests that need to
// manipulate time. Its initial value is always the unix epoch in the
// UTC timezone. The FakeClock returned is thread-safe.
func NewFake() FakeClock {
// We're explicit about this time construction to avoid early user
// questions about why the time object doesn't have a Location by
// default.
return &fake{t: time.Unix(0, 0).UTC()}
}
// FakeClock is a Clock with additional controls. The return value of
// Now return can be modified with Add. Use NewFake to get a
// thread-safe FakeClock implementation.
type FakeClock interface {
Clock
// Adjust the time that will be returned by Now.
Add(d time.Duration)
// Set the Clock's time to exactly the time given.
Set(t time.Time)
}
// To prevent mistakes with the API, we hide this behind NewFake. It's
// easy forget to create a pointer to a fake since time.Time (and
// sync.Mutex) are also simple values. The code will appear to work
// but the clock's time will never be adjusted.
type fake struct {
sync.RWMutex
t time.Time
sends sortedSends
}
func (f *fake) Now() time.Time {
f.RLock()
defer f.RUnlock()
return f.t
}
func (f *fake) Sleep(d time.Duration) {
if d < 0 {
// time.Sleep just returns immediately. Do the same.
return
}
f.Add(d)
}
func (f *fake) After(d time.Duration) <-chan time.Time {
return f.NewTimer(d).C
}
func (f *fake) NewTimer(d time.Duration) *Timer {
f.Lock()
defer f.Unlock()
ch := make(chan time.Time, 1)
tt := f.t.Add(d)
ft := &fakeTimer{c: ch, clk: f, active: true}
t := &Timer{
C: ch,
fakeTimer: ft,
}
s := f.addSend(tt, ft)
ft.sends = []*send{s}
return t
}
func (f *fake) Add(d time.Duration) {
f.Lock()
defer f.Unlock()
f.t = f.t.Add(d)
f.sendTimes()
}
func (f *fake) Set(t time.Time) {
f.Lock()
defer f.Unlock()
f.t = t
f.sendTimes()
}
// Only to be called while the fake's lock is held
func (f *fake) sendTimes() {
newSends := make(sortedSends, 0)
for _, s := range f.sends {
if !s.active || !s.ft.active {
continue
}
if s.target.Equal(f.t) || s.target.Before(f.t) {
s.ft.active = false
s.active = false
// The select is to drop second sends from resets without a user
// receiving from ft.c.
select {
case s.ft.c <- s.target:
default:
}
}
if s.active {
newSends = append(newSends, s)
}
}
f.sends = newSends
}
// Only to be called while the fake's lock is held
func (f *fake) addSend(target time.Time, ft *fakeTimer) *send {
s := &send{target: target, ft: ft, active: true}
f.sends = append(f.sends, s)
// This will be a small enough slice to be fast. Can be replaced with a more
// complicated container if someone is making many timers.
sort.Sort(f.sends)
return s
}
// send is a struct that represents a scheduled send of a time.Time to its
// fakeTimer's channel. They are actually sent when the relevant fake's time
// goes equal or past their target time, as long as the relevant fakeTimer has
// not been Reset or Stop'ed. When a Timer is Reset, the old sends are
// deactivated and will be removed from the clocks list on the next attempt to
// send.
type send struct {
target time.Time
active bool
ft *fakeTimer
}
type sortedSends []*send
func (s sortedSends) Len() int {
return len(s)
}
func (s sortedSends) Less(i, j int) bool {
return s[i].target.Before(s[j].target)
}
func (s sortedSends) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
|