Skip to Content
TestingDeterminism fixtures

Determinism fixtures

Reproducibility is the foundation every other part of the testing stack builds on. If two runs of the same test produce different seeds, different timestamps, or different run IDs, then no frame-checksum comparison, no shadow-run, and no soak test can be trusted.

ftui-harness centralises determinism in a single type, DeterminismFixture, driven by three environment variables.

Source: crates/ftui-harness/src/determinism.rs.

The three variables

VariableTypeDefaultEffect
E2E_DETERMINISTICbool-ish (1, true, yes)unset = offTurns on deterministic mode. Swaps wall-clock timestamps for T{n:06} counters and swaps PID-based run IDs for {prefix}_seed{seed}.
E2E_SEEDu64fixture default (e.g. 0)Seeds both RNGs and the deterministic run_id. Tests that thread a seed through their sub-systems should read this.
E2E_TIME_STEP_MSu6416Cadence of LabSession::now_ms() advances. Also drives deterministic tick spacing.

All three variables are off by default in cargo test. They are switched on automatically by the scripts/*.sh entry points that need them (see E2E scripts).

When to use each

Turn on determinism whenever the output is going to be diffed or stored as evidence. That means:

E2E_DETERMINISTIC=1 cargo test -p ftui-harness shadow_ratatui_e2e

Without this flag, run_id contains the process PID and the wall-clock seconds — both of which change every run.

The DeterminismFixture type

pub struct DeterminismFixture { seed: u64, deterministic: bool, time_step_ms: u64, run_id: String, ts_counter: AtomicU64, ms_counter: AtomicU64, start: Instant, }

Constructed with:

let fx = DeterminismFixture::new("doctor_happy", /* default_seed */ 0);

new reads E2E_DETERMINISTIC, E2E_SEED, and E2E_TIME_STEP_MS from the environment once and caches them. Subsequent reads on the same fixture are consistent.

Methods

MethodPurpose
seed()The effective seed (env override or default_seed).
deterministic()true when E2E_DETERMINISTIC is set.
run_id()Stable run identifier — {prefix}_seed{seed} in deterministic mode, otherwise {prefix}_{pid}_{unix_secs}.
timestamp()T{n:06} counter in deterministic mode, wall-clock seconds otherwise.
now_ms()Monotonic ms; advances by time_step_ms per call in deterministic mode.
env_snapshot()EnvSnapshot with TERM, COLORTERM, NO_COLOR, TMUX, ZELLIJ, seed, and deterministic flag.

Scenario counter

Lab::run_scenario increments a global counter every time it runs a scenario. Read it with:

use ftui_harness::determinism::lab_scenarios_run_total; let before = lab_scenarios_run_total(); // ... run scenarios ... let after = lab_scenarios_run_total(); assert!(after > before);

LAB_RECORDINGS_TOTAL and LAB_REPLAYS_TOTAL play similar roles for record/replay flows, exposed through lab_recordings_total() and lab_replays_total().

What determinism does not cover

Terminal capability detection is environmental. TERM, COLORTERM, NO_COLOR, TMUX, and ZELLIJ are read by ftui-core’s capability detector. If you want a stable capability profile, pin it with FTUI_TEST_PROFILE=modern (or similar) — see snapshot tests.

External RNGs. rand::thread_rng(), std::time::SystemTime::now(), uuid::Uuid::new_v4() — none of these consult the fixture. Route anything nondeterministic through a seeded ChaCha8Rng or LabSession::now_ms() before asserting on it.

Subprocess clocks. A child process started without env propagation reads its own wall clock. The doctor_frankentui capture layer threads these variables through explicitly; your harness must do the same if it spawns children.