k3s/vendor/github.com/jmhodges/clock/clock.go

234 lines
6.1 KiB
Go

// 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"
)
// Some in-use reflection-heavy systems, like facebookgo/inject, fail when given
// a value type like sysClock{}. Since it's hidden by an interface, this has
// surprised users. We fixed that by making systemClock a &sysClock.
var systemClock Clock = &sysClock{}
// New returns a Clock that matches the actual system time.
func New() 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
}
// Deprecated: Default is just an alias for New but less memorable.
func Default() 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
// Since is a short hand for Now().Sub(t).
Since(time.Time) time.Duration
// 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) Since(t time.Time) time.Duration {
return time.Since(t)
}
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) Since(t time.Time) time.Duration {
return f.Now().Sub(t)
}
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]
}