diff --git a/staging/src/k8s.io/apimachinery/pkg/util/clock/clock.go b/staging/src/k8s.io/apimachinery/pkg/util/clock/clock.go index 0d739d961f1..1689e62e829 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/clock/clock.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/clock/clock.go @@ -21,11 +21,18 @@ import ( "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 // needs to do arbitrary things based on time. type Clock interface { - Now() time.Time - Since(time.Time) time.Duration + PassiveClock After(time.Duration) <-chan time.Time NewTimer(time.Duration) Timer Sleep(time.Duration) @@ -66,10 +73,15 @@ func (RealClock) Sleep(d time.Duration) { time.Sleep(d) } -// FakeClock implements Clock, but returns an arbitrary time. -type FakeClock struct { +// FakePassiveClock implements PassiveClock, but returns an arbitrary time. +type FakePassiveClock struct { lock sync.RWMutex 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 []fakeClockWaiter @@ -82,26 +94,39 @@ type fakeClockWaiter struct { destChan chan time.Time } -func NewFakeClock(t time.Time) *FakeClock { - return &FakeClock{ +func NewFakePassiveClock(t time.Time) *FakePassiveClock { + return &FakePassiveClock{ time: t, } } +func NewFakeClock(t time.Time) *FakeClock { + return &FakeClock{ + FakePassiveClock: *NewFakePassiveClock(t), + } +} + // Now returns f's time. -func (f *FakeClock) Now() time.Time { +func (f *FakePassiveClock) Now() time.Time { f.lock.RLock() defer f.lock.RUnlock() return f.time } // 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() defer f.lock.RUnlock() 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). func (f *FakeClock) After(d time.Duration) <-chan time.Time { f.lock.Lock() diff --git a/staging/src/k8s.io/apimachinery/pkg/util/clock/clock_test.go b/staging/src/k8s.io/apimachinery/pkg/util/clock/clock_test.go index 9707acec4b8..16967d9a0c3 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/clock/clock_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/clock/clock_test.go @@ -33,20 +33,50 @@ var ( _ = 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) { startTime := time.Now() tc := NewFakeClock(startTime) + exercisePassiveClock(t, tc) + tc.SetTime(startTime) tc.Step(time.Second) now := tc.Now() if 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) {