Skip to content

Commit 8326a3a

Browse files
authored
chore: change mock clock to allow Advance() within timer/tick functions (#13500)
1 parent 7c081dc commit 8326a3a

File tree

10 files changed

+424
-217
lines changed

10 files changed

+424
-217
lines changed

clock/clock.go

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type Clock interface {
2626
Now(tags ...string) time.Time
2727
// Since returns the time elapsed since t. It is shorthand for Clock.Now().Sub(t).
2828
Since(t time.Time, tags ...string) time.Duration
29+
// Until returns the duration until t. It is shorthand for t.Sub(Clock.Now()).
30+
Until(t time.Time, tags ...string) time.Duration
2931
}
3032

3133
// Waiter can be waited on for an error.

clock/example_test.go

+67-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestExampleTickerFunc(t *testing.T) {
4444
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
4545
defer cancel()
4646

47-
mClock := clock.NewMock()
47+
mClock := clock.NewMock(t)
4848

4949
// Because the ticker is started on a goroutine, we can't immediately start
5050
// advancing the clock, or we will race with the start of the ticker. If we
@@ -76,9 +76,74 @@ func TestExampleTickerFunc(t *testing.T) {
7676
}
7777

7878
// Now that we know the ticker is started, we can advance the time.
79-
mClock.Advance(time.Hour).MustWait(ctx, t)
79+
mClock.Advance(time.Hour).MustWait(ctx)
8080

8181
if tks := tc.Ticks(); tks != 1 {
8282
t.Fatalf("expected 1 got %d ticks", tks)
8383
}
8484
}
85+
86+
type exampleLatencyMeasurer struct {
87+
mu sync.Mutex
88+
lastLatency time.Duration
89+
}
90+
91+
func newExampleLatencyMeasurer(ctx context.Context, clk clock.Clock) *exampleLatencyMeasurer {
92+
m := &exampleLatencyMeasurer{}
93+
clk.TickerFunc(ctx, 10*time.Second, func() error {
94+
start := clk.Now()
95+
// m.doSomething()
96+
latency := clk.Since(start)
97+
m.mu.Lock()
98+
defer m.mu.Unlock()
99+
m.lastLatency = latency
100+
return nil
101+
})
102+
return m
103+
}
104+
105+
func (m *exampleLatencyMeasurer) LastLatency() time.Duration {
106+
m.mu.Lock()
107+
defer m.mu.Unlock()
108+
return m.lastLatency
109+
}
110+
111+
func TestExampleLatencyMeasurer(t *testing.T) {
112+
t.Parallel()
113+
114+
// nolint:gocritic // trying to avoid Coder-specific stuff with an eye toward spinning this out
115+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
116+
defer cancel()
117+
118+
mClock := clock.NewMock(t)
119+
trap := mClock.Trap().Since()
120+
defer trap.Close()
121+
122+
lm := newExampleLatencyMeasurer(ctx, mClock)
123+
124+
w := mClock.Advance(10 * time.Second) // triggers first tick
125+
c := trap.MustWait(ctx) // call to Since()
126+
mClock.Advance(33 * time.Millisecond)
127+
c.Release()
128+
w.MustWait(ctx)
129+
130+
if l := lm.LastLatency(); l != 33*time.Millisecond {
131+
t.Fatalf("expected 33ms got %s", l.String())
132+
}
133+
134+
// Next tick is in 10s - 33ms, but if we don't want to calculate, we can use:
135+
d, w2 := mClock.AdvanceNext()
136+
c = trap.MustWait(ctx)
137+
mClock.Advance(17 * time.Millisecond)
138+
c.Release()
139+
w2.MustWait(ctx)
140+
141+
expectedD := 10*time.Second - 33*time.Millisecond
142+
if d != expectedD {
143+
t.Fatalf("expected %s got %s", expectedD.String(), d.String())
144+
}
145+
146+
if l := lm.LastLatency(); l != 17*time.Millisecond {
147+
t.Fatalf("expected 17ms got %s", l.String())
148+
}
149+
}

0 commit comments

Comments
 (0)