Skip to content

Development and Testing

Prakhar Yadav edited this page Sep 16, 2026 · 1 revision

Development and Testing

Ordinary development and CI never write to the real /sys. Everything runs against fixture trees via --sysfs-root plus a scriptable MockSmc; real hardware is a final, explicit, supervised gate. Cargo.toml deps are exactly the allowlist (serde, toml, serde_json, thiserror, tracing, tracing-subscriber, libc); unsafe lives only in safety.rs (grep-gated); unwrap/expect/panic are clippy-denied outside tests, main.rs wiring, and safety.rs's sanctioned arm_test_panic.

Quality gates (check.sh — every task, before declaring done)

cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test --features hw   # must SKIP cleanly (no applesmc here) — proves the guard works

Plus an expanded gate 2b denying clippy::unwrap_used / expect_used / panic explicitly (the §8 lints sit at warn in Cargo.toml [lints.clippy]).

Test inventory

Suite Tests What each proves
tests/policy_traces.rs (7) plateau 80 °C × 50 → f(80); hot-core {92,60,88} acts on 92; descending approach, no ratchet; hysteresis no-flap; sensor-loss streak → ReturnToAuto; overshoot guard; hold-with-hot-core escalates The debate's traces: H1/H2 dead, guards live
tests/integration.rs (13) once decision + AUTO restore (incl. from pre-existing Manual); doctor honors --sysfs-root/--config; hold→cmd→daemon applies within one poll; selftest-panic → fixture fan1_manual == 0; L1 re-assert on induced drift; hw guard skips; F14 reconcile (observe + curve); F18 monitor_only in JSON + human; F24 mode-change journal End-to-end binary behavior on fixtures
tests/schema.rs (4) status --json vs afanctl.status.v1; hold/observe/curve cmd files vs afanctl.cmd.v1; once state file vs afanctl.state.v1 Versioned schemas hold
tests/pin_constants.rs (7) echo tolerance boundary (49 passes / 51 fails); stall fires at exactly STALL_POLLS; F19/F20/F21 constants pinned; interval_s 12 legal / 14 rejected; sub/super-epsilon jitter behavior Frozen constants cannot drift silently
tests/settle_window.rs (2) 300 ms echo latency accepted, curve converges failure-free; never-adopted echo → VerifyFailed → fallback restores AUTO Settle-window verify on both backends
co-located #[cfg(test)] units per-module (config rejections with key+fix, smc round-trip/verify/drift/mode-flip/outliers, supervisor transitions, doctor checks, notify socket) Every testable PRD claim has a test

Integration tests spawn the real binary against a tempdir copy of tests/fixtures/sysfs/ via --sysfs-root, with AFANCTL_RUNTIME_DIR pointed at a tempdir; the repo fixture is never mutated.

Fixture trees (tests/fixtures/sysfs/)

Healthy baseline devices/ mirrors the live machine exactly: applesmc.768 (fan1_label Exhaust, fan1_min 1200, fan1_max 7200, fan1_manual 0, fan1_input/fan1_output 1200, empty fan1_safe, attribute-less husk hwmon/hwmon3) + coretemp.0/hwmon/hwmon4 (Package id 0 45 °C, Core 0 44 °C, Core 1 43 °C, all crit 100 °C; milli-°C on disk). Fault variants (each a full independent tree): outlier_hi (> 120 °C), outlier_neg (< 0 °C), empty_temp, garbage_temp, garbage_fan, missing_fan, no_coretemp, layout_changed (fan files moved into hwmon3, fan1_input 3400 — exercises the layout hook).

MockSmc fault face

The full fault-injection surface the defect map demands: drift, write-not-taking (set_write_stuck), sensor outliers, mode flips, tach lag (set_tach_lag), tach frozen (set_tach_frozen), echo latency (set_echo_latency). The hardware gate's three fixture-uncatchable defects (no reconcile; tach-vs-echo confusion; ~1 s SMC tick) are now modeled here.

Hardware-in-the-loop guard (hw feature)

The only path that may touch real /sys, and only with all three: --features hw and AFANCTL_HWTEST=1 and real applesmc present — otherwise skipped. Never in CI. The supervised hardware gate (§9.3) is the only real-sysfs session; the orchestrator never ran it.

Performance / complexity budgets

R11: poll loop < 0.1 % CPU at 1 Hz (sleep with timer slack, no busy waits), RSS < 5 MB, binary < 2 MB release (strip, lto). Measured: 0.05 % CPU / 2.4 MB peak over a 1 h 10 min observe soak. Shipped product is ~4.2k non-test LOC against the early ~1.4k sketch — the delta is mandated surface (fault face, doctor checks + --compare, key+reason+fix errors), ledgered per file in QUESTIONS.md/DEVIATIONS.md; the "LOC within ±20 %" criterion is therefore not met as written — re-baseline the sketch, don't carve up gate-tested code.

Clone this wiki locally