diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 99b3b9d6ce4ad2656247d34f8da81047c6b5704c..7069110edd30e304f37c2b9263d69a4750f4dec9 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -3341,6 +3341,27 @@ mod tests { }); } + /// Polls the terminal content until `expected` appears, or panics after ~1s. + /// The PTY IO thread writes into the terminal grid independently of the + /// GPUI executor, so we need a real-time polling loop to synchronize. + async fn assert_content_eventually( + terminal: &Entity, + expected: &str, + cx: &mut TestAppContext, + ) { + let mut content = String::new(); + for _ in 0..100 { + content = terminal.update(cx, |term, _| term.get_content()); + if content.contains(expected) { + return; + } + cx.background_executor + .timer(Duration::from_millis(10)) + .await; + } + panic!("Expected terminal content to contain {expected:?}, got: {content}"); + } + /// Test that kill_active_task properly terminates both the foreground process /// and the shell, allowing wait_for_completed_task to complete and output to be captured. #[cfg(unix)] @@ -3353,10 +3374,7 @@ mod tests { let (terminal, completion_rx) = build_test_terminal(cx, "echo", &["test_output_before_kill; sleep 60"]).await; - // Wait a bit for the echo to execute and produce output - cx.background_executor - .timer(Duration::from_millis(200)) - .await; + assert_content_eventually(&terminal, "test_output_before_kill", cx).await; // Kill the active task terminal.update(cx, |term, _cx| { @@ -3400,6 +3418,8 @@ mod tests { .expect("Should receive exit status"); assert_eq!(exit_status, Some(ExitStatus::default())); + assert_content_eventually(&terminal, "done", cx).await; + // Now try to kill - should be a no-op since task already completed terminal.update(cx, |term, _cx| { term.kill_active_task();