mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Factor PassiveClock out of clock.Clock
PassiveClock has the subset of Clock functionality that only involves reading the clock. Identifying this subset makes it possible to write packages that are more clearly easy to test. When a package is coded against Clock rather than PassiveClock this adds two problems for the unit test functions. One is that Clock provides no way for the test function to know when the next activity is scheduled for. That could be added to FakeClock relatively easily. The second problem is that when a package uses channels to schedule future activity, once the Clock has advanced to such a future time the Clock (and hence the test function) does not get informed when that activity has completed.
This commit is contained in:
parent
369b765052
commit
2e2e6b82e0
@ -21,11 +21,18 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PassiveClock allows for injecting fake or real clocks into code
|
||||||
|
// that needs to read the current time but does not support scheduling
|
||||||
|
// activity in the future.
|
||||||
|
type PassiveClock interface {
|
||||||
|
Now() time.Time
|
||||||
|
Since(time.Time) time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
// Clock allows for injecting fake or real clocks into code that
|
// Clock allows for injecting fake or real clocks into code that
|
||||||
// needs to do arbitrary things based on time.
|
// needs to do arbitrary things based on time.
|
||||||
type Clock interface {
|
type Clock interface {
|
||||||
Now() time.Time
|
PassiveClock
|
||||||
Since(time.Time) time.Duration
|
|
||||||
After(time.Duration) <-chan time.Time
|
After(time.Duration) <-chan time.Time
|
||||||
NewTimer(time.Duration) Timer
|
NewTimer(time.Duration) Timer
|
||||||
Sleep(time.Duration)
|
Sleep(time.Duration)
|
||||||
@ -66,10 +73,15 @@ func (RealClock) Sleep(d time.Duration) {
|
|||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeClock implements Clock, but returns an arbitrary time.
|
// FakePassiveClock implements PassiveClock, but returns an arbitrary time.
|
||||||
type FakeClock struct {
|
type FakePassiveClock struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
time time.Time
|
time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// FakeClock implements Clock, but returns an arbitrary time.
|
||||||
|
type FakeClock struct {
|
||||||
|
FakePassiveClock
|
||||||
|
|
||||||
// waiters are waiting for the fake time to pass their specified time
|
// waiters are waiting for the fake time to pass their specified time
|
||||||
waiters []fakeClockWaiter
|
waiters []fakeClockWaiter
|
||||||
@ -82,26 +94,39 @@ type fakeClockWaiter struct {
|
|||||||
destChan chan time.Time
|
destChan chan time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFakeClock(t time.Time) *FakeClock {
|
func NewFakePassiveClock(t time.Time) *FakePassiveClock {
|
||||||
return &FakeClock{
|
return &FakePassiveClock{
|
||||||
time: t,
|
time: t,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewFakeClock(t time.Time) *FakeClock {
|
||||||
|
return &FakeClock{
|
||||||
|
FakePassiveClock: *NewFakePassiveClock(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now returns f's time.
|
// Now returns f's time.
|
||||||
func (f *FakeClock) Now() time.Time {
|
func (f *FakePassiveClock) Now() time.Time {
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
defer f.lock.RUnlock()
|
defer f.lock.RUnlock()
|
||||||
return f.time
|
return f.time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since returns time since the time in f.
|
// Since returns time since the time in f.
|
||||||
func (f *FakeClock) Since(ts time.Time) time.Duration {
|
func (f *FakePassiveClock) Since(ts time.Time) time.Duration {
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
defer f.lock.RUnlock()
|
defer f.lock.RUnlock()
|
||||||
return f.time.Sub(ts)
|
return f.time.Sub(ts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the time.
|
||||||
|
func (f *FakePassiveClock) SetTime(t time.Time) {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
f.time = t
|
||||||
|
}
|
||||||
|
|
||||||
// Fake version of time.After(d).
|
// Fake version of time.After(d).
|
||||||
func (f *FakeClock) After(d time.Duration) <-chan time.Time {
|
func (f *FakeClock) After(d time.Duration) <-chan time.Time {
|
||||||
f.lock.Lock()
|
f.lock.Lock()
|
||||||
|
@ -33,20 +33,50 @@ var (
|
|||||||
_ = Ticker(&fakeTicker{})
|
_ = Ticker(&fakeTicker{})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SettablePassiveClock interface {
|
||||||
|
PassiveClock
|
||||||
|
SetTime(time.Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exercisePassiveClock(t *testing.T, pc SettablePassiveClock) {
|
||||||
|
t1 := time.Now()
|
||||||
|
t2 := t1.Add(time.Hour)
|
||||||
|
pc.SetTime(t1)
|
||||||
|
tx := pc.Now()
|
||||||
|
if tx != t1 {
|
||||||
|
t.Errorf("SetTime(%#+v); Now() => %#+v", t1, tx)
|
||||||
|
}
|
||||||
|
dx := pc.Since(t1)
|
||||||
|
if dx != 0 {
|
||||||
|
t.Errorf("Since() => %v", dx)
|
||||||
|
}
|
||||||
|
pc.SetTime(t2)
|
||||||
|
dx = pc.Since(t1)
|
||||||
|
if dx != time.Hour {
|
||||||
|
t.Errorf("Since() => %v", dx)
|
||||||
|
}
|
||||||
|
tx = pc.Now()
|
||||||
|
if tx != t2 {
|
||||||
|
t.Errorf("Now() => %#+v", tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFakePassiveClock(t *testing.T) {
|
||||||
|
startTime := time.Now()
|
||||||
|
tc := NewFakePassiveClock(startTime)
|
||||||
|
exercisePassiveClock(t, tc)
|
||||||
|
}
|
||||||
|
|
||||||
func TestFakeClock(t *testing.T) {
|
func TestFakeClock(t *testing.T) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
tc := NewFakeClock(startTime)
|
tc := NewFakeClock(startTime)
|
||||||
|
exercisePassiveClock(t, tc)
|
||||||
|
tc.SetTime(startTime)
|
||||||
tc.Step(time.Second)
|
tc.Step(time.Second)
|
||||||
now := tc.Now()
|
now := tc.Now()
|
||||||
if now.Sub(startTime) != time.Second {
|
if now.Sub(startTime) != time.Second {
|
||||||
t.Errorf("input: %s now=%s gap=%s expected=%s", startTime, now, now.Sub(startTime), time.Second)
|
t.Errorf("input: %s now=%s gap=%s expected=%s", startTime, now, now.Sub(startTime), time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
tt := tc.Now()
|
|
||||||
tc.SetTime(tt.Add(time.Hour))
|
|
||||||
if tc.Since(tt) != time.Hour {
|
|
||||||
t.Errorf("input: %s now=%s gap=%s expected=%s", tt, tc.Now(), tc.Since(tt), time.Hour)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFakeClockSleep(t *testing.T) {
|
func TestFakeClockSleep(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user