diff --git a/docs/superpowers/evidence/perf-probes-loadavg-fixture-2026-09-14.json b/docs/superpowers/evidence/perf-probes-loadavg-fixture-2026-09-14.json new file mode 100644 index 00000000..f8edd39b --- /dev/null +++ b/docs/superpowers/evidence/perf-probes-loadavg-fixture-2026-09-14.json @@ -0,0 +1,29 @@ +{ + "intent": "Make the arch_perf_probes lib tests independent of the host's real load average so the pre-push ci-mirror gate is not red whenever a game is running.", + "scope": [ + "linux_configuration/fixes/lib/arch_perf_probes.sh (LOADAVG_FILE override, default /proc/loadavg)", + "linux_configuration/fixes/lib/tests/arch_perf_harness.sh (fixture written by arch_reset)", + "linux_configuration/fixes/lib/tests/test_arch_perf_probes.sh (deterministic load cases)" + ], + "changes": [ + "collect_basics reads the 1-minute load from ${LOADAVG_FILE:-/proc/loadavg}; behaviour on the host is unchanged", + "The harness exports LOADAVG_FILE into TEST_TMPDIR and arch_reset writes a quiet fixture (0.10)", + "The load test now asserts the (16/16) finding from a 16.50 fixture instead of SKIPping when the host is idle; a missing file case asserts no finding" + ], + "verification": [ + { + "command": "./linux_configuration/fixes/lib/tests/test_arch_perf_probes.sh", + "result": "pass", + "evidence": "arch_perf_probes (basics/governor): 17 passed, 0 failed (was 16 with one SKIP, and 1 failed at host load 22/16)" + }, + { + "command": "./linux_configuration/fixes/lib/tests/run_all.sh (host load ~19/16, BeamNG running)", + "result": "pass", + "evidence": "All fixes lib test files passed." + } + ], + "risks": ["None on the host: the default path is the real /proc/loadavg"], + "rollback": [ + "git revert the commit; the tests return to reading the real load and go red under host load" + ] +} diff --git a/linux_configuration/fixes/lib/arch_perf_probes.sh b/linux_configuration/fixes/lib/arch_perf_probes.sh index 1dd84001..302901ce 100755 --- a/linux_configuration/fixes/lib/arch_perf_probes.sh +++ b/linux_configuration/fixes/lib/arch_perf_probes.sh @@ -19,7 +19,9 @@ collect_basics() { local cpu_count cpu_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1) local load1 - load1=$(awk '{print int($1)}' /proc/loadavg 2>/dev/null || echo 0) + # Overridable so the tests do not depend on the host being idle: the + # "quiet machine" case went red whenever a game was running on the host. + load1=$(awk '{print int($1)}' "${LOADAVG_FILE:-/proc/loadavg}" 2>/dev/null || echo 0) if [[ ${load1:-0} -ge ${cpu_count:-1} ]]; then add_finding "1-minute load average is at/above CPU thread count (${load1}/${cpu_count}); background tasks may be saturating the system." fi diff --git a/linux_configuration/fixes/lib/tests/arch_perf_harness.sh b/linux_configuration/fixes/lib/tests/arch_perf_harness.sh index ca9e7e8a..1d31fab9 100755 --- a/linux_configuration/fixes/lib/tests/arch_perf_harness.sh +++ b/linux_configuration/fixes/lib/tests/arch_perf_harness.sh @@ -37,6 +37,9 @@ export REPORT_FILE="${TEST_TMPDIR}/report.log" export JOURNALD_CONF_DIR="${TEST_TMPDIR}/journald.conf.d" export SYSTEMD_UNIT_DIR="${TEST_TMPDIR}/systemd_units" export ORGANIZE_SCRIPT_CANDIDATES="${TEST_TMPDIR}/organize_downloads.sh" +# collect_basics reads the 1-minute load from here; a fixture, not the real +# /proc/loadavg, or the "quiet machine" case depends on nothing else running. +export LOADAVG_FILE="${TEST_TMPDIR}/loadavg" export APPLY_SAFE_FIXES="false" export INSTALL_TOOLS="false" declare -a FINDINGS=() @@ -85,6 +88,7 @@ arch_reset() { rm -rf "${JOURNALD_CONF_DIR}" "${SYSTEMD_UNIT_DIR}" mkdir -p "${JOURNALD_CONF_DIR}" "${SYSTEMD_UNIT_DIR}" rm -f "${ORGANIZE_SCRIPT_CANDIDATES}" + echo "0.10 0.20 0.30 1/100 1" >"${LOADAVG_FILE}" _t_full_path _arch_default_stubs } diff --git a/linux_configuration/fixes/lib/tests/test_arch_perf_probes.sh b/linux_configuration/fixes/lib/tests/test_arch_perf_probes.sh index c9968332..842c0d03 100755 --- a/linux_configuration/fixes/lib/tests/test_arch_perf_probes.sh +++ b/linux_configuration/fixes/lib/tests/test_arch_perf_probes.sh @@ -67,20 +67,25 @@ _t_contains "$findings" "ACPI errors detected" \ _t_contains "$findings" "Xorg is using high CPU (45%)" \ "collect_basics: flags a CPU-hungry Xorg" -# A load average at or above the thread count is flagged. /proc/loadavg is -# read directly by the lib, so the case is driven by a low CPU count instead. +# A load average at or above the thread count is flagged. arch_reset -_t_stub getconf 'echo 1' +_t_stub getconf 'echo 16' _t_stub systemctl 'exit 0' _t_stub journalctl 'exit 0' _t_stub ps 'echo none' +echo "16.50 12.00 9.00 3/900 1" >"${LOADAVG_FILE}" collect_basics -if [[ $(awk '{print int($1)}' /proc/loadavg) -ge 1 ]]; then - _t_contains "$(_t_findings)" "load average is at/above CPU thread count" \ - "collect_basics: flags a load average at or above the thread count" -else - _t_pass "collect_basics: SKIP load case (this host's 1-minute load is below 1)" -fi +_t_contains "$(_t_findings)" "load average is at/above CPU thread count (16/16)" \ + "collect_basics: flags a load average at or above the thread count" + +# An unreadable load file degrades to 0 rather than aborting the probe. +arch_reset +_t_stub systemctl 'exit 0' +_t_stub journalctl 'exit 0' +_t_stub ps 'echo none' +rm -f "${LOADAVG_FILE}" +collect_basics +_t_eq "" "$(_t_findings)" "collect_basics: missing load file is not a finding" # --- check_cpu_governor -----------------------------------------------------