ci / rust (push) Successful in 2m59s
CI e2e tests failed with "tmux exited with code 1: server exited unexpectedly" because the startup `start-server \; set-option -g default-terminal` call left an empty server that tmux's default `exit-empty on` killed before the first `new-session` could attach. Move the `set-option -g default-terminal tmux-256color` into the same tmux command as `new-session` (chained with `;`) so a single tmux invocation creates the server, sets the option, and spawns the pane. The pane reads `default-terminal` at creation time, so chaining before `new-session` still applies to the very first session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
168 lines
4.7 KiB
Rust
168 lines
4.7 KiB
Rust
//! Integration tests for the tmux wrapper against a real `tmux` binary.
|
|
//!
|
|
//! Each test spins up an isolated tmux server (unique socket name + temp
|
|
//! `TMUX_TMPDIR`) so they can run in parallel and never touch the user's
|
|
//! actual tmux session.
|
|
|
|
mod common;
|
|
|
|
use std::process::Command;
|
|
use std::time::Duration;
|
|
|
|
use common::{wait_for, TmuxFixture};
|
|
use tempfile::TempDir;
|
|
use uuid::Uuid;
|
|
|
|
#[test]
|
|
fn create_list_kill_round_trip() {
|
|
let fx = TmuxFixture::new();
|
|
let cwd = TempDir::new().unwrap();
|
|
let id = Uuid::new_v4();
|
|
|
|
assert!(fx.tmux.list_sessions().unwrap().is_empty());
|
|
|
|
fx.tmux
|
|
.new_session(id, cwd.path(), "sleep 30")
|
|
.expect("new_session");
|
|
|
|
wait_for(Duration::from_secs(3), || {
|
|
fx.tmux.list_sessions().unwrap().contains(&id)
|
|
});
|
|
|
|
fx.tmux.kill_session(id).expect("kill_session");
|
|
|
|
wait_for(Duration::from_secs(3), || {
|
|
!fx.tmux.list_sessions().unwrap().contains(&id)
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn list_sessions_ignores_foreign_names() {
|
|
let fx = TmuxFixture::new();
|
|
let cwd = TempDir::new().unwrap();
|
|
let maestro_id = Uuid::new_v4();
|
|
|
|
// Drop a non-maestro session onto the same tmux server. list_sessions
|
|
// should still report only the maestro-prefixed ones.
|
|
let out = Command::new("tmux")
|
|
.arg("-L")
|
|
.arg(fx.tmux.socket_name().unwrap())
|
|
.env("TMUX_TMPDIR", fx.tmux.tmpdir().unwrap())
|
|
.args(["new-session", "-d", "-s", "foreign", "-c"])
|
|
.arg(cwd.path())
|
|
.arg("sleep 30")
|
|
.output()
|
|
.expect("raw tmux");
|
|
assert!(out.status.success(), "raw tmux failed: {out:?}");
|
|
|
|
fx.tmux
|
|
.new_session(maestro_id, cwd.path(), "sleep 30")
|
|
.expect("new_session");
|
|
|
|
wait_for(Duration::from_secs(3), || {
|
|
fx.tmux.list_sessions().unwrap() == vec![maestro_id]
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn send_keys_and_pipe_pane_capture_output() {
|
|
let fx = TmuxFixture::new();
|
|
let cwd = TempDir::new().unwrap();
|
|
let sink = TempDir::new().unwrap();
|
|
let sink_path = sink.path().join("out.log");
|
|
let id = Uuid::new_v4();
|
|
|
|
fx.tmux
|
|
.new_session(id, cwd.path(), "bash --noprofile --norc")
|
|
.expect("new_session");
|
|
|
|
wait_for(Duration::from_secs(3), || {
|
|
fx.tmux.list_sessions().unwrap().contains(&id)
|
|
});
|
|
|
|
let sink_command = format!("cat >> {}", shell_quote(&sink_path.display().to_string()));
|
|
fx.tmux.pipe_pane(id, &sink_command).expect("pipe_pane");
|
|
|
|
fx.tmux
|
|
.send_keys(id, b"echo MAESTRO_MARKER_BANG\n")
|
|
.expect("send_keys");
|
|
|
|
wait_for(Duration::from_secs(5), || {
|
|
std::fs::read_to_string(&sink_path)
|
|
.map(|s| s.contains("MAESTRO_MARKER_BANG"))
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
fx.tmux.kill_session(id).expect("kill_session");
|
|
}
|
|
|
|
#[test]
|
|
fn resize_window_succeeds() {
|
|
let fx = TmuxFixture::new();
|
|
let cwd = TempDir::new().unwrap();
|
|
let id = Uuid::new_v4();
|
|
|
|
fx.tmux
|
|
.new_session(id, cwd.path(), "sleep 30")
|
|
.expect("new_session");
|
|
|
|
wait_for(Duration::from_secs(3), || {
|
|
fx.tmux.list_sessions().unwrap().contains(&id)
|
|
});
|
|
|
|
fx.tmux.resize_window(id, 120, 40).expect("resize_window");
|
|
|
|
fx.tmux.kill_session(id).expect("kill_session");
|
|
}
|
|
|
|
#[test]
|
|
fn new_session_sets_256color_env_and_default_terminal() {
|
|
let fx = TmuxFixture::new();
|
|
let cwd = TempDir::new().unwrap();
|
|
let sink = TempDir::new().unwrap();
|
|
let sink_path = sink.path().join("env.log");
|
|
let id = Uuid::new_v4();
|
|
|
|
fx.tmux
|
|
.new_session(id, cwd.path(), "bash --noprofile --norc")
|
|
.expect("new_session");
|
|
|
|
wait_for(Duration::from_secs(3), || {
|
|
fx.tmux.list_sessions().unwrap().contains(&id)
|
|
});
|
|
|
|
let sink_command = format!("cat >> {}", shell_quote(&sink_path.display().to_string()));
|
|
fx.tmux.pipe_pane(id, &sink_command).expect("pipe_pane");
|
|
|
|
fx.tmux
|
|
.send_keys(
|
|
id,
|
|
b"printf 'TERM=%s\\nCOLORTERM=%s\\nCOLORS=%s\\n' \"$TERM\" \"$COLORTERM\" \"$(tput colors)\"\n",
|
|
)
|
|
.expect("send_keys");
|
|
|
|
wait_for(Duration::from_secs(5), || {
|
|
std::fs::read_to_string(&sink_path)
|
|
.map(|s| {
|
|
s.contains("TERM=tmux-256color")
|
|
&& s.contains("COLORTERM=truecolor")
|
|
&& s.contains("COLORS=256")
|
|
})
|
|
.unwrap_or(false)
|
|
});
|
|
|
|
fx.tmux.kill_session(id).expect("kill_session");
|
|
}
|
|
|
|
#[test]
|
|
fn list_sessions_returns_empty_when_no_server() {
|
|
let fx = TmuxFixture::new();
|
|
let sessions = fx.tmux.list_sessions().expect("list_sessions");
|
|
assert!(sessions.is_empty());
|
|
}
|
|
|
|
fn shell_quote(s: &str) -> String {
|
|
let escaped = s.replace('\'', r#"'\''"#);
|
|
format!("'{escaped}'")
|
|
}
|