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

Commit4e071d4

Browse files
committed
feat: add NewTicker to clock testing library
1 parentd616464 commit4e071d4

File tree

6 files changed

+209
-16
lines changed

6 files changed

+209
-16
lines changed

‎clock/clock.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
)
1111

1212
typeClockinterface {
13+
NewTicker(d time.Duration,tags...string)*Ticker
1314
// TickerFunc is a convenience function that calls f on the interval d until either the given
1415
// context expires or f returns an error. Callers may call Wait() on the returned Waiter to
1516
// wait until this happens and obtain the error.

‎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