Skip to content

Commit 8884aa7

Browse files
authored
Merge pull request #325 from aojea/fake_clock_waiters
fake clock: expose the number of waiters
2 parents 1f6e0b7 + 755dc6a commit 8884aa7

File tree

2 files changed

+82
-2
lines changed

2 files changed

+82
-2
lines changed

clock/testing/fake_clock.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,14 +221,26 @@ func (f *FakeClock) setTimeLocked(t time.Time) {
221221
f.waiters = newWaiters
222222
}
223223

224-
// HasWaiters returns true if After or AfterFunc has been called on f but not yet satisfied (so you can
225-
// write race-free tests).
224+
// HasWaiters returns true if Waiters() returns non-0 (so you can write race-free tests).
226225
func (f *FakeClock) HasWaiters() bool {
227226
f.lock.RLock()
228227
defer f.lock.RUnlock()
229228
return len(f.waiters) > 0
230229
}
231230

231+
// Waiters returns the number of "waiters" on the clock (so you can write race-free
232+
// tests). A waiter exists for:
233+
// - every call to After that has not yet signaled its channel.
234+
// - every call to AfterFunc that has not yet called its callback.
235+
// - every timer created with NewTimer which is currently ticking.
236+
// - every ticker created with NewTicker which is currently ticking.
237+
// - every ticker created with Tick.
238+
func (f *FakeClock) Waiters() int {
239+
f.lock.RLock()
240+
defer f.lock.RUnlock()
241+
return len(f.waiters)
242+
}
243+
232244
// Sleep is akin to time.Sleep
233245
func (f *FakeClock) Sleep(d time.Duration) {
234246
f.Step(d)

clock/testing/fake_clock_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,71 @@ func assertReadTime(t testing.TB, c <-chan time.Time) time.Time {
444444
}
445445
panic("unreachable")
446446
}
447+
448+
func TestFakeClockWaiters(t *testing.T) {
449+
startTime := time.Now()
450+
tc := NewFakeClock(startTime)
451+
452+
// Initial state
453+
if count := tc.Waiters(); count != 0 {
454+
t.Errorf("Expected 0 waiters initially, got %d", count)
455+
}
456+
457+
// Add a Timer
458+
timer1 := tc.NewTimer(1 * time.Second)
459+
if count := tc.Waiters(); count != 1 {
460+
t.Errorf("Expected 1 waiter after NewTimer, got %d", count)
461+
}
462+
463+
// Add an After
464+
_ = tc.After(2 * time.Second)
465+
if count := tc.Waiters(); count != 2 {
466+
t.Errorf("Expected 2 waiters after After, got %d", count)
467+
}
468+
469+
// Add a Ticker
470+
ticker := tc.NewTicker(3 * time.Second)
471+
if count := tc.Waiters(); count != 3 {
472+
t.Errorf("Expected 3 waiters after NewTicker, got %d", count)
473+
}
474+
475+
// Step past the first timer
476+
tc.Step(1 * time.Second)
477+
<-timer1.C() // Drain channel
478+
if count := tc.Waiters(); count != 2 {
479+
t.Errorf("Expected 2 waiters after first timer fired, got %d", count)
480+
}
481+
482+
// Step past the After
483+
tc.Step(1 * time.Second)
484+
// Note: After channel is implicitly drained by setTimeLocked
485+
if count := tc.Waiters(); count != 1 {
486+
t.Errorf("Expected 1 waiter after After fired, got %d", count)
487+
}
488+
489+
// Step past the Ticker (it should re-arm)
490+
tc.Step(1 * time.Second)
491+
<-ticker.C() // Drain channel
492+
if count := tc.Waiters(); count != 1 {
493+
t.Errorf("Expected 1 waiter after Ticker fired (should re-arm), got %d", count)
494+
}
495+
496+
// Stop the ticker (Note: fakeTicker.Stop is currently a no-op, so this won't change the count)
497+
// If fakeTicker.Stop were implemented to remove the waiter, the expected count would be 0.
498+
ticker.Stop()
499+
if count := tc.Waiters(); count != 1 {
500+
t.Errorf("Expected 1 waiter after stopping ticker (no-op), got %d", count)
501+
}
502+
503+
// Add another timer and stop it
504+
timer2 := tc.NewTimer(5 * time.Second)
505+
if count := tc.Waiters(); count != 2 {
506+
t.Errorf("Expected 2 waiters after adding second timer, got %d", count)
507+
}
508+
if stopped := timer2.Stop(); !stopped {
509+
t.Errorf("Expected timer2.Stop() to return true")
510+
}
511+
if count := tc.Waiters(); count != 1 {
512+
t.Errorf("Expected 1 waiter after stopping second timer, got %d", count)
513+
}
514+
}

0 commit comments

Comments
 (0)