From bce28752adb4eea0788013b9e2b0793d82737dfe Mon Sep 17 00:00:00 2001 From: Kataoka Katsuki <49934462+kataokatsuki@users.noreply.github.com> Date: Sun, 4 Oct 2026 22:49:07 +0900 Subject: [PATCH] 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 --- tests/api_ping.rs | 42 ++++++++++++++++++++++++++++++++++++++++ tests/auto_detect.rs | 3 +++ tests/cli/harness.rs | 7 +++++-- tests/client_mode.rs | 4 ++++ tests/cross_area.rs | 2 ++ tests/detach_reattach.rs | 1 + tests/host_shutdown.rs | 1 + tests/live_handoff.rs | 4 ++++ tests/multi_client.rs | 2 ++ tests/server_headless.rs | 2 ++ tests/support/mod.rs | 7 +++++++ 11 files changed, 73 insertions(+), 2 deletions(-) diff --git a/tests/api_ping.rs b/tests/api_ping.rs index b86d0378..5c47c6b9 100644 --- a/tests/api_ping.rs +++ b/tests/api_ping.rs @@ -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(); diff --git a/tests/auto_detect.rs b/tests/auto_detect.rs index deb01bab..21332ab5 100644 --- a/tests/auto_detect.rs +++ b/tests/auto_detect.rs @@ -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); diff --git a/tests/cli/harness.rs b/tests/cli/harness.rs index 91dab149..c4b83b42 100644 --- a/tests/cli/harness.rs +++ b/tests/cli/harness.rs @@ -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); diff --git a/tests/client_mode.rs b/tests/client_mode.rs index b5532317..03a6a7ed 100644 --- a/tests/client_mode.rs +++ b/tests/client_mode.rs @@ -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); diff --git a/tests/cross_area.rs b/tests/cross_area.rs index 3e933c95..ea1c25ed 100644 --- a/tests/cross_area.rs +++ b/tests/cross_area.rs @@ -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")); diff --git a/tests/detach_reattach.rs b/tests/detach_reattach.rs index 0bbe94b6..7e464976 100644 --- a/tests/detach_reattach.rs +++ b/tests/detach_reattach.rs @@ -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); diff --git a/tests/host_shutdown.rs b/tests/host_shutdown.rs index f988a52a..9e4f117d 100644 --- a/tests/host_shutdown.rs +++ b/tests/host_shutdown.rs @@ -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") diff --git a/tests/live_handoff.rs b/tests/live_handoff.rs index 3420940c..eb76fea6 100644 --- a/tests/live_handoff.rs +++ b/tests/live_handoff.rs @@ -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); diff --git a/tests/multi_client.rs b/tests/multi_client.rs index 04f5ed0f..8d9daae8 100644 --- a/tests/multi_client.rs +++ b/tests/multi_client.rs @@ -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); diff --git a/tests/server_headless.rs b/tests/server_headless.rs index 6bb054ad..d7278535 100644 --- a/tests/server_headless.rs +++ b/tests/server_headless.rs @@ -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); diff --git a/tests/support/mod.rs b/tests/support/mod.rs index cc29afe9..5ea22dca 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -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>> = OnceLock::new(); static RUNTIME_DIR_REGISTRY: OnceLock>> = 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) { let Some(pid) = pid else { return;