diff --git a/net/dns/recursive/recursive_test.go b/net/dns/recursive/recursive_test.go index 40d5aa15b..04f765249 100644 --- a/net/dns/recursive/recursive_test.go +++ b/net/dns/recursive/recursive_test.go @@ -36,9 +36,9 @@ func init() { } func newResolver(tb testing.TB) *Resolver { - clock := &tstest.Clock{ + clock := tstest.NewClock(tstest.ClockOpts{ Step: 50 * time.Millisecond, - } + }) return &Resolver{ Logf: tb.Logf, timeNow: clock.Now, diff --git a/net/dnscache/messagecache_test.go b/net/dnscache/messagecache_test.go index 3b52759f4..41fc33448 100644 --- a/net/dnscache/messagecache_test.go +++ b/net/dnscache/messagecache_test.go @@ -18,9 +18,9 @@ import ( ) func TestMessageCache(t *testing.T) { - clock := &tstest.Clock{ + clock := tstest.NewClock(tstest.ClockOpts{ Start: time.Date(1987, 11, 1, 0, 0, 0, 0, time.UTC), - } + }) mc := &MessageCache{Clock: clock.Now} mc.SetMaxCacheSize(2) clock.Advance(time.Second) diff --git a/tstest/clock.go b/tstest/clock.go index fb7d4ea70..a5b4af55c 100644 --- a/tstest/clock.go +++ b/tstest/clock.go @@ -8,6 +8,56 @@ import ( "time" ) +// ClockOpts is used to configure the initial settings for a Clock. Once the +// settings are configured as desired, call NewClock to get the resulting Clock. +type ClockOpts struct { + // Start is the starting time for the Clock. When FollowRealTime is false, + // Start is also the value that will be returned by the first call + // to Clock.Now. + Start time.Time + // Step is the amount of time the Clock will advance whenever Clock.Now is + // called. If set to zero, the Clock will only advance when Clock.Advance is + // called and/or if FollowRealTime is true. + // + // FollowRealTime and Step cannot be enabled at the same time. + Step time.Duration + + // TimerChannelSize configures the maximum buffered ticks that are + // permitted in the channel of any Timer and Ticker created by this Clock. + // The special value 0 means to use the default of 1. The buffer may need to + // be increased if time is advanced by more than a single tick and proper + // functioning of the test requires that the ticks are not lost. + TimerChannelSize int + + // FollowRealTime makes the simulated time increment along with real time. + // It is a compromise between determinism and the difficulty of explicitly + // managing the simulated time via Step or Clock.Advance. When + // FollowRealTime is set, calls to Now() and PeekNow() will add the + // elapsed real-world time to the simulated time. + // + // FollowRealTime and Step cannot be enabled at the same time. + FollowRealTime bool +} + +// NewClock creates a Clock with the specified settings. To create a +// Clock with only the default settings, new(Clock) is equivalent, except that +// the start time will not be computed until one of the receivers is called. +func NewClock(co ClockOpts) *Clock { + if co.TimerChannelSize != 0 || co.FollowRealTime { + panic("TimerChannelSize and FollowRealTime are not implemented yet") + } + + clock := &Clock{ + Start: co.Start, + Step: co.Step, + } + clock.Lock() + defer clock.Unlock() + clock.initLocked() + + return clock +} + // Clock is a testing clock that advances every time its Now method is // called, beginning at Start. // @@ -58,3 +108,27 @@ func (c *Clock) Reset() { defer c.Unlock() c.Present = c.Start } + +// GetStart returns the initial simulated time when this Clock was created. +func (c *Clock) GetStart() time.Time { + c.Lock() + defer c.Unlock() + c.initLocked() + return c.Start +} + +// GetStep returns the amount that simulated time advances on every call to Now. +func (c *Clock) GetStep() time.Duration { + c.Lock() + defer c.Unlock() + c.initLocked() + return c.Step +} + +// SetStep updates the amount that simulated time advances on every call to Now. +func (c *Clock) SetStep(d time.Duration) { + c.Lock() + defer c.Unlock() + c.initLocked() + c.Step = d +} diff --git a/tsweb/tsweb_test.go b/tsweb/tsweb_test.go index 6d72c0d3d..8a3b0d974 100644 --- a/tsweb/tsweb_test.go +++ b/tsweb/tsweb_test.go @@ -65,10 +65,7 @@ func TestStdHandler(t *testing.T) { testErr = errors.New("test error") bgCtx = context.Background() // canceledCtx, cancel = context.WithCancel(bgCtx) - clock = tstest.Clock{ - Start: time.Now(), - Step: time.Second, - } + startTime = time.Unix(1687870000, 1234) ) // cancel() @@ -86,7 +83,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/"), wantCode: 200, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", TLS: false, @@ -103,7 +100,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 404, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -119,7 +116,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 404, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -136,7 +133,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 404, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -153,7 +150,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 500, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -170,7 +167,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 500, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -187,7 +184,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 500, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -204,7 +201,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 200, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -221,7 +218,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 200, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -238,7 +235,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 200, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", Host: "example.com", @@ -260,7 +257,7 @@ func TestStdHandler(t *testing.T) { r: req(bgCtx, "http://example.com/foo"), wantCode: 200, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", @@ -279,7 +276,7 @@ func TestStdHandler(t *testing.T) { http.Error(w, e.Msg, 200) }, wantLog: AccessLogRecord{ - When: clock.Start, + When: startTime, Seconds: 1.0, Proto: "HTTP/1.1", TLS: false, @@ -302,7 +299,10 @@ func TestStdHandler(t *testing.T) { t.Logf(fmt, args...) } - clock.Reset() + clock := tstest.NewClock(tstest.ClockOpts{ + Start: startTime, + Step: time.Second, + }) rec := noopHijacker{httptest.NewRecorder(), false} h := StdHandler(test.rh, HandlerOptions{Logf: logf, Now: clock.Now, OnError: test.errHandler})