fangorn/huorn-minecraft
public
ref:main
//! Integration tests for terminal lifecycle through the backend system.
//!
//! These create real TerminalState instances with PTY processes,
//! verify the full pipeline (backend → session → VTE → Term → renderer),
//! and confirm clean destruction.
use huorn_minecraft::terminal::TerminalState;
#[test]
fn terminal_create_render_destroy() {
let mut term = TerminalState::new(80, 24, 14.0, "/bin/bash", "/tmp", "plain")
.expect("failed to create terminal");
assert!(term.is_alive());
// Render initial state
term.render();
let pixels = term.pixel_buffer();
assert!(!pixels.is_empty());
assert!(pixels.len() > 100, "pixel buffer suspiciously small");
// Destroy
drop(term);
// No panic = success
}
#[test]
fn terminal_send_key_and_poll() {
let mut term = TerminalState::new(80, 24, 14.0, "/bin/bash", "/tmp", "plain")
.expect("failed to create terminal");
// Send Enter key (GLFW keycode 257)
term.send_key(257, 0);
// Poll should not crash and terminal should stay alive
for _ in 0..5 {
term.poll_pty();
std::thread::sleep(std::time::Duration::from_millis(50));
}
assert!(term.is_alive());
}
#[test]
fn terminal_resize_lifecycle() {
let mut term = TerminalState::new(80, 24, 14.0, "/bin/bash", "/tmp", "plain")
.expect("failed to create terminal");
let dims_before = term.dimensions();
// Resize to larger
term.resize(120, 40);
let dims_after = term.dimensions();
assert!(
dims_after[0] > dims_before[0],
"width should increase: {} -> {}",
dims_before[0],
dims_after[0]
);
assert!(
dims_after[1] > dims_before[1],
"height should increase: {} -> {}",
dims_before[1],
dims_after[1]
);
// Terminal should still be alive after resize
assert!(term.is_alive());
term.poll_pty();
assert!(term.is_alive());
}
#[test]
fn terminal_scroll() {
let mut term = TerminalState::new(80, 24, 14.0, "/bin/bash", "/tmp", "plain")
.expect("failed to create terminal");
// Generate enough output to have scrollback
term.send_text("for i in $(seq 1 100); do echo \"line $i\"; done\n");
for _ in 0..20 {
term.poll_pty();
std::thread::sleep(std::time::Duration::from_millis(50));
}
// Scroll up and down should not crash
term.scroll(5);
term.scroll(-5);
assert!(term.is_alive());
}
#[test]
fn terminal_content_after_command() {
let mut term = TerminalState::new(80, 24, 14.0, "/bin/bash", "/tmp", "plain")
.expect("failed to create terminal");
term.send_text("echo INTEGRATION_MARKER_12345\n");
// Poll until output arrives
for _ in 0..20 {
term.poll_pty();
std::thread::sleep(std::time::Duration::from_millis(50));
}
let content = term.get_content();
assert!(
content.contains("INTEGRATION_MARKER_12345"),
"terminal content should contain our marker, got: {}",
&content[..content.len().min(500)]
);
}
#[test]
fn terminal_pixel_buffer_changes_after_output() {
let mut term = TerminalState::new(80, 24, 14.0, "/bin/bash", "/tmp", "plain")
.expect("failed to create terminal");
// Initial render
term.render();
let initial_pixels: Vec<u8> = term.pixel_buffer().to_vec();
// Write something visible
term.send_text("echo AAAAAAAAAAAAAAAAAAAAAA\n");
for _ in 0..10 {
term.poll_pty();
std::thread::sleep(std::time::Duration::from_millis(50));
}
// Re-render
term.render();
let after_pixels: Vec<u8> = term.pixel_buffer().to_vec();
// Pixel buffer should have changed (text was rendered)
assert_ne!(
initial_pixels, after_pixels,
"pixel buffer should change after terminal output"
);
}
#[test]
fn terminal_rapid_create_destroy_no_leak() {
// Create and destroy 10 terminals rapidly — no leaks, no panics
for i in 0..10 {
let term = TerminalState::new(40, 12, 14.0, "/bin/bash", "/tmp", "plain")
.unwrap_or_else(|e| panic!("terminal {} failed: {}", i, e));
assert!(term.is_alive());
drop(term);
}
}