internal

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2025 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrClockIsRunning is returned when Start is called on a mock clock that is already running
	ErrClockIsRunning = errors.New("clock is running")

	// ErrClockNotRunning is returned when Stop is called on a mock clock that is not running.
	// It is also recovered from a panic when a mock clock method is called that requires the clock
	// to be running but is not.
	ErrClockNotRunning = errors.New("clock is stopped")

	// ErrNotADelorean is recovered from a panic resulting from an attempt to send a mock clock
	// back in time.
	ErrNotADelorean = errors.New("not a DeLorean clock (cannot go back in time)")

	// ErrClockLocked is returned when an operation is attempted on a mock clock that is locked
	ErrClockLocked = errors.New("clock is locked")
)

Functions

func ContextWithClock

func ContextWithClock(ctx context.Context, c Clock) context.Context

ContextWithClock returns a new context containing a specified clock

func WaitFor

func WaitFor(fn func())

func Waitable

func Waitable(fn func()) chan struct{}

Waitable is a helper function that runs the provided function in a goroutine and returns a channel that will be closed when the function completes. This is useful for waiting on asynchronous operations in tests.

FUTURE: support a timeout parameter to avoid indefinite waits in the event that the specified function does not complete.

Types

type Clock

type Clock interface {
	// After returns a channel that will send the current time after at least
	// duration d.
	After(d time.Duration) <-chan time.Time

	// AfterFunc waits for the duration to elapse and then calls f in its own
	// goroutine. It returns a Timer that can be used to stop the countdown
	// or to reset the Timer to run at a different time.
	//
	// If the Timer is stopped, the function f will not be called.
	AfterFunc(d time.Duration, f func()) *Timer

	// NewTicker returns a new Ticker that will send the current time on its
	// channel after each tick. The duration d must be greater than zero; if
	// d <= 0, NewTicker will panic.
	//
	// The duration of the Ticker can be modified using the Reset method
	//
	// The Ticker will continue ticking until Stop is called on it.
	NewTicker(d time.Duration) *Ticker

	// NewTimer returns a new Timer that will send the current time on its
	// channel after the duration d. The duration d must be greater than zero;
	// if d <= 0, NewTimer will panic.
	//
	// The duration of the Timer can be modified using the Reset method.
	//
	// The Timer will tick at the designated time unless Stop is called on it
	// beforehand.  A stopped Timer will resume if it is reset.
	NewTimer(d time.Duration) *Timer

	// Now returns the current time.
	Now() time.Time

	// Since returns the duration since t, according to the current time.  It is
	// shorthand for clock.Now().Sub(t).
	Since(t time.Time) time.Duration

	// Sleep pauses the calling goroutine for at least the duration d.
	Sleep(d time.Duration)

	// Tick returns a channel that will send the current time after each tick.
	// Unlike NewTicker, if the duration d is zero or negative, Tick will return
	// a nil channel and will not panic.
	Tick(d time.Duration) <-chan time.Time

	// Until returns the duration until t, according to the current time.  It is
	// shorthand for t.Sub(clock.Now()).
	Until(t time.Time) time.Duration

	// ContextWithDeadline returns a new context with the given deadline. If the
	// given time is in the past, the returned context is already done.
	//
	// This function should be used in preference over context.WithDeadline
	// to ensure that code relying on the deadline behaves correctly under test
	// conditions which may provide a mock clock in the parent context.
	ContextWithDeadline(ctx context.Context, d time.Time) (context.Context, context.CancelFunc)

	// ContextWithDeadlineCause returns a new context with the given deadline and
	// cause. If the given time is in the past, the returned context is already
	// done.
	//
	// The cause is used to set the context error.
	//
	// This function should be used in preference over context.WithDeadlineCause
	// to ensure that code relying on the deadline behaves correctly under test
	// conditions which may provide a mock clock in the parent context.
	ContextWithDeadlineCause(ctx context.Context, d time.Time, cause error) (context.Context, context.CancelFunc)

	// ContextWithTimeout returns a new context with the given timeout. If the
	// given duration is zero or negative, the returned context is already done.
	//
	// This function should be used in preference over context.WithTimeout
	// to ensure that code relying on the timeout behaves correctly under test
	// conditions which may provide a mock clock in the parent context.
	ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)

	// ContextWithTimeoutCause returns a new context with the given timeout and
	// cause. If the given duration is zero or negative, the returned context is
	// already done.
	//
	// The cause is used to set the context error.
	//
	// This function should be used in preference over context.WithTimeoutCause
	// to ensure that code relying on the timeout behaves correctly under test
	// conditions which may provide a mock clock in the parent context.
	ContextWithTimeoutCause(ctx context.Context, d time.Duration, cause error) (context.Context, context.CancelFunc)
}

Clock represents an interface described by the functions in the time package of the standard library. It extends the time package with additional methods to create contexts with deadlines and timeouts based on the clock providing the interface.

This allows for the creation of mock clocks for testing purposes through an API that is similar to and consistent with that of the system clock in the standard library `time` package.

var SystemClockInstance Clock = SystemClock{}

func ClockFromContext

func ClockFromContext(ctx context.Context) Clock

ClockFromContext returns the Clock in the specified context. Returns nil if no Clock is present.

type MockClock

type MockClock struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

MockClock represents a mock clock that moves forward from an established time and can be advanced, rewound or reset at will.

func NewMockClock

func NewMockClock(options ...MockClockOption) *MockClock

NewMockClock returns a new mock clock

func (*MockClock) AdvanceBy

func (m *MockClock) AdvanceBy(d time.Duration)

AdvanceBy moves the clock forward by the specified duration. This should only be called from a single goroutine at a time.

func (*MockClock) AdvanceTo

func (m *MockClock) AdvanceTo(t time.Time)

AdvanceTo is used to move the current time of the mock clock to a specific time, executing all timers that would be triggered during that passage of time.

No attempt is made to simulate the expected elapsed time between the current time and the new time or any relative time between timers.

func (*MockClock) After

func (m *MockClock) After(d time.Duration) <-chan time.Time

After waits for the duration to elapse and then sends the current time on the returned channel.

func (*MockClock) AfterFunc

func (m *MockClock) AfterFunc(d time.Duration, f func()) *Timer

AfterFunc waits for the duration to elapse and then executes a function in its own goroutine. A Timer is returned that can be stopped.

func (*MockClock) ContextWithDeadline

func (m *MockClock) ContextWithDeadline(ctx context.Context, t time.Time) (context.Context, context.CancelFunc)

ContextWithDeadline returns a new context with the given deadline.

func (*MockClock) ContextWithDeadlineCause

func (m *MockClock) ContextWithDeadlineCause(ctx context.Context, t time.Time, cause error) (context.Context, context.CancelFunc)

ContextWithDeadlineCause returns a new context with the given deadline and cause.

func (*MockClock) ContextWithTimeout

func (m *MockClock) ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)

ContextWithTimeout returns a new context with the given timeout.

func (*MockClock) ContextWithTimeoutCause

func (m *MockClock) ContextWithTimeoutCause(ctx context.Context, d time.Duration, cause error) (context.Context, context.CancelFunc)

ContextWithTimeoutCause returns a new context with the given timeout and cause.

func (*MockClock) CreatedAt

func (m *MockClock) CreatedAt() time.Time

CreatedAt returns the time at which the clock was created.

func (*MockClock) IsRunning

func (m *MockClock) IsRunning() bool

IsRunning returns true if the clock is in a running state.

In the running state the clock is advanced by elapsed time whenever Now() is obtained from the clock or when Update() is explicitly called. AdvanceBy() and AdvanceTo() are not supported in the running state and will panic.

The running state is not the default state of the clock; it must be set using the StartRunning option when creating the clock or by calling Start() on the created clock.

A running clock may be stopped by calling Stop() on that clock.

func (*MockClock) NewTicker

func (m *MockClock) NewTicker(d time.Duration) *Ticker

Ticker creates a new instance of Ticker.

func (*MockClock) NewTimer

func (m *MockClock) NewTimer(d time.Duration) *Timer

Timer creates a new Timer. Since this is a mock implementation, the Timer will not fire until the clock is advanced.

func (*MockClock) Now

func (m *MockClock) Now() time.Time

Now returns the current wall time according to the mock clock.

If the clock is frozen, the time will not advance until the clock is unfrozen.

If the clock is not frozen, the clock will first advance by the time elapsed since the clock was last updated.

func (*MockClock) Since

func (m *MockClock) Since(t time.Time) time.Duration

Since returns time since `t` using the mock clock's wall time.

func (*MockClock) SinceCreated

func (m *MockClock) SinceCreated() time.Duration

SinceCreated returns the elapsed mock time since the clock was created. This is the same as calling clock.Since(clock.CreatedAt()).

func (*MockClock) Sleep

func (m *MockClock) Sleep(d time.Duration)

Sleep pauses the goroutine for the given duration.

If the duration is zero or negative the function returns immediately.

If the clock is running, the duration is passed to time.Sleep() to suspend the calling goroutine for the given duration.

If the clock is stopped, the duration is passed to After() and the calling goroutine will block until the clock is advanced by at least the specified duration.

The clock must be moved forward in a separate goroutine.

func (*MockClock) Start

func (m *MockClock) Start()

Start decrements the stop counter on the clock.

func (*MockClock) Stop

func (m *MockClock) Stop()

Stop increments the stop counter on the clock. When > 0, the clock is prevented from advancing implicitly with Update() and must be advanced explicitly using AdvanceBy() or AdvanceTo().

Every call to Stop() must be matched with a call to Start() to resume implicit advancement.

func (*MockClock) Tick

func (m *MockClock) Tick(d time.Duration) <-chan time.Time

Tick is a convenience function for Ticker(). It will return a ticker channel that cannot be stopped or nil if the given duration is 0 or negative.

func (*MockClock) Until

func (m *MockClock) Until(t time.Time) time.Duration

Until returns time until `t` using the mock clock's wall time.

func (*MockClock) Update

func (m *MockClock) Update()

Update moves the current time of the mock clock forward by a duration corresponding to the passage of real-time since it was last advanced.

Calling this method while the clock is frozen will result in a panic.

type MockClockOption

type MockClockOption func(*MockClock)

MockClockOption represents an option that can be passed to NewMockClock.

func AtTime

func AtTime(t time.Time) MockClockOption

AtTime sets the initial time of the mock clock.

This may be useful for testing purposes when you want to start the clock at a particular time whilst retaining the ability to control the advancement of time. The time is set in the location of the clock.

Default

1970-01-01 00:00:00 +0000 UTC (in the location of the clock)

func DropsTicks

func DropsTicks() MockClockOption

DropsTicks sets the mock clock to drop ticks when the clock is advanced. That is, if the clock is advanced by a duration that would ordinarily result in a ticker being triggered more than once, the clock will only trigger a single tick event for the final tick.

Example

When a ticker is set to tick every 300 ms and the clock is advanced by 1s:

  • in normal operation, the ticker will be triggered at 300ms, 600ms and 900ms.

  • with DropsTicks applied, the clock will only send a tick event for the final tick at 900ms.

This may be used to simulate a reader that is reading from a Ticker and failing to "keep up". This is not ideal since it is using the clock to simulate the reader behaviour but may be easier than contriving that reader behaviour in other ways for testing purposes.

Default

not set/disabled

func InLocation

func InLocation(loc *time.Location) MockClockOption

InLocation sets the mock clock to the given location; the time returned by Now() will be in this location.

It is not normally necessary to set the location of the clock but may be useful when you want to start the clock in a particular location whilst retaining the ability to control the advancement of time.

Default

UTC

func StartRunning

func StartRunning() MockClockOption

StartRunning sets the mock clock to start in a running state. In this state the clock is advanced by elapsed time whenever Now() is obtained from the clock or when Update() is explicitly called.

AdvanceBy() and AdvanceTo() are not supported when the clock is in a running state and will panic.

This more closely mimics the behaviour of a real clock but means that tests will run in real-time; this is not recommended for most tests as it will make them run more slowly than they might.

Default

not set / stopped

func YieldTime

func YieldTime(d time.Duration) MockClockOption

YieldTime sets a duration for which the calling goroutine will be suspended when performing operations such as advancing the clock or adding a timer or ticker.

This allows other goroutines to be scheduled at times when it may be useful for a test. The duration should rarely need to be changed and should not be set to a value that is too high as this will cause a test to run more slowly than it might.

To disable this behaviour (not recommended) set the duration to 0.

Default

1ms

type MockContext

type MockContext struct {
	sync.Mutex
	// contains filtered or unexported fields
}

MockContext is a mock implementation of context.Context that uses a MockClock to simulate the passage of time. It allows for testing of context cancellation and deadlines without relying on real time.

func NewMockContext

func NewMockContext(
	parent context.Context,
	clock Clock,
	deadline time.Time,
	cause error,
) (*MockContext, context.CancelFunc)

NewMockContext returns a new context with the given deadline and a cancellable timer.

If the specified deadline has already passed, the context is immediately cancelled with context.DeadlineExceeded.

func (*MockContext) Deadline

func (c *MockContext) Deadline() (time.Time, bool)

func (*MockContext) Done

func (c *MockContext) Done() <-chan struct{}

func (*MockContext) Err

func (c *MockContext) Err() error

func (*MockContext) String

func (c *MockContext) String() string

func (*MockContext) Value

func (c *MockContext) Value(key any) any

type StartFuncs

type StartFuncs struct {
	sync.WaitGroup
	// contains filtered or unexported fields
}

func (*StartFuncs) OnStart

func (wg *StartFuncs) OnStart(fn func())

func (*StartFuncs) Start

func (wg *StartFuncs) Start()

type SystemClock

type SystemClock struct{}

SystemClock implements the Clock interface using the standard time package. It provides methods to interact with the system clock, such as getting the current time, sleeping for a duration, and creating timers and tickers.

func (SystemClock) After

func (c SystemClock) After(d time.Duration) <-chan time.Time

func (SystemClock) AfterFunc

func (c SystemClock) AfterFunc(d time.Duration, f func()) *Timer

func (SystemClock) ContextWithDeadline

func (c SystemClock) ContextWithDeadline(ctx context.Context, d time.Time) (context.Context, context.CancelFunc)

func (SystemClock) ContextWithDeadlineCause

func (c SystemClock) ContextWithDeadlineCause(ctx context.Context, d time.Time, cause error) (context.Context, context.CancelFunc)

func (SystemClock) ContextWithTimeout

func (c SystemClock) ContextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)

func (SystemClock) ContextWithTimeoutCause

func (c SystemClock) ContextWithTimeoutCause(ctx context.Context, d time.Duration, cause error) (context.Context, context.CancelFunc)

func (SystemClock) NewTicker

func (c SystemClock) NewTicker(d time.Duration) *Ticker

func (SystemClock) NewTimer

func (c SystemClock) NewTimer(d time.Duration) *Timer

func (SystemClock) Now

func (c SystemClock) Now() time.Time

func (SystemClock) Since

func (c SystemClock) Since(t time.Time) time.Duration

func (SystemClock) Sleep

func (c SystemClock) Sleep(d time.Duration)

func (SystemClock) Tick

func (c SystemClock) Tick(d time.Duration) <-chan time.Time

func (SystemClock) Until

func (c SystemClock) Until(t time.Time) time.Duration

type Tickables

type Tickables []tickable

Tickables represents a list of mock Tickables; it supports sorting by next tick time.

func (Tickables) Equal

func (a Tickables) Equal(target Tickables) bool

func (Tickables) Len

func (a Tickables) Len() int

func (Tickables) Less

func (a Tickables) Less(i, j int) bool

func (Tickables) Swap

func (a Tickables) Swap(i, j int)

type Ticker

type Ticker struct {
	// wraps a time.Timer in normal use; for a mock, this is non-nil but is
	// used only as a container for the <-chan time.Time read-only reference
	// to the mock timer's channel.
	*time.Ticker
	// contains filtered or unexported fields
}

Ticker implements a ticker that can be used with a mock clock

func (*Ticker) Reset

func (t *Ticker) Reset(d time.Duration)

Reset resets the ticker to the specified duration.

If the Ticker has been stopped it is restarted with the new duration.

If the Ticker is already running it will be reset to the new duration; the next tick will occur at the specified duration from the current time.

The function panics if the given duration is zero or negative, or if the Ticker has not been initialized.

func (*Ticker) Stop

func (t *Ticker) Stop()

Stop stops the ticker and prevents any further ticks from being sent to the channel; the channel is not closed.

type Timer

type Timer struct {
	// wraps a time.Timer in normal use; for a mock, this is non-nil but is
	// used only as a container for the <-chan time.Time read-only reference
	// to the mock timer's channel.
	*time.Timer
	// contains filtered or unexported fields
}

Timer implements a timer that can be used with a mock clock

func (*Timer) Reset

func (t *Timer) Reset(d time.Duration) bool

Reset modifies the timer to expire after duration d from the current time. If the timer has already expired it is re-activated.

Returns true if the timer was already active, false if the timer had expired or been stopped (and was re-activated).

func (*Timer) Stop

func (t *Timer) Stop() bool

Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the timer has already expired or been stopped.

type WaitFuncs

type WaitFuncs struct {
	sync.WaitGroup
}

func (*WaitFuncs) Go

func (wg *WaitFuncs) Go(fn func())

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL