use crate::app::window_state::WindowState;
use crate::config::Config;
use crate::pane::{LaunchCommand, SplitDirection};
use crate::profile::Profile;
use std::sync::Arc;
use std::time::{Duration, Instant};
fn runtime() -> Arc<tokio::runtime::Runtime> {
Arc::new(
tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()
.expect("runtime"),
)
}
fn marker_profile(marker: &str) -> Profile {
let mut profile = Profile::new("marker");
profile.command = Some("/bin/sh".to_string());
profile.command_args = Some(vec!["-c".to_string(), format!("echo {marker}; exec cat")]);
profile
}
fn window_with_profile_tab(config: &Config, profile: &Profile) -> WindowState {
let rt = runtime();
let mut ws = WindowState::new(config.clone(), Arc::clone(&rt));
let id = ws
.tab_manager
.new_tab_from_profile(config, rt, profile, Some((80, 24)))
.expect("profile tab");
ws.tab_manager.switch_to(id);
ws
}
fn pane_text(ws: &WindowState, pane: crate::pane::PaneId) -> String {
ws.tab_manager
.active_tab()
.and_then(|t| t.pane_manager())
.and_then(|pm| pm.get_pane(pane))
.and_then(|p| p.terminal.try_read().ok().map(|t| t.export_text()))
.unwrap_or_default()
}
fn wait_for(ws: &WindowState, pane: crate::pane::PaneId, needle: &str, times: usize) {
let deadline = Instant::now() + Duration::from_secs(10);
loop {
let text = pane_text(ws, pane);
if text.matches(needle).count() >= times {
return;
}
assert!(
Instant::now() < deadline,
"pane {pane} never showed {needle:?} x{times}: {text:?}"
);
std::thread::sleep(Duration::from_millis(50));
}
}
fn focused(ws: &WindowState) -> crate::pane::PaneId {
ws.tab_manager
.active_tab()
.and_then(|t| t.focused_pane_id())
.expect("focused pane")
}
#[test]
#[ignore = "requires PTY spawn"]
fn restart_pane_reruns_the_profile_program() {
let config = Config::default();
let mut ws = window_with_profile_tab(&config, &marker_profile("restart-marker"));
let pane = focused(&ws);
wait_for(&ws, pane, "restart-marker", 1);
assert_eq!(
ws.tab_manager
.active_tab()
.and_then(|t| t.pane_manager())
.and_then(|pm| pm.get_pane(pane))
.and_then(|p| p.launch.clone())
.map(|l| l.program),
Some("/bin/sh".to_string()),
"the first pane records the profile program"
);
if let Some(term) = ws
.tab_manager
.active_tab()
.and_then(|t| t.pane_manager())
.and_then(|pm| pm.get_pane(pane))
.and_then(|p| p.terminal.try_read().ok())
{
term.process_data(b"\x1b[2J\x1b[H");
}
assert!(!pane_text(&ws, pane).contains("restart-marker"));
ws.restart_focused_pane();
wait_for(&ws, pane, "restart-marker", 1);
}
#[test]
#[ignore = "requires PTY spawn"]
fn a_split_inherits_the_profile_program_unless_disabled() {
for inherit in [true, false] {
let mut config = Config::default();
config.panes.split_inherits_profile = inherit;
let mut ws = window_with_profile_tab(&config, &marker_profile("split-marker"));
let first = focused(&ws);
wait_for(&ws, first, "split-marker", 1);
let new_pane = ws
.split_pane_direction(SplitDirection::Vertical, true, None, 50)
.expect("split");
let launch = ws
.tab_manager
.active_tab()
.and_then(|t| t.pane_manager())
.and_then(|pm| pm.get_pane(new_pane))
.and_then(|p| p.launch.clone());
if inherit {
assert_eq!(launch.map(|l| l.program), Some("/bin/sh".to_string()));
wait_for(&ws, new_pane, "split-marker", 1);
} else {
assert_eq!(launch, None, "the configured shell, resolved at restart");
std::thread::sleep(Duration::from_millis(500));
assert!(!pane_text(&ws, new_pane).contains("split-marker"));
}
}
}
#[test]
#[ignore = "requires PTY spawn"]
fn an_explicit_split_command_wins_over_the_profile() {
let config = Config::default();
let mut ws = window_with_profile_tab(&config, &marker_profile("profile-marker"));
let new_pane = ws
.split_pane_direction(
SplitDirection::Horizontal,
true,
Some((
"/bin/sh".to_string(),
vec![
"-c".to_string(),
"echo explicit-marker; exec cat".to_string(),
],
)),
50,
)
.expect("split");
wait_for(&ws, new_pane, "explicit-marker", 1);
let launch = ws
.tab_manager
.active_tab()
.and_then(|t| t.pane_manager())
.and_then(|pm| pm.get_pane(new_pane))
.and_then(|p| p.launch.clone());
assert_eq!(
launch,
Some(LaunchCommand::new(
"/bin/sh".to_string(),
vec![
"-c".to_string(),
"echo explicit-marker; exec cat".to_string()
]
))
);
}