From 4d2094ff90dc731339dab949b83f16dca8cb8569 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:48:25 +0800 Subject: [PATCH] test(daemon): poll for the child's exec before asserting its path Command::spawn returns after the fork, possibly before the child has exec'd sleep; until then /proc//exe still points at the test binary itself, so asserting the basename immediately is a race. This intermittently failed the Linux CI job on main (runs 29549626571, 29560431777, 29563049775). Poll process_path for up to 5s and carry the last-seen basename into the failure message. --- src/daemon/spawn.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/daemon/spawn.rs b/src/daemon/spawn.rs index d71fe63f..51aced8a 100644 --- a/src/daemon/spawn.rs +++ b/src/daemon/spawn.rs @@ -528,6 +528,10 @@ mod tests { /// match — this is what keeps a stale pidfile with a recycled pid from /// getting an innocent process killed. Driven with a real `sleep` child: /// alive, path readable, basename `sleep` ≠ the test binary's. + /// + /// `spawn` returns after the fork, possibly before the child has exec'd — + /// until then its executable path still reads as *this* test binary — so + /// the path assertions poll until the exec is visible. #[cfg(any(target_os = "macos", target_os = "linux"))] #[test] fn reap_guard_rejects_a_live_process_of_another_executable() { @@ -538,11 +542,19 @@ mod tests { let pid = child.id() as libc::pid_t; assert!(process_alive(pid), "the sleep child is alive and ours"); - assert_eq!( - process_path(pid).and_then(|p| p.file_name().map(|n| n.to_os_string())), - Some("sleep".into()), - "process_path resolves an arbitrary pid, not just our parent" - ); + let deadline = Instant::now() + Duration::from_secs(5); + loop { + let basename = process_path(pid).and_then(|p| p.file_name().map(|n| n.to_os_string())); + if basename == Some("sleep".into()) { + break; + } + assert!( + Instant::now() < deadline, + "process_path resolves an arbitrary pid, not just our parent \ + (still {basename:?} after 5s)" + ); + std::thread::sleep(Duration::from_millis(10)); + } assert!( !process_matches_own_exe(pid), "sleep must not match the test binary; matching here would mean the reap could kill it"