Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd314742

Browse files
committed
feat: add NewTicker to clock testing library
1 parent7049d7a commitd314742

File tree

6 files changed

+216
-17
lines changed

6 files changed

+216
-17
lines changed

‎clock/clock.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@ import (
1010
)
1111

1212
typeClockinterface {
13+
// NewTicker returns a new Ticker containing a channel that will send the current time on the
14+
// channel after each tick. The period of the ticks is specified by the duration argument. The
15+
// ticker will adjust the time interval or drop ticks to make up for slow receivers. The
16+
// duration d must be greater than zero; if not, NewTicker will panic. Stop the ticker to
17+
// release associated resources.
18+
NewTicker(d time.Duration,tags...string)*Ticker
1319
// TickerFunc is a convenience function that calls f on the interval d until either the given
1420
// context expires or f returns an error. Callers may call Wait() on the returned Waiter to
15-
// wait until this happens and obtain the error.
21+
// wait until this happens and obtain the error. The duration d must be greater than zero; if
22+
// not, TickerFunc will panic.
1623
TickerFunc(ctx context.Context,d time.Duration,ffunc()error,tags...string)Waiter
1724
// NewTimer creates a new Timer that will send the current time on its channel after at least
1825
// duration d.

‎clock/mock.go

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ type event interface {
3232
}
3333

3434
func (m*Mock)TickerFunc(ctx context.Context,d time.Duration,ffunc()error,tags...string)Waiter {
35+
ifd<=0 {
36+
panic("TickerFunc called with negative or zero duration")
37+
}
3538
m.mu.Lock()
3639
deferm.mu.Unlock()
3740
c:=newCall(clockFunctionTickerFunc,tags,withDuration(d))
@@ -51,6 +54,28 @@ func (m *Mock) TickerFunc(ctx context.Context, d time.Duration, f func() error,
5154
returnt
5255
}
5356

57+
func (m*Mock)NewTicker(d time.Duration,tags...string)*Ticker {
58+
ifd<=0 {
59+
panic("NewTicker called with negative or zero duration")
60+
}
61+
m.mu.Lock()
62+
deferm.mu.Unlock()
63+
c:=newCall(clockFunctionNewTicker,tags,withDuration(d))
64+
m.matchCallLocked(c)
65+
deferclose(c.complete)
66+
// 1 element buffer follows standard library implementation
67+
ticks:=make(chan time.Time,1)
68+
t:=&Ticker{
69+
C:ticks,
70+
c:ticks,
71+
d:d,
72+
nxt:m.cur.Add(d),
73+
mock:m,
74+
}
75+
m.addEventLocked(t)
76+
returnt
77+
}
78+
5479
func (m*Mock)NewTimer(d time.Duration,tags...string)*Timer {
5580
m.mu.Lock()
5681
deferm.mu.Unlock()
@@ -70,7 +95,7 @@ func (m *Mock) NewTimer(d time.Duration, tags ...string) *Timer {
7095
got.fire(t.mock.cur)
7196
returnt
7297
}
73-
m.addTimerLocked(t)
98+
m.addEventLocked(t)
7499
returnt
75100
}
76101

@@ -91,7 +116,7 @@ func (m *Mock) AfterFunc(d time.Duration, f func(), tags ...string) *Timer {
91116
got.fire(t.mock.cur)
92117
returnt
93118
}
94-
m.addTimerLocked(t)
119+
m.addEventLocked(t)
95120
returnt
96121
}
97122

@@ -122,8 +147,8 @@ func (m *Mock) Until(t time.Time, tags ...string) time.Duration {
122147
returnt.Sub(m.cur)
123148
}
124149

125-
func (m*Mock)addTimerLocked(t*Timer) {
126-
m.all=append(m.all,t)
150+
func (m*Mock)addEventLocked(eevent) {
151+
m.all=append(m.all,e)
127152
m.recomputeNextLocked()
128153
}
129154

@@ -152,20 +177,12 @@ func (m *Mock) removeTimer(t *Timer) {
152177
}
153178

154179
func (m*Mock)removeTimerLocked(t*Timer) {
155-
deferm.recomputeNextLocked()
156180
t.stopped=true
157-
vareevent=t
158-
fori:=rangem.all {
159-
ifm.all[i]==e {
160-
m.all=append(m.all[:i],m.all[i+1:]...)
161-
return
162-
}
163-
}
181+
m.removeEventLocked(t)
164182
}
165183

166-
func (m*Mock)removeTickerFuncLocked(ct*mockTickerFunc) {
184+
func (m*Mock)removeEventLocked(eevent) {
167185
deferm.recomputeNextLocked()
168-
vareevent=ct
169186
fori:=rangem.all {
170187
ifm.all[i]==e {
171188
m.all=append(m.all[:i],m.all[i+1:]...)
@@ -371,6 +388,18 @@ func (t Trapper) TickerFuncWait(tags ...string) *Trap {
371388
returnt.mock.newTrap(clockFunctionTickerFuncWait,tags)
372389
}
373390

391+
func (tTrapper)NewTicker(tags...string)*Trap {
392+
returnt.mock.newTrap(clockFunctionNewTicker,tags)
393+
}
394+
395+
func (tTrapper)TickerStop(tags...string)*Trap {
396+
returnt.mock.newTrap(clockFunctionTickerStop,tags)
397+
}
398+
399+
func (tTrapper)TickerReset(tags...string)*Trap {
400+
returnt.mock.newTrap(clockFunctionTickerReset,tags)
401+
}
402+
374403
func (tTrapper)Now(tags...string)*Trap {
375404
returnt.mock.newTrap(clockFunctionNow,tags)
376405
}
@@ -459,7 +488,7 @@ func (m *mockTickerFunc) exitLocked(err error) {
459488
}
460489
m.done=true
461490
m.err=err
462-
m.mock.removeTickerFuncLocked(m)
491+
m.mock.removeEventLocked(m)
463492
m.cond.Broadcast()
464493
}
465494

@@ -493,6 +522,9 @@ const (
493522
clockFunctionTimerReset
494523
clockFunctionTickerFunc
495524
clockFunctionTickerFuncWait
525+
clockFunctionNewTicker
526+
clockFunctionTickerReset
527+
clockFunctionTickerStop
496528
clockFunctionNow
497529
clockFunctionSince
498530
clockFunctionUntil

‎clock/mock_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,90 @@ func TestAfterFunc_NegativeDuration(t *testing.T) {
8080
t.Fatal("timer still running")
8181
}
8282
}
83+
84+
funcTestNewTicker(t*testing.T) {
85+
t.Parallel()
86+
// nolint:gocritic // trying to avoid Coder-specific stuff with an eye toward spinning this out
87+
ctx,cancel:=context.WithTimeout(context.Background(),1000*time.Second)
88+
defercancel()
89+
90+
mClock:=clock.NewMock(t)
91+
start:=mClock.Now()
92+
trapNT:=mClock.Trap().NewTicker("new")
93+
defertrapNT.Close()
94+
trapStop:=mClock.Trap().TickerStop("stop")
95+
defertrapStop.Close()
96+
trapReset:=mClock.Trap().TickerReset("reset")
97+
defertrapReset.Close()
98+
99+
tickers:=make(chan*clock.Ticker,1)
100+
gofunc() {
101+
tickers<-mClock.NewTicker(time.Hour,"new")
102+
}()
103+
c:=trapNT.MustWait(ctx)
104+
c.Release()
105+
ifc.Duration!=time.Hour {
106+
t.Fatalf("expected time.Hour, got: %v",c.Duration)
107+
}
108+
tkr:=<-tickers
109+
110+
fori:=0;i<3;i++ {
111+
mClock.Advance(time.Hour).MustWait(ctx)
112+
}
113+
114+
// should get first tick, rest dropped
115+
tTime:=start.Add(time.Hour)
116+
select {
117+
case<-ctx.Done():
118+
t.Fatal("timeout waiting for ticker")
119+
casetick:=<-tkr.C:
120+
if!tick.Equal(tTime) {
121+
t.Fatalf("expected time %v, got %v",tTime,tick)
122+
}
123+
}
124+
125+
gotkr.Reset(time.Minute,"reset")
126+
c=trapReset.MustWait(ctx)
127+
mClock.Advance(time.Second).MustWait(ctx)
128+
c.Release()
129+
ifc.Duration!=time.Minute {
130+
t.Fatalf("expected time.Minute, got: %v",c.Duration)
131+
}
132+
mClock.Advance(time.Minute).MustWait(ctx)
133+
134+
// tick should show present time, ensuring the 2 hour ticks got dropped when
135+
// we didn't read from the channel.
136+
tTime=mClock.Now()
137+
select {
138+
case<-ctx.Done():
139+
t.Fatal("timeout waiting for ticker")
140+
casetick:=<-tkr.C:
141+
if!tick.Equal(tTime) {
142+
t.Fatalf("expected time %v, got %v",tTime,tick)
143+
}
144+
}
145+
146+
gotkr.Stop("stop")
147+
trapStop.MustWait(ctx).Release()
148+
mClock.Advance(time.Hour).MustWait(ctx)
149+
select {
150+
case<-tkr.C:
151+
t.Fatal("ticker still running")
152+
default:
153+
// OK
154+
}
155+
156+
// Resetting after stop
157+
gotkr.Reset(time.Minute,"reset")
158+
trapReset.MustWait(ctx).Release()
159+
mClock.Advance(time.Minute).MustWait(ctx)
160+
tTime=mClock.Now()
161+
select {
162+
case<-ctx.Done():
163+
t.Fatal("timeout waiting for ticker")
164+
casetick:=<-tkr.C:
165+
if!tick.Equal(tTime) {
166+
t.Fatalf("expected time %v, got %v",tTime,tick)
167+
}
168+
}
169+
}

‎clock/real.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ func NewReal() Clock {
1111
returnrealClock{}
1212
}
1313

14+
func (realClock)NewTicker(d time.Duration,_...string)*Ticker {
15+
tkr:=time.NewTicker(d)
16+
return&Ticker{ticker:tkr,C:tkr.C}
17+
}
18+
1419
func (realClock)TickerFunc(ctx context.Context,d time.Duration,ffunc()error,_...string)Waiter {
1520
ct:=&realContextTicker{
1621
ctx:ctx,

‎clock/ticker.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package clock
2+
3+
import"time"
4+
5+
typeTickerstruct {
6+
C<-chan time.Time
7+
//nolint: revive
8+
cchan time.Time
9+
ticker*time.Ticker// realtime impl, if set
10+
d time.Duration// period, if set
11+
nxt time.Time// next tick time
12+
mock*Mock// mock clock, if set
13+
stoppedbool// true if the ticker is not running
14+
}
15+
16+
func (t*Ticker)fire(tt time.Time) {
17+
t.mock.mu.Lock()
18+
defert.mock.mu.Unlock()
19+
ift.stopped {
20+
return
21+
}
22+
for!t.nxt.After(t.mock.cur) {
23+
t.nxt=t.nxt.Add(t.d)
24+
}
25+
t.mock.recomputeNextLocked()
26+
select {
27+
caset.c<-tt:
28+
default:
29+
}
30+
}
31+
32+
func (t*Ticker)next() time.Time {
33+
returnt.nxt
34+
}
35+
36+
func (t*Ticker)Stop(tags...string) {
37+
ift.ticker!=nil {
38+
t.ticker.Stop()
39+
return
40+
}
41+
t.mock.mu.Lock()
42+
defert.mock.mu.Unlock()
43+
c:=newCall(clockFunctionTickerStop,tags)
44+
t.mock.matchCallLocked(c)
45+
deferclose(c.complete)
46+
t.mock.removeEventLocked(t)
47+
t.stopped=true
48+
}
49+
50+
func (t*Ticker)Reset(d time.Duration,tags...string) {
51+
ift.ticker!=nil {
52+
t.ticker.Reset(d)
53+
return
54+
}
55+
t.mock.mu.Lock()
56+
defert.mock.mu.Unlock()
57+
c:=newCall(clockFunctionTickerReset,tags,withDuration(d))
58+
t.mock.matchCallLocked(c)
59+
deferclose(c.complete)
60+
t.nxt=t.mock.cur.Add(d)
61+
t.d=d
62+
ift.stopped {
63+
t.stopped=false
64+
t.mock.addEventLocked(t)
65+
}else {
66+
t.mock.recomputeNextLocked()
67+
}
68+
}

‎clock/timer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,6 @@ func (t *Timer) Reset(d time.Duration, tags ...string) bool {
6464
t.mock.removeTimerLocked(t)
6565
t.stopped=false
6666
t.nxt=t.mock.cur.Add(d)
67-
t.mock.addTimerLocked(t)
67+
t.mock.addEventLocked(t)
6868
returnresult
6969
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp