clock

package
v0.212.0 Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2026 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package clock is a time-source seam for deterministic fake-time testing of time-driven components. Production code takes a Clock; tests inject a Fake and step it with Advance, so timer-driven logic runs deterministically with no sleeps.

The seam carries the three shapes timer consumers actually use:

  • Now — the current instant.
  • After — the CHANNEL shape: `case <-clk.After(d):` in a select loop. The returned channel is buffered (cap 1) in both implementations, so a receiver that abandons the channel never blocks the sender.
  • AfterFunc — the CALLBACK shape, returning a Stop handle mirroring time.Timer.Stop.

Fake fires both shapes during Advance in deadline-ascending order, ties broken by insertion order, deterministically. Both Real and Fake are safe for concurrent use; Fake's Advance is intended to be driven from a single test goroutine.

Authored in Go+ and distributed as generated Go — consumers never need the goplus toolchain.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Clock

type Clock interface {
	// Now returns the current time: wall-clock in production, step-driven
	// in fake-time tests.
	Now() time.Time

	// After returns a channel that fires once with the fire time after d.
	// Matches stdlib time.After shape; the channel has buffer 1 in both
	// implementations so an abandoned receiver never leaks a blocked sender.
	After(d time.Duration) <-chan time.Time

	// AfterFunc schedules fn after d and returns a Stop handle that can
	// cancel a pending fire (mirrors time.AfterFunc + time.Timer.Stop).
	AfterFunc(d time.Duration, fn func()) Stop
}

Clock is the time-source seam.

type Fake

type Fake struct {
	// contains filtered or unexported fields
}

Fake is the test Clock: time moves only via Advance. Both After channels and AfterFunc callbacks registered before an Advance fire during it, in (deadline, insertion-seq) order. After and AfterFunc share one insertion counter, so cross-shape ties are deterministic too.

Registrations from multiple goroutines are serialized by the internal mutex; Advance is intended to be called from a single test-driver goroutine. Tests that need barrier-style synchronization between Advance and a goroutine consuming an After channel must add their own sync.

func NewFake

func NewFake(start time.Time) *Fake

NewFake returns a Fake anchored at start. The zero time is permitted.

func (*Fake) Advance

func (c *Fake) Advance(d time.Duration)

Advance moves time forward by d and fires every due registration in (deadline, insertion-seq) order. Time advances chronologically: a callback observes its own deadline from Now, and Now reaches the step target after the final event. Callbacks run without the mutex, so they may register more work; registrations due by the target participate in the same drain.

func (*Fake) After

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

After returns a buffered (cap 1) channel that fires at the first Advance whose new now covers c.now + d. A d <= 0 fires synchronously before returning (the buffer absorbs the send), matching stdlib semantics.

func (*Fake) AfterFunc

func (c *Fake) AfterFunc(d time.Duration, fn func()) Stop

AfterFunc schedules fn at deadline c.now + d; it fires during a later Advance. Unlike After, a d <= 0 cannot fire synchronously — invoking fn under the mutex would deadlock re-entrant registrations — so it registers at deadline == now and fires on the next Advance (in the same pass as any callback that registered it; see Advance).

func (*Fake) Now

func (c *Fake) Now() time.Time

Now returns the current step-clock time.

func (*Fake) PendingLen

func (c *Fake) PendingLen() int

PendingLen reports the number of After registrations awaiting Advance — the observability seam for synchronizing an Advance against a goroutine's re-registration of its next tick (poll PendingLen to the expected count, then Advance).

func (*Fake) TimerLen added in v0.212.0

func (c *Fake) TimerLen() int

TimerLen reports the number of live AfterFunc registrations. Stopped and fired timers are removed lazily by Advance and are not counted.

type Real

type Real struct{}

Real is the production Clock: delegates to time.Now / time.After / time.AfterFunc. The zero value is usable.

func (Real) After

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

After returns time.After(d).

NOTE: stdlib time.After allocates a timer per call that stays live until it fires. For tight loops with large periods + frequent cancellation, consumers may prefer an explicit time.NewTimer + Stop pattern; for the common one-timer-in-flight loop the cost is amortized.

func (Real) AfterFunc

func (Real) AfterFunc(d time.Duration, fn func()) Stop

AfterFunc wraps time.AfterFunc.

func (Real) Now

func (Real) Now() time.Time

Now returns time.Now().

type Stop

type Stop interface {
	Stop() bool
}

Stop is the per-timer cancellation handle returned by AfterFunc. Stop returns true iff the call stopped a not-yet-fired, not-already-stopped timer (the fn will NOT fire); false otherwise.

Jump to

Keyboard shortcuts

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