test: isolate integration subprocesses from pane env (#4929)

* test: isolate integration subprocesses from pane env

refs #4928

* test: cover pane env isolation for spawned servers

refs #4928

---------

Co-authored-by: kataokatsuki <kataokatsuki@users.noreply.github.com>
This commit is contained in:
Kataoka Katsuki
2026-10-04 15:49:07 +02:00
committed by GitHub
co-authored by kataokatsuki
parent 5da0a01e1e
commit bce28752ad
11 changed files with 73 additions and 2 deletions
+42
View File
@@ -141,6 +141,7 @@ fn spawn_herdr_with_options(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -311,6 +312,47 @@ fn ping_over_socket_returns_version() {
cleanup_spawned_herdr(child, base);
}
#[test]
fn spawned_server_ignores_inherited_pane_env() {
let _lock = test_lock();
let base = unique_test_dir();
let config_home = base.join("config");
let runtime_dir = base.join("runtime");
let socket_path = runtime_dir.join("herdr.sock");
let startup_cwd = base.join("startup");
fs::create_dir_all(&startup_cwd).unwrap();
// A shell inside a herdr pane passes these to every process it starts.
let saved: Vec<_> = ["HERDR_STARTUP_CWD", "HERDR_SESSION"]
.into_iter()
.map(|name| (name, std::env::var_os(name)))
.collect();
std::env::set_var("HERDR_STARTUP_CWD", &startup_cwd);
std::env::set_var("HERDR_SESSION", "inherited");
let child = spawn_herdr(&config_home, &runtime_dir, &socket_path);
for (name, value) in saved {
match value {
Some(value) => std::env::set_var(name, value),
None => std::env::remove_var(name),
}
}
wait_for_socket(&socket_path, Duration::from_secs(5));
let value = send_request(
&socket_path,
r#"{"id":"req_1","method":"workspace.list","params":{}}"#,
);
assert_eq!(value["result"]["workspaces"], serde_json::json!([]));
let app_dir = if cfg!(debug_assertions) {
"herdr-dev"
} else {
"herdr"
};
assert!(!config_home.join(app_dir).join("sessions").exists());
cleanup_spawned_herdr(child, base);
}
#[test]
fn server_reload_agent_manifests_reports_runtime_override() {
let _lock = test_lock();
+3
View File
@@ -106,6 +106,7 @@ fn spawn_server(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -150,6 +151,7 @@ fn spawn_herdr_auto(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
// No subcommand → auto-detect launch.
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -600,6 +602,7 @@ fn auto_detect_default_socket_path_from_config_dir() {
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", &config_home);
cmd.env("XDG_RUNTIME_DIR", &runtime_dir);
+5 -2
View File
@@ -7,8 +7,8 @@ pub(super) use std::thread;
pub(super) use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
pub(super) use crate::support::{
cleanup_test_base, register_runtime_dir, register_spawned_herdr_pid,
unregister_spawned_herdr_pid, CURRENT_PROTOCOL,
cleanup_test_base, isolate_herdr_test_process, register_runtime_dir,
register_spawned_herdr_pid, unregister_spawned_herdr_pid, CURRENT_PROTOCOL,
};
use portable_pty::{native_pty_system, Child, CommandBuilder, MasterPty, PtySize};
@@ -182,6 +182,8 @@ pub(super) fn spawn_named_server(
.env_remove("HERDR_SOCKET_PATH")
.env_remove("HERDR_CLIENT_SOCKET_PATH")
.env_remove("HERDR_ENV")
.env_remove("HERDR_STARTUP_CWD")
.env_remove("HERDR_SESSION")
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null());
@@ -300,6 +302,7 @@ pub(super) fn spawn_herdr_with_config(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
+4
View File
@@ -125,6 +125,7 @@ fn spawn_client_process_with_args_and_env(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.args(args);
cmd.env("HERDR_DISABLE_SOUND", "1");
cmd.env("XDG_STATE_HOME", runtime_dir.join("state"));
@@ -185,6 +186,7 @@ fn spawn_server_with_config(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -435,6 +437,7 @@ fn client_sees_headless_startup_config_diagnostic() {
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", &config_home);
cmd.env("XDG_RUNTIME_DIR", &runtime_dir);
@@ -2068,6 +2071,7 @@ fn client_receives_notify_on_agent_state_change() {
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", &config_home);
cmd.env("XDG_RUNTIME_DIR", &runtime_dir);
+2
View File
@@ -118,6 +118,7 @@ fn spawn_server_with_path(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_STATE_HOME", runtime_dir.join("state"));
cmd.env("XDG_CONFIG_HOME", config_home);
@@ -156,6 +157,7 @@ fn spawn_client_process(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("client");
cmd.env("HERDR_DISABLE_SOUND", "1");
cmd.env("XDG_STATE_HOME", runtime_dir.join("state"));
+1
View File
@@ -121,6 +121,7 @@ fn spawn_server_with_config(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
+1
View File
@@ -134,6 +134,7 @@ async fn host_shutdown_saves_layout_before_releasing_delay_lock() {
.env_remove("HERDR_SOCKET_PATH")
.env("DBUS_SYSTEM_BUS_ADDRESS", address.trim())
.env_remove("HERDR_CLIENT_SOCKET_PATH")
.env_remove("HERDR_STARTUP_CWD")
.env_remove("HERDR_SESSION")
.env_remove("HERDR_WORKSPACE_ID")
.env_remove("HERDR_TAB_ID")
+4
View File
@@ -78,6 +78,7 @@ fn spawn_server_with_env(
})
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -121,6 +122,7 @@ fn spawn_named_session_server(
})
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -155,6 +157,7 @@ fn spawn_default_session_server(config_home: &Path, runtime_dir: &Path) -> Spawn
})
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -196,6 +199,7 @@ fn spawn_server_with_args_and_socket_env(
})
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
if let Some(session_name) = session_name {
cmd.arg("--session");
cmd.arg(session_name);
+2
View File
@@ -89,6 +89,7 @@ fn spawn_server(config: &Path, runtime: &Path, api: &Path) -> SpawnedHerdr {
})
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config);
cmd.env("XDG_RUNTIME_DIR", runtime);
@@ -116,6 +117,7 @@ fn spawn_client(config: &Path, runtime: &Path, api: &Path) -> SpawnedHerdr {
})
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("client");
cmd.env("HERDR_DISABLE_SOUND", "1");
cmd.env("XDG_CONFIG_HOME", config);
+2
View File
@@ -123,6 +123,7 @@ fn spawn_server(
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", config_home);
cmd.env("XDG_RUNTIME_DIR", runtime_dir);
@@ -356,6 +357,7 @@ fn duplicate_server_start_fails_gracefully() {
.unwrap();
let mut cmd = CommandBuilder::new(env!("CARGO_BIN_EXE_herdr"));
support::isolate_herdr_test_process(&mut cmd);
cmd.arg("server");
cmd.env("XDG_CONFIG_HOME", &config_home);
cmd.env("XDG_RUNTIME_DIR", &runtime_dir);
+7
View File
@@ -7,6 +7,8 @@ use std::sync::{Mutex, Once, OnceLock};
use std::thread;
use std::time::{Duration, Instant};
use portable_pty::CommandBuilder;
static PID_REGISTRY: OnceLock<Mutex<HashSet<u32>>> = OnceLock::new();
static RUNTIME_DIR_REGISTRY: OnceLock<Mutex<HashSet<PathBuf>>> = OnceLock::new();
static INIT: Once = Once::new();
@@ -24,6 +26,11 @@ const CLIENT_MESSAGE_CLIENT_SHELL_PANE_INPUT: u32 = 13;
const CLIENT_MESSAGE_CLIENT_SHELL_FOCUS: u32 = 18;
const CLIENT_MESSAGE_ENDPOINT_CONTROL: u32 = 20;
pub fn isolate_herdr_test_process(command: &mut CommandBuilder) {
command.env_remove("HERDR_STARTUP_CWD");
command.env_remove("HERDR_SESSION");
}
pub fn register_spawned_herdr_pid(pid: Option<u32>) {
let Some(pid) = pid else {
return;