test(TASK-050): wait on every expected marker before asserting hook counts

Hook processes are fire-and-forget: the binary's exit drain acknowledges
spawns only, so the STARTED writer can land after the terminal one. The
macro-bracketing tests waited only for the terminal marker and then
asserted STARTED's count immediately, flaking on Windows CI and loaded
Linux runners. The bounded wait now covers every expected marker before
the exact-count and absence assertions run; the probe script's scenario K
comment is corrected to state the spawn-ack-only contract it already
waits around.
This commit is contained in:
Dark-Alex-17 committed 2026-09-17 00:33:37 -06:00
1 parent ec66494e8f
commit 611900057c
2 files changed
+18 -11

No files matched your search

+15 -9
View File
@@ -130,21 +130,27 @@ fn run_coyote(dir: &Path, args: &[&str]) -> Output {
.unwrap()
}
/// Bounded poll for the expected terminal marker. The binary drains pending
/// hooks before exiting, so the log is normally complete once `output()`
/// returns; the poll only absorbs filesystem latency, never paces the run.
/// Marker matching stays byte-exact on both platforms because `lines()`
/// strips the CRLF that cmd's echo emits on Windows.
fn wait_for_marker(log: &Path, needle: &str) -> String {
/// Bounded poll until every expected marker is present. The binary's exit
/// drain only acknowledges that each hook process SPAWNED — the hooks
/// themselves keep running concurrently after `output()` returns, and
/// nothing orders their writes (the STARTED writer can land after the
/// terminal one). Waiting on all expected markers, not just the terminal
/// one, keeps the exact-count assertions below race-free. Marker matching
/// stays byte-exact on both platforms because `lines()` strips the CRLF
/// that cmd's echo emits on Windows.
fn wait_for_markers(log: &Path, needles: &[&str]) -> String {
let deadline = Instant::now() + Duration::from_secs(10);
loop {
let contents = fs::read_to_string(log).unwrap_or_default();
if contents.lines().any(|line| line == needle) {
if needles
.iter()
.all(|needle| contents.lines().any(|line| line == *needle))
{
return contents;
}
assert!(
Instant::now() < deadline,
"timed out waiting for '{needle}'; log so far: {contents:?}"
"timed out waiting for {needles:?}; log so far: {contents:?}"
);
thread::sleep(Duration::from_millis(50));
}
@@ -187,7 +193,7 @@ fn probe_macro_run(label: &str, macro_yaml: &str, args: &[&str], expect_success:
} else {
"FAILED"
};
let contents = wait_for_marker(&log, terminal);
let contents = wait_for_markers(&log, &["STARTED", terminal]);
assert_started_and_single_terminal(&contents, terminal);
contents
}