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 (*Fake) Advance ¶
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 ¶
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 ¶
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) PendingLen ¶
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).
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 ¶
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.