watch

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 32 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Loop

func Loop(ctx context.Context, opts LoopOptions) error

Loop is the watch session: one cycle per batch, until the channel closes or the context ends.

Failures are sticky, and the accounting is per test name, not per package: a cycle that runs only a subset of a package's tests can clear exactly the names it executed and nothing more. A run that never executed the broken test — because narrowing selected different tests — cannot silently declare the package healthy, and an infrastructure error keeps the entry rather than evicting it: the one failure the mechanism must never drop is the one it could not re-check.

func RunTests

func RunTests(ctx context.Context, req RunRequest) (bool, []byte, error)

RunTests executes a warm binary against a -test.run pattern. A failing test is a result, not an error; errors are reserved for the binary not running at all.

Types

type Batch

type Batch struct {
	Paths  []string
	Resync bool
}

Batch is one debounced set of saves. Resync means the paths are unreliable — events were dropped — and the whole dirty tree must be reconsidered from scratch.

type BinaryCache

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

BinaryCache keeps one pre-linked test binary per package for the life of a watch session.

The savings this buys is the whole point of watch mode: `go test` pays flag parsing, cache probing and a relink on every invocation — around a second even when nothing changed — while a warm binary starts in milliseconds. Staleness is decided by the same fingerprint the index uses, computed over the working tree's content digests, so a binary survives exactly as long as its package's dependency closure is byte-identical.

func NewBinaryCache

func NewBinaryCache() (*BinaryCache, error)

func (*BinaryCache) Close

func (c *BinaryCache) Close()

func (*BinaryCache) Ensure

func (c *BinaryCache) Ensure(ctx context.Context, req BuildRequest) (string, bool, error)

Ensure returns a runnable test binary for the package, building one only when the cached binary's fingerprint no longer matches the working tree. The empty path with a nil error means the package has no test binary at all.

type BuildRequest

type BuildRequest struct {
	Root             string
	Env              []string
	Tags             []string
	ImportPath       string
	Fingerprint      cache.Fingerprint
	BuildParallelism int
}

type Cycle

type Cycle struct {
	Runs     []PlannedRun
	Deferred int
	Narrowed bool
	Note     string
}

Cycle is everything one save resolved to.

type LoopOptions

type LoopOptions struct {
	Planner  *Planner
	Binaries *BinaryCache
	Batches  <-chan Batch
	Out      io.Writer
	UseColor bool
	Jobs     int
	Timeout  time.Duration

	// Journal receives every attributable verdict the session produces. The
	// sticky mechanism makes watch a natural flake detector: a failure re-runs
	// on later cycles, and when the package's fingerprint has not moved, a pass
	// on the re-run is a verdict change with no code change. Nil records
	// nothing.
	Journal *flake.Journal
	// Quarantine labels failures of tests already known to be flaky, so a red
	// cycle says "possibly not your change" where that is the documented truth.
	Quarantine *flake.Quarantine
}

type PlannedRun

type PlannedRun struct {
	ImportPath string
	Dir        string
	// Pattern selects the tests to run; empty means the whole package.
	Pattern string
	Tests   []string
	Reason  string
	// Fingerprint keys the warm binary for this package.
	Fingerprint cache.Fingerprint
}

PlannedRun is one package's execution order for a cycle.

type Planner

type Planner struct {
	Root  string
	Tags  []string
	Env   []string
	Store *cache.Store
	// NoIndex disables line-level narrowing, mirroring the global --no-index
	// flag: plans stay at package precision even when an index exists.
	NoIndex bool
	// contains filtered or unexported fields
}

Planner turns "these files were just saved" into "run exactly these tests".

It reuses the whole selection pipeline — the same graph, the same narrowing, the same fail-safe rules — so watch mode can never skip a test that a CI run of `assay run` would have executed for the same dirty tree. What it adds is the delta: of everything the dirty tree affects, only the packages reachable from this save are run now.

func (*Planner) Invalidate

func (p *Planner) Invalidate()

Invalidate drops every piece of memoised state. Called when filesystem events were lost: a graph whose inputs may have changed unseen cannot be trusted to stay memoised.

func (*Planner) Plan

func (p *Planner) Plan(ctx context.Context, batch []string) (*Cycle, error)

type RunRequest

type RunRequest struct {
	Binary  string
	Dir     string
	Env     []string
	Pattern string
	Timeout time.Duration
}

type RunResult

type RunResult struct {
	ImportPath string
	Tests      []string
	// Fingerprint is the package content state this result was observed at,
	// which is the identity flake evidence is keyed by.
	Fingerprint cache.Fingerprint
	Passed      bool
	Skipped     bool
	Output      []byte
	Duration    time.Duration
	Reused      bool
	Err         error
}

RunResult is one package's test execution within a cycle. Skipped means no test binary exists — a package whose tests are all behind a build tag — which is a different claim from "ran and passed" and must never be counted as one.

type Watcher

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

Watcher turns raw filesystem events into debounced batches of relevant paths.

Editors are noisy — atomic-save temp files, backup files, a write event per chunk — so events are filtered to files that can change what runs (.go and module files) and collected until the tree has been quiet for the debounce window, or until a continuous writer has stretched the wait to its cap.

A batch send never blocks the event pump: if the consumer is mid-cycle and the channel is full, the paths stay in pending and ride out with the next flush. Overflow is the one event that cannot be papered over — the kernel dropped an unknown number of changes — so it surfaces as a resync batch (nil paths), which the planner treats as "run the whole dirty plan" and the loop treats as "drop the memoised graph".

func NewWatcher

func NewWatcher(root string, debounce time.Duration) (*Watcher, error)

func (*Watcher) Batches

func (w *Watcher) Batches() <-chan Batch

func (*Watcher) Close

func (w *Watcher) Close() error

func (*Watcher) Run

func (w *Watcher) Run(ctx context.Context)

Run pumps events until the context ends.

Jump to

Keyboard shortcuts

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