diff --git a/.repository-projection.json b/.repository-projection.json index 635e8962a..7ef92da22 100644 --- a/.repository-projection.json +++ b/.repository-projection.json @@ -3,11 +3,11 @@ "projection": "deixic-code", "projectionSchemaVersion": 1, "sourceRepository": "dx-corp/mono", - "sourceSha": "a68479e9a31343578da4b1472d2d5c0c653eddbf", + "sourceSha": "8b32b94c6bf3d6850bea833b991b2fc6ee26ffdc", "destinationRepository": "dx-corp/code", - "priorProjectedBase": "4a9f1f46f5c068650ff290b79cb96bb41b4995e2", + "priorProjectedBase": "45779ed2d5271af92aacacae152b859a886531e4", "definitionDigest": "cb9d429542ebb0a2de9b42a7aad60d9d8696a648ceba47c30f05c0b285ca0db7", "toolDigest": "f8cb071b0f27267120ccf45a00d0982f45113bd23535bef6a1555b4933f99f13", - "contentDigest": "ca0836c2c73c392cc9190a55e0e160e0fb9b5e409129dafdcd0711ed63b4c69e", + "contentDigest": "1ff896cc41aec4814fb80a4aa347bdc43672ac61d76472ec53da8c5a1263b14e", "publicationEligible": true } diff --git a/packages/tui-rs/src/entrypoint/shutdown_signal.rs b/packages/tui-rs/src/entrypoint/shutdown_signal.rs index ba3820a83..9cd442744 100644 --- a/packages/tui-rs/src/entrypoint/shutdown_signal.rs +++ b/packages/tui-rs/src/entrypoint/shutdown_signal.rs @@ -905,11 +905,11 @@ mod tests { /// with buffered, unflushed entries, and a real child process is /// registered in the *actual* global `process_registry`. A real /// `SIGTERM` is self-sent; this asserts the buffered entry lands on - /// disk (via the cancellation drop cascade) and the child is reaped + /// disk (via the cancellation drop cascade) and its Child owner reaps it /// -- the two failure modes this regression protects. /// /// The registry is process-global and tests run in parallel, so the - /// reaping step uses `process_registry::cleanup_one(child_pid)` + /// termination step uses `process_registry::cleanup_one(child_pid)` /// (the per-PID variant of the production `cleanup_all` path) rather /// than draining the whole registry: a full drain could kill another /// concurrently-running test's registered child and make that test's @@ -954,11 +954,12 @@ mod tests { // A real background child, registered exactly the way the bash // tool's `run_in_background` does. - let mut child = std::process::Command::new("sleep") + let mut child = tokio::process::Command::new("sleep") .arg("300") + .kill_on_drop(true) .spawn() .expect("spawn background child"); - let child_pid = child.id(); + let child_pid = child.id().expect("PID of newly spawned background child"); crate::tools::process_registry::register(child_pid); /// Stands in for `App::run(self)`: owns the manager and suspends @@ -976,7 +977,8 @@ mod tests { assert_eq!(result, 0, "self-kill(SIGTERM) failed"); }); - let reaped = tokio::time::timeout(std::time::Duration::from_secs(5), async { + let shutdown_deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(5); + let terminated = tokio::time::timeout_at(shutdown_deadline, async { tokio::select! { biased; signal = monitor.recv() => { @@ -990,17 +992,22 @@ mod tests { .expect("shutdown race did not resolve within 5s of a real SIGTERM"); assert!( - reaped, + terminated, "expected the registered child to be cleaned up by cleanup_one" ); - // The child process tree must actually be gone, not just - // unregistered. - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - let wait_result = child.try_wait().expect("try_wait"); - assert!( - wait_result.is_some(), - "background child {child_pid} was not reaped by cleanup_background_processes" + // The registry requests termination; the Child owner must await and + // reap it, as the production Bash watcher does. A fixed sleep followed + // by try_wait races process exit under concurrent workspace load. + let status = tokio::time::timeout_at(shutdown_deadline, child.wait()) + .await + .expect("background child was not reaped within the shutdown deadline") + .expect("wait for background child"); + use std::os::unix::process::ExitStatusExt; + assert_eq!( + status.signal(), + Some(libc::SIGKILL), + "cleanup_one must terminate the registered background child" ); // The buffered entry must be on disk: this only happens because