From 4e62d6bb8517b5c23a61a6297ba724141f1edced Mon Sep 17 00:00:00 2001 From: ShortArrow Date: Wed, 14 Oct 2026 15:58:13 +0900 Subject: [PATCH 1/3] test(pty): pin that a return typed after the prompt reaches a slow shell as CR (refs #58) --- runex/tests/support/pty.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/runex/tests/support/pty.rs b/runex/tests/support/pty.rs index bc5c169..69fd955 100644 --- a/runex/tests/support/pty.rs +++ b/runex/tests/support/pty.rs @@ -591,6 +591,23 @@ mod startup_tests { .expect_err("echo prints a line and exits without a prompt"); assert_eq!(err.step, "startup", "{err:?}"); } + + #[test] + fn a_return_typed_after_the_prompt_reaches_a_slow_shell_as_cr() { + // While the PTY is canonical, ICRNL turns a typed CR into LF and + // PSReadLine does not run an LF-terminated line. This shell leaves + // canonical mode 800 ms after launch, then prints a prompt and + // reports the byte it reads. + let script = std::env::temp_dir().join(format!("runex-pty-slow-shell-{}.sh", std::process::id())); + std::fs::write(&script, "sleep 0.8; stty raw -echo; printf '$ '; head -c 1 | od -An -c\n") + .expect("the temp dir is writable"); + let mut session = PtySession::launch(&format!("sh {}", script.display())).expect("sh runs under a PTY"); + let settled = session.settle_startup(PtyShell::Bash); + let _ = std::fs::remove_file(&script); + settled.expect("the shell prints its prompt"); + session.enter().expect("the PTY accepts a key"); + assert!(session.read_until("\\r", Duration::from_secs(2)).is_ok(), "{:?}", session.transcript); + } } #[cfg(test)] From 6bb7c75dadaa2e85d4d86bccf1ab5e876890d916 Mon Sep 17 00:00:00 2001 From: ShortArrow Date: Wed, 14 Oct 2026 16:21:38 +0900 Subject: [PATCH 2/3] test(pty): make the slow stand-in shell print before its prompt, as pwsh does (refs #58) --- runex/tests/support/pty.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/runex/tests/support/pty.rs b/runex/tests/support/pty.rs index 69fd955..045462d 100644 --- a/runex/tests/support/pty.rs +++ b/runex/tests/support/pty.rs @@ -592,18 +592,23 @@ mod startup_tests { assert_eq!(err.step, "startup", "{err:?}"); } + /// While the PTY is canonical, ICRNL turns a typed CR into LF, and + /// PSReadLine does not run an LF-terminated line. Like pwsh, this + /// shell prints a terminal mode sequence first, leaves canonical mode + /// 800 ms after launch, and only then prints its prompt; it reports + /// the byte it reads. #[test] fn a_return_typed_after_the_prompt_reaches_a_slow_shell_as_cr() { - // While the PTY is canonical, ICRNL turns a typed CR into LF and - // PSReadLine does not run an LF-terminated line. This shell leaves - // canonical mode 800 ms after launch, then prints a prompt and - // reports the byte it reads. let script = std::env::temp_dir().join(format!("runex-pty-slow-shell-{}.sh", std::process::id())); - std::fs::write(&script, "sleep 0.8; stty raw -echo; printf '$ '; head -c 1 | od -An -c\n") - .expect("the temp dir is writable"); - let mut session = PtySession::launch(&format!("sh {}", script.display())).expect("sh runs under a PTY"); - let settled = session.settle_startup(PtyShell::Bash); + std::fs::write( + &script, + "printf '\\033[?1h'; sleep 0.8; stty raw -echo; printf '$ '; head -c 1 | od -An -c\n", + ) + .expect("the temp dir is writable"); + let launched = PtySession::launch(&format!("sh {}", script.display())); + let settled = launched.map(|mut session| (session.settle_startup(PtyShell::Bash), session)); let _ = std::fs::remove_file(&script); + let (settled, mut session) = settled.expect("sh runs under a PTY"); settled.expect("the shell prints its prompt"); session.enter().expect("the PTY accepts a key"); assert!(session.read_until("\\r", Duration::from_secs(2)).is_ok(), "{:?}", session.transcript); From e70e0f560d05db4169a4f3e153111f3dbe19f652 Mon Sep 17 00:00:00 2001 From: ShortArrow Date: Thu, 15 Oct 2026 09:50:48 +0900 Subject: [PATCH 3/3] test(pty): time the stand-in shell's mode switch from its first output (refs #58) --- runex/tests/support/pty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runex/tests/support/pty.rs b/runex/tests/support/pty.rs index 045462d..fc12902 100644 --- a/runex/tests/support/pty.rs +++ b/runex/tests/support/pty.rs @@ -595,7 +595,7 @@ mod startup_tests { /// While the PTY is canonical, ICRNL turns a typed CR into LF, and /// PSReadLine does not run an LF-terminated line. Like pwsh, this /// shell prints a terminal mode sequence first, leaves canonical mode - /// 800 ms after launch, and only then prints its prompt; it reports + /// 800 ms after that, and only then prints its prompt; it reports /// the byte it reads. #[test] fn a_return_typed_after_the_prompt_reaches_a_slow_shell_as_cr() {