mod common;
#[cfg(unix)]
use common::Terminal;
#[test]
#[cfg(unix)]
fn test_pty_interrupt_computation() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send_line("while(TRUE) {}")
.expect("Should start loop");
std::thread::sleep(std::time::Duration::from_millis(500));
terminal.send_interrupt().expect("Should send interrupt");
terminal
.wait_for_prompt()
.expect("Should show prompt after interrupt");
terminal
.send_line("42")
.expect("Should send simple expression");
terminal
.expect("[1] 42")
.expect("Should execute normally after interrupt");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_cursor_position() {
let mut terminal = Terminal::spawn().expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let (initial_row, initial_col) = terminal
.cursor_position()
.expect("Should get cursor position");
assert!(initial_col > 0, "Cursor should be after prompt");
terminal
.current_line()
.assert_contains("> ")
.expect("Current line should show prompt");
terminal.send_line("").expect("Should send empty line");
std::thread::sleep(std::time::Duration::from_millis(200));
let (after_enter_row, after_enter_col) = terminal
.cursor_position()
.expect("Should get cursor position after enter");
assert!(
after_enter_row > initial_row,
"Cursor row should increase after Enter"
);
assert_eq!(
after_enter_col, initial_col,
"Cursor should still be at prompt position after Enter"
);
terminal
.current_line()
.assert_contains("> ")
.expect("Should show prompt after empty input");
terminal.send("a").expect("Should send 'a'");
std::thread::sleep(std::time::Duration::from_millis(300));
let (_, col_with_a) = terminal
.cursor_position()
.expect("Should get cursor position with 'a'");
assert_eq!(
col_with_a,
initial_col + 1,
"Cursor should be one position after prompt after typing 'a'"
);
terminal.send_interrupt().expect("Should send interrupt");
std::thread::sleep(std::time::Duration::from_millis(300));
terminal
.wait_for_prompt()
.expect("Should show prompt after interrupt");
let (_after_intr_row, after_intr_col) = terminal
.cursor_position()
.expect("Should get cursor position after interrupt");
assert_eq!(
after_intr_col, initial_col,
"Cursor should be at prompt position after interrupt"
);
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_screen_state_inspection() {
let mut terminal = Terminal::spawn().expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let screen = terminal.screen().expect("Should get screen snapshot");
assert!(!screen.lines.is_empty(), "Screen should have lines");
let initial_col = screen.cursor_col;
assert!(
initial_col > 0,
"Initial cursor column should be after prompt"
);
let prompt_row = screen.cursor_row as usize;
terminal
.line(prompt_row)
.assert_contains("> ")
.expect("Prompt line should start with prompt indicator");
terminal.send_line("100").expect("Should send 100");
terminal
.clear_and_expect("[1] 100")
.expect("Should show result");
terminal
.assert_cursor(screen.cursor_row + 2, initial_col)
.expect("Cursor should be 2 rows down, at prompt position");
terminal.clear_buffer().expect("Should clear buffer");
terminal.send_line("200").expect("Should send 200");
terminal.expect("[1] 200").expect("Should show result 200");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)] fn test_pty_bracketed_paste() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let paste_start = "\x1b[200~";
let paste_end = "\x1b[201~";
let content = "x <- '".to_string() + &"a".repeat(10) + "'";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal.send(&pasted).expect("Should send bracketed paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after paste");
terminal
.send_line("nchar(x)")
.expect("Should send nchar(x)");
terminal
.clear_and_expect("[1] 10")
.expect("nchar(x) should return 10");
let content = "y <- '".to_string() + &"b".repeat(100) + "'";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send medium bracketed paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after medium paste");
terminal
.send_line("nchar(y)")
.expect("Should send nchar(y)");
terminal
.clear_and_expect("[1] 100")
.expect("nchar(y) should return 100");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_bracketed_paste_long_string() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let paste_start = "\x1b[200~";
let paste_end = "\x1b[201~";
let content = "x <- '".to_string() + &"a".repeat(5000) + "'";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send long bracketed paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after long paste");
terminal
.send_line("nchar(x)")
.expect("Should send nchar(x)");
terminal
.clear_and_expect("[1] 5000")
.expect("nchar(x) should return 5000");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_bracketed_paste_multiline_long_string() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let paste_start = "\x1b[200~";
let paste_end = "\x1b[201~";
let content = "x <- '".to_string() + &"a".repeat(2000) + "\n" + &"b".repeat(2000) + "'";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send multiline long paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after multiline paste");
terminal
.send_line("nchar(x)")
.expect("Should send nchar(x)");
terminal
.clear_and_expect("[1] 4001")
.expect("nchar(x) should return 4001");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_bracketed_paste_long_multibyte_string() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let paste_start = "\x1b[200~";
let paste_end = "\x1b[201~";
let s = "ä¸".repeat(1000)
+ "\n"
+ &"æ–‡".repeat(1000)
+ "\n"
+ &"ä¸".repeat(1000)
+ "\n"
+ &"æ–‡".repeat(1000);
let content = "x <- '".to_string() + &s + "'";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send multibyte long paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after multibyte paste");
terminal
.send_line("nchar(x)")
.expect("Should send nchar(x)");
terminal
.clear_and_expect("[1] 4003")
.expect("nchar(x) should return 4003");
let content = "xy <- '".to_string() + &s + "'";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send multibyte paste with different padding");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after second paste");
terminal
.send_line("nchar(xy)")
.expect("Should send nchar(xy)");
terminal
.clear_and_expect("[1] 4003")
.expect("nchar(xy) should return 4003");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_multiple_expressions_print_intermediate() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let paste_start = "\x1b[200~";
let paste_end = "\x1b[201~";
let content = "1 + 1\n2 + 2";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send multiple expressions");
std::thread::sleep(std::time::Duration::from_millis(500));
let screen = terminal.screen().expect("Should get screen");
let screen_text = screen.lines.join("\n");
assert!(
screen_text.contains("[1] 2"),
"First expression result should be visible. Screen:\n{}",
screen_text
);
assert!(
screen_text.contains("[1] 4"),
"Second expression result should be visible. Screen:\n{}",
screen_text
);
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_bracketed_paste_with_auto_match() {
let mut terminal = Terminal::spawn().expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
let paste_start = "\x1b[200~";
let paste_end = "\x1b[201~";
let content = "x <- (1)";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal.send(&pasted).expect("Should send bracketed paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after paste");
terminal.send_line("x").expect("Should send x");
terminal
.clear_and_expect("[1] 1")
.expect("x should be 1, not error from extra bracket");
let content = "y <- sum(c(1, 2, 3))";
let pasted = format!("{}{}{}\n", paste_start, content, paste_end);
terminal
.send(&pasted)
.expect("Should send nested brackets paste");
terminal
.clear_and_expect("> ")
.expect("Should show prompt after nested paste");
terminal.send_line("y").expect("Should send y");
terminal.clear_and_expect("[1] 6").expect("y should be 6");
terminal.quit().expect("Should quit cleanly");
}
#[test]
#[cfg(unix)]
fn test_pty_escape_cancels_input() {
let mut terminal =
Terminal::spawn_with_args(&["--no-auto-match"]).expect("Failed to spawn arf");
terminal.wait_for_prompt().expect("Should show prompt");
terminal
.send("invalid_var")
.expect("Should send partial input");
std::thread::sleep(std::time::Duration::from_millis(100));
terminal.send_interrupt().expect("Should send interrupt");
std::thread::sleep(std::time::Duration::from_millis(200));
terminal
.wait_for_prompt()
.expect("Should show prompt after cancel");
terminal.send_line("42").expect("Should send 42");
terminal
.clear_and_expect("[1] 42")
.expect("Should execute 42");
terminal
.send_line("invalid_var")
.expect("Should send invalid_var");
terminal
.expect("Error")
.expect("Should show error for undefined variable");
terminal.quit().expect("Should quit cleanly");
}