clockz

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Mar 19, 2026 License: MIT Imports: 4 Imported by: 0

README

clockz

CI Status codecov Go Report Card CodeQL Go Reference License Go Version Release

Type-safe clock abstractions for Go with zero dependencies.

Build time-dependent code that's deterministic to test and simple to reason about.

Deterministic Time

func TestRetryBackoff(t *testing.T) {
    // Create a clock frozen at a specific moment
    clock := clockz.NewFakeClockAt(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC))
    service := NewService(clock)

    go service.RetryWithBackoff()

    // Advance through retry delays instantly
    clock.Advance(1 * time.Second)  // First retry
    clock.Advance(2 * time.Second)  // Second retry
    clock.Advance(4 * time.Second)  // Third retry

    // Test completes in milliseconds, not seconds
}

No sleeps. No flaky timeouts. Time moves when you say so.

Installation

go get github.com/zoobz-io/clockz

Requires Go 1.24+

Quick Start

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/zoobz-io/clockz"
)

type Service struct {
    clock clockz.Clock
}

func (s *Service) ProcessWithTimeout(ctx context.Context) error {
    ctx, cancel := s.clock.WithTimeout(ctx, 30*time.Second)
    defer cancel()

    timer := s.clock.NewTimer(5 * time.Second)
    defer timer.Stop()

    select {
    case t := <-timer.C():
        fmt.Printf("Processing at %v\n", t)
        return nil
    case <-ctx.Done():
        return ctx.Err()
    }
}

func main() {
    // Production: real time
    service := &Service{clock: clockz.RealClock}
    if err := service.ProcessWithTimeout(context.Background()); err != nil {
        fmt.Printf("Error: %v\n", err)
    }
}

Capabilities

Feature Description Docs
RealClock Production clock wrapping time package API Reference
FakeClock Test clock with manual time control API Reference
Timers & Tickers Create, stop, reset timing primitives Concepts
Context Integration WithTimeout and WithDeadline support Concepts
Time Advancement Advance() and SetTime() for tests Testing Guide

Why clockz?

  • Deterministic tests — Eliminate time-based flakiness entirely
  • Zero dependencies — Only the standard library
  • Thread-safe — All operations safe for concurrent use
  • Familiar API — Mirrors the time package interface
  • Context-aware — Built-in timeout and deadline support

Documentation

Learn

Guides

Cookbook

Reference

Contributing

Contributions welcome. See CONTRIBUTING.md for guidelines.

make help      # Available commands
make check     # Run tests and lint

License

MIT — see LICENSE

Documentation

Overview

Package clockz provides a fake clock implementation for deterministic testing.

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.
	Now() time.Time

	// After waits for the duration to elapse and then sends the current time
	// on the returned channel.
	After(d time.Duration) <-chan time.Time

	// AfterFunc waits for the duration to elapse and then executes f
	// in its own goroutine. It returns a Timer that can be used to
	// cancel the call using its Stop method.
	AfterFunc(d time.Duration, f func()) Timer

	// NewTimer creates a new Timer that will send the current time
	// on its channel after at least duration d.
	NewTimer(d time.Duration) Timer

	// NewTicker returns a new Ticker containing a channel that will
	// send the time with a period specified by the duration argument.
	NewTicker(d time.Duration) Ticker

	// Sleep blocks for the specified duration.
	// Implemented as simple blocking on After() channel.
	Sleep(d time.Duration)

	// Since returns the time elapsed since t.
	// Equivalent to Now().Sub(t).
	Since(t time.Time) time.Duration

	// WithTimeout returns a context that cancels after the specified duration.
	// Equivalent to context.WithTimeout but uses clock time source.
	WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc)

	// WithDeadline returns a context that cancels at the specified deadline.
	// Equivalent to context.WithDeadline but uses clock time source.
	WithDeadline(ctx context.Context, deadline time.Time) (context.Context, context.CancelFunc)
}

Clock provides an interface for time operations, enabling both real and mock implementations for testing.

var RealClock Clock = &realClock{}

RealClock is the default Clock implementation using the standard time package.

type FakeClock

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

FakeClock implements Clock for testing purposes. It allows manual control of time progression.

func NewFakeClock

func NewFakeClock() *FakeClock

NewFakeClock creates a new FakeClock set to time.Now().

func NewFakeClockAt

func NewFakeClockAt(t time.Time) *FakeClock

NewFakeClockAt creates a new FakeClock set to the given time.

func (*FakeClock) Advance

func (f *FakeClock) Advance(d time.Duration)

Advance advances the fake clock by the given duration.

func (*FakeClock) After

func (f *FakeClock) After(d time.Duration) <-chan time.Time

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

func (*FakeClock) AfterFunc

func (f *FakeClock) AfterFunc(d time.Duration, fn func()) Timer

AfterFunc waits for the duration to elapse and then executes f.

func (*FakeClock) BlockUntilReady

func (f *FakeClock) BlockUntilReady()

BlockUntilReady blocks until all pending operations have completed. This includes both AfterFunc callbacks, timer channel deliveries, and context operations.

Timer channel deliveries are processed using non-blocking sends that preserve the original semantics - abandoned channels are skipped without hanging the call.

Use this method to ensure deterministic timing in tests:

clock.Advance(duration)
clock.BlockUntilReady()  // Guarantees all timers processed
// Now safe to check timer channels or send additional data

func (*FakeClock) HasWaiters

func (f *FakeClock) HasWaiters() bool

HasWaiters returns true if there are any waiters (timers or contexts).

func (*FakeClock) NewTicker

func (f *FakeClock) NewTicker(d time.Duration) Ticker

NewTicker returns a new Ticker. Panics if d <= 0, matching time.NewTicker behavior.

func (*FakeClock) NewTimer

func (f *FakeClock) NewTimer(d time.Duration) Timer

NewTimer creates a new Timer.

func (*FakeClock) Now

func (f *FakeClock) Now() time.Time

Now returns the current time of the fake clock.

func (*FakeClock) SetTime

func (f *FakeClock) SetTime(t time.Time)

SetTime sets the fake clock to the given time.

func (*FakeClock) Since

func (f *FakeClock) Since(t time.Time) time.Duration

Since returns the time elapsed since t.

func (*FakeClock) Sleep

func (f *FakeClock) Sleep(d time.Duration)

Sleep blocks for the specified duration.

func (*FakeClock) WithDeadline

func (f *FakeClock) WithDeadline(ctx context.Context, deadline time.Time) (context.Context, context.CancelFunc)

WithDeadline returns a context that cancels at the specified deadline. Uses fake clock time source for deterministic testing.

func (*FakeClock) WithTimeout

func (f *FakeClock) WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc)

WithTimeout returns a context that cancels after the specified duration. Uses fake clock time source for deterministic testing.

type Ticker

type Ticker interface {
	// Stop turns off a ticker. After Stop, no more ticks will be sent.
	Stop()

	// C returns the channel on which the ticks are delivered.
	C() <-chan time.Time
}

Ticker holds a channel that delivers ticks of a clock at intervals.

type Timer

type Timer interface {
	// 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.
	Stop() bool

	// Reset changes the timer to expire after duration d.
	// It returns true if the timer had been active, false if
	// the timer had expired or been stopped.
	Reset(d time.Duration) bool

	// C returns the channel on which the time will be sent.
	C() <-chan time.Time
}

Timer represents a single event timer.

Jump to

Keyboard shortcuts

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