Files
claude-chill/crates/pty-mock/src/bin.rs
adam 3256167f48 test: add PTY integration test harness
Add a pty-mock workspace crate (echo/alt-screen/paste-echo/buffer-fill/
sync-blocks subcommands) plus an in-process integration test suite that
drives Proxy directly via a new_for_test constructor and #[cfg(test)]
state accessors. Sets up the regression net needed before swapping the
underlying VT emulator from vt100 to termwiz.

Pulled from upstream draft PR #35; the alt-screen feed-order semantic
change is intentionally deferred (its dedicated test is left #[ignore]).
2026-05-01 17:11:08 +00:00

147 lines
3.4 KiB
Rust

use clap::{Parser, Subcommand};
use std::io::{self, BufRead, Read, Write};
#[derive(Parser)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Echo,
AltScreen,
PasteEcho,
BufferFill,
SyncBlocks,
}
fn cmd_echo() {
let stdin = io::stdin();
let mut stdout = io::stdout();
for line in stdin.lock().lines() {
let line = match line {
Ok(l) => l,
Err(_) => break,
};
if writeln!(stdout, "{}", line).is_err() {
break;
}
if stdout.flush().is_err() {
break;
}
}
}
fn cmd_alt_screen() {
let mut stdout = io::stdout();
let mut stdin = io::stdin();
// Enter alt screen
let _ = stdout.write_all(b"\x1b[?1049h");
let _ = stdout.flush();
// Write some content while in alt screen
let _ = stdout.write_all(b"alt screen content\r\n");
let _ = stdout.flush();
// Wait for any byte on stdin as trigger to exit
let mut trigger = [0u8; 1];
let _ = stdin.read_exact(&mut trigger);
// Exit alt screen
let _ = stdout.write_all(b"\x1b[?1049l");
let _ = stdout.flush();
// Write content after alt screen exit
let _ = stdout.write_all(b"back to normal\r\n");
let _ = stdout.flush();
}
fn cmd_paste_echo() {
let mut stdout = io::stdout();
let stdin = io::stdin();
// Enable bracketed paste mode
let _ = stdout.write_all(b"\x1b[?2004h");
let _ = stdout.flush();
// Read lines, prefix paste content
for line in stdin.lock().lines() {
let line = match line {
Ok(l) => l,
Err(_) => break,
};
if writeln!(stdout, "pasted: {}", line).is_err() {
break;
}
if stdout.flush().is_err() {
break;
}
}
}
fn cmd_buffer_fill() {
let mut stdout = io::stdout();
let stdin = io::stdin();
let mut stdin_lock = stdin.lock();
// Write large output to fill the PTY buffer
let chunk = "X".repeat(1024) + "\r\n";
for _ in 0..256 {
if stdout.write_all(chunk.as_bytes()).is_err() {
break;
}
}
let _ = stdout.flush();
// Now read stdin and echo it back
let mut buf = [0u8; 4096];
loop {
match stdin_lock.read(&mut buf) {
Ok(0) => break,
Ok(n) => {
if stdout.write_all(&buf[..n]).is_err() {
break;
}
if stdout.flush().is_err() {
break;
}
}
Err(_) => break,
}
}
}
fn cmd_sync_blocks() {
let stdin = io::stdin();
let mut stdout = io::stdout();
for line in stdin.lock().lines() {
let line = match line {
Ok(l) => l,
Err(_) => break,
};
// Wrap response in sync update block
let _ = stdout.write_all(b"\x1b[?2026h");
if writeln!(stdout, "sync: {}", line).is_err() {
break;
}
let _ = stdout.write_all(b"\x1b[?2026l");
if stdout.flush().is_err() {
break;
}
}
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Echo => cmd_echo(),
Commands::AltScreen => cmd_alt_screen(),
Commands::PasteEcho => cmd_paste_echo(),
Commands::BufferFill => cmd_buffer_fill(),
Commands::SyncBlocks => cmd_sync_blocks(),
}
}