Files
hermes-agent/tests/scripts/test_run_tests_parallel_timeout_scaling.py
teknium1 d228013832 fix(ci): feed the timeout scaler only healthy durations, and wire the cache in CI
Greptile's two findings on the original PR were both right.

1. The scaler read test_durations.json from the checkout, but CI ran on
   a fresh runner where that file never exists (it is gitignored and the
   slicing-era artifact/merge job that produced it is gone). The feature
   was inert exactly where the false FLAKY kills happen. tests.yml now
   restores the most recent main-saved cache before the run (PRs read
   only) and saves it after a green push to main, mirroring the
   ci-timings-baseline restore/save pattern already in ci.yaml.

2. _save_durations persisted every file's total subprocess wall,
   including the ~cap of a timed-out attempt and the retry-summed wall
   of a FLAKY file. With the scaler that compounds: a hang cached at
   ~300s earns 900s next run, then ~900s cached earns 2700s, until the
   job timeout is the only bound. _clean_pass_durations drops failed and
   FLAKY files from the write so a file's cached duration is always a
   first-attempt-clean measurement; those files keep their previous
   known-good entry.

Tests trimmed to the salvage bar (<=2 invariants for the scaler plus one
for the cache filter) and moved next to the other runner tests under
tests/scripts/.
2026-09-15 03:47:55 -07:00

65 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Duration-aware per-file timeout scaling in scripts/run_tests_parallel.py.
The flat --file-timeout cap (default 300s) falsely SIGKILL'd
known-slow large-collection files under CI load, then the automatic
retry passed — manufacturing FLAKY reports for healthy files
(tests/test_hermes_state.py, 2026-08-18 on main). The scaler gives a
file max(flat_cap, 3 × last observed duration) and never lowers the cap.
Only first-attempt-clean durations feed the cache, so a hang can never
compound its own bound.
"""
from __future__ import annotations
import importlib.util
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
_RUNNER_PATH = REPO_ROOT / "scripts" / "run_tests_parallel.py"
def _load_runner():
spec = importlib.util.spec_from_file_location("run_tests_parallel", _RUNNER_PATH)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def test_scaler_never_lowers_the_flat_cap() -> None:
mod = _load_runner()
uncached = REPO_ROOT / "tests" / "test_example.py"
fast = REPO_ROOT / "tests" / "test_fast.py"
zero = REPO_ROOT / "tests" / "test_zero.py"
durations = {
mod._format_file(fast, REPO_ROOT): 4.2,
mod._format_file(zero, REPO_ROOT): 0.0,
}
assert mod._effective_file_timeout(uncached, REPO_ROOT, 300.0, None) == 300.0
assert mod._effective_file_timeout(uncached, REPO_ROOT, 300.0, durations) == 300.0
assert mod._effective_file_timeout(fast, REPO_ROOT, 300.0, durations) == 300.0
assert mod._effective_file_timeout(zero, REPO_ROOT, 300.0, durations) == 300.0
def test_slow_file_gets_proportional_headroom() -> None:
mod = _load_runner()
f = REPO_ROOT / "tests" / "test_hermes_state.py"
durations = {mod._format_file(f, REPO_ROOT): 205.0}
# 205s last run → 615s bound: a load-dilated healthy run survives,
# a genuine hang is still killed.
assert mod._effective_file_timeout(f, REPO_ROOT, 300.0, durations) == 615.0
def test_only_first_attempt_clean_durations_feed_the_cache() -> None:
"""A timed-out or retried file must not raise its own future bound."""
mod = _load_runner()
clean = REPO_ROOT / "tests" / "test_clean.py"
hung = REPO_ROOT / "tests" / "test_hung.py"
flaky = REPO_ROOT / "tests" / "test_flaky.py"
file_times = [(clean, 12.0), (hung, 300.4), (flaky, 250.0)]
failures = [(hung, "(300s exceeded; process tree SIGKILL'd)", {})]
flaky_results = [(flaky, "⚠ FLAKY: failed on attempt 1, passed on retry")]
kept = mod._clean_pass_durations(file_times, failures, flaky_results)
assert kept == [(clean, 12.0)]