Skip to content
This repository has been archived by the owner on May 18, 2023. It is now read-only.

Allow resetting previously stopped tickers #46

Merged
merged 1 commit into from
Apr 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions clock.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,13 @@ func (t *internalTimer) Tick(now time.Time) {

// Ticker holds a channel that receives "ticks" at regular intervals.
type Ticker struct {
C <-chan time.Time
c chan time.Time
ticker *time.Ticker // realtime impl, if set
next time.Time // next tick time
mock *Mock // mock clock, if set
d time.Duration // time between ticks
C <-chan time.Time
c chan time.Time
ticker *time.Ticker // realtime impl, if set
next time.Time // next tick time
mock *Mock // mock clock, if set
d time.Duration // time between ticks
stopped bool // True if stopped, false if running
}

// Stop turns off the ticker.
Expand All @@ -339,6 +340,7 @@ func (t *Ticker) Stop() {
} else {
t.mock.mu.Lock()
t.mock.removeClockTimer((*internalTicker)(t))
t.stopped = true
t.mock.mu.Unlock()
}
}
Expand All @@ -353,6 +355,11 @@ func (t *Ticker) Reset(dur time.Duration) {
t.mock.mu.Lock()
defer t.mock.mu.Unlock()

if t.stopped {
t.mock.timers = append(t.mock.timers, (*internalTicker)(t))
t.stopped = false
}

t.d = dur
t.next = t.mock.now.Add(dur)
}
Expand Down
66 changes: 66 additions & 0 deletions clock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@ func TestClock_Ticker_Rst(t *testing.T) {
ticker.Stop()
}

// Ensure that the clock's ticker can stop and then be reset correctly.
func TestClock_Ticker_Stop_Rst(t *testing.T) {
start := time.Now()
ticker := New().Ticker(20 * time.Millisecond)
<-ticker.C
ticker.Stop()
select {
case <-ticker.C:
t.Fatal("unexpected send")
case <-time.After(30 * time.Millisecond):
}
ticker.Reset(5 * time.Millisecond)
<-ticker.C
dur := time.Since(start)
if dur >= 60*time.Millisecond {
t.Fatal("took more than 60ms")
}
ticker.Stop()
}

// Ensure that the clock's timer waits correctly.
func TestClock_Timer(t *testing.T) {
start := time.Now()
Expand Down Expand Up @@ -475,6 +495,52 @@ func TestMock_Ticker_Reset(t *testing.T) {
}
}

func TestMock_Ticker_Stop_Reset(t *testing.T) {
var n int32
clock := NewMock()

ticker := clock.Ticker(5 * time.Second)
defer ticker.Stop()

go func() {
for {
<-ticker.C
atomic.AddInt32(&n, 1)
}
}()
gosched()

// Move clock forward.
clock.Add(10 * time.Second)
if atomic.LoadInt32(&n) != 2 {
t.Fatalf("expected 2, got: %d", n)
}

ticker.Stop()

// Move clock forward again.
clock.Add(5 * time.Second)
if atomic.LoadInt32(&n) != 2 {
t.Fatalf("still expected 2, got: %d", n)
}

ticker.Reset(2 * time.Second)

// Advance the remaining 2 seconds
clock.Add(2 * time.Second)

if atomic.LoadInt32(&n) != 3 {
t.Fatalf("expected 3, got: %d", n)
}

// Advance another 2 seconds
clock.Add(2 * time.Second)

if atomic.LoadInt32(&n) != 4 {
t.Fatalf("expected 4, got: %d", n)
}
}

// Ensure that multiple tickers can be used together.
func TestMock_Ticker_Multi(t *testing.T) {
var n int32
Expand Down