2023-01-27 21:37:20 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
2020-03-19 00:37:15 +00:00
|
|
|
|
2020-04-29 02:56:11 +00:00
|
|
|
package tstest
|
2020-03-19 00:37:15 +00:00
|
|
|
|
2020-03-27 10:05:25 +00:00
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
2020-03-19 00:37:15 +00:00
|
|
|
|
2023-07-06 20:09:24 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2020-03-19 00:37:15 +00:00
|
|
|
// Clock is a testing clock that advances every time its Now method is
|
|
|
|
// called, beginning at Start.
|
|
|
|
//
|
|
|
|
// The zero value starts virtual time at an arbitrary value recorded
|
2020-03-27 07:38:11 +00:00
|
|
|
// in Start on the first call to Now, and time never advances.
|
2020-03-19 00:37:15 +00:00
|
|
|
type Clock struct {
|
|
|
|
// Start is the first value returned by Now.
|
|
|
|
Start time.Time
|
|
|
|
// Step is how much to advance with each Now call.
|
|
|
|
Step time.Duration
|
|
|
|
// Present is the time that the next Now call will receive.
|
|
|
|
Present time.Time
|
2020-03-27 10:05:25 +00:00
|
|
|
|
|
|
|
sync.Mutex
|
2020-03-19 00:37:15 +00:00
|
|
|
}
|
|
|
|
|
2022-09-25 18:29:55 +00:00
|
|
|
// Now returns the virtual clock's current time, and advances it
|
2020-03-19 00:37:15 +00:00
|
|
|
// according to its step configuration.
|
|
|
|
func (c *Clock) Now() time.Time {
|
2020-03-27 10:05:25 +00:00
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
2020-07-11 05:16:36 +00:00
|
|
|
c.initLocked()
|
|
|
|
step := c.Step
|
|
|
|
ret := c.Present
|
|
|
|
c.Present = c.Present.Add(step)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Clock) Advance(d time.Duration) {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
c.initLocked()
|
|
|
|
c.Present = c.Present.Add(d)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Clock) initLocked() {
|
2020-03-19 00:37:15 +00:00
|
|
|
if c.Start.IsZero() {
|
|
|
|
c.Start = time.Now()
|
2020-04-17 20:19:41 +00:00
|
|
|
}
|
|
|
|
if c.Present.Before(c.Start) {
|
2020-03-19 00:37:15 +00:00
|
|
|
c.Present = c.Start
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset rewinds the virtual clock to its start time.
|
|
|
|
func (c *Clock) Reset() {
|
2020-03-27 10:05:25 +00:00
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
2020-03-19 00:37:15 +00:00
|
|
|
c.Present = c.Start
|
|
|
|
}
|
2023-07-06 20:09:24 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|