Fix wait command client timeouts and test failures

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Harivansh Rathi 2026-03-25 21:06:42 -04:00
parent f87ac61790
commit 7f12524fbb
4 changed files with 47 additions and 19 deletions

View file

@ -79,8 +79,23 @@ fn spawn_daemon(opts: &GlobalOpts) -> Result<()> {
Ok(()) Ok(())
} }
fn request_read_timeout(request: &Request) -> Duration {
let default_timeout = Duration::from_secs(30);
match request.action.as_str() {
"wait-window" | "wait-focus" => {
let wait_timeout = request
.extra
.get("timeout_ms")
.and_then(|value| value.as_u64())
.unwrap_or(10_000);
Duration::from_millis(wait_timeout.saturating_add(5_000))
}
_ => default_timeout,
}
}
fn send_request_over_stream(mut stream: UnixStream, request: &Request) -> Result<Response> { fn send_request_over_stream(mut stream: UnixStream, request: &Request) -> Result<Response> {
stream.set_read_timeout(Some(Duration::from_secs(30)))?; stream.set_read_timeout(Some(request_read_timeout(request)))?;
stream.set_write_timeout(Some(Duration::from_secs(5)))?; stream.set_write_timeout(Some(Duration::from_secs(5)))?;
let json = serde_json::to_string(request)?; let json = serde_json::to_string(request)?;

View file

@ -245,9 +245,13 @@ pub fn run() -> Result<()> {
// All other commands need a daemon connection // All other commands need a daemon connection
let request = build_request(&app.command)?; let request = build_request(&app.command)?;
let response = connection::send_command(&app.global, &request)?; let response = connection::send_command(&app.global, &request)?;
let success = response.success;
if app.global.json { if app.global.json {
println!("{}", serde_json::to_string_pretty(&response)?); println!("{}", serde_json::to_string_pretty(&response)?);
if !success {
std::process::exit(1);
}
} else { } else {
print_response(&app.command, &response)?; print_response(&app.command, &response)?;
} }
@ -392,11 +396,7 @@ fn print_response(cmd: &Command, response: &Response) -> Result<()> {
} else { } else {
"visible" "visible"
}; };
let display_title = if title.len() > 30 { let display_title = truncate_display(title, 30);
format!("{}...", &title[..27])
} else {
title.to_string()
};
println!( println!(
"@{:<4} {:<30} ({:<7}) {},{} {}x{}", "@{:<4} {:<30} ({:<7}) {},{} {}x{}",
ref_id, display_title, state, x, y, width, height ref_id, display_title, state, x, y, width, height
@ -496,11 +496,7 @@ fn print_window_line(window: &serde_json::Value, stderr: bool) {
let line = format!( let line = format!(
"@{:<4} {:<30} ({:<7}) {},{} {}x{} [{}]", "@{:<4} {:<30} ({:<7}) {},{} {}x{} [{}]",
ref_id, ref_id,
if title.len() > 30 { truncate_display(title, 30),
format!("{}...", &title[..27])
} else {
title.to_string()
},
state, state,
x, x,
y, y,
@ -514,3 +510,13 @@ fn print_window_line(window: &serde_json::Value, stderr: bool) {
println!("{line}"); println!("{line}");
} }
} }
fn truncate_display(value: &str, max_chars: usize) -> String {
let char_count = value.chars().count();
if char_count <= max_chars {
return value.to_string();
}
let truncated: String = value.chars().take(max_chars.saturating_sub(3)).collect();
format!("{truncated}...")
}

View file

@ -21,6 +21,13 @@ pub fn env_lock() -> &'static Mutex<()> {
LOCK.get_or_init(|| Mutex::new(())) LOCK.get_or_init(|| Mutex::new(()))
} }
pub fn env_lock_guard() -> std::sync::MutexGuard<'static, ()> {
match env_lock().lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
}
}
pub struct SessionEnvGuard { pub struct SessionEnvGuard {
old_session_type: Option<String>, old_session_type: Option<String>,
} }

View file

@ -8,13 +8,13 @@ use deskctl::core::doctor;
use deskctl::core::protocol::Request; use deskctl::core::protocol::Request;
use self::support::{ use self::support::{
deskctl_tmp_screenshot_count, env_lock, json_response, successful_json_response, FixtureWindow, deskctl_tmp_screenshot_count, env_lock_guard, json_response, successful_json_response,
SessionEnvGuard, TestSession, FixtureWindow, SessionEnvGuard, TestSession,
}; };
#[test] #[test]
fn doctor_reports_healthy_x11_environment() -> Result<()> { fn doctor_reports_healthy_x11_environment() -> Result<()> {
let _guard = env_lock().lock().unwrap(); let _guard = env_lock_guard();
let Some(_env) = SessionEnvGuard::prepare() else { let Some(_env) = SessionEnvGuard::prepare() else {
eprintln!("Skipping X11 integration test because DISPLAY is not set"); eprintln!("Skipping X11 integration test because DISPLAY is not set");
return Ok(()); return Ok(());
@ -46,7 +46,7 @@ fn doctor_reports_healthy_x11_environment() -> Result<()> {
#[test] #[test]
fn list_windows_is_side_effect_free() -> Result<()> { fn list_windows_is_side_effect_free() -> Result<()> {
let _guard = env_lock().lock().unwrap(); let _guard = env_lock_guard();
let Some(_env) = SessionEnvGuard::prepare() else { let Some(_env) = SessionEnvGuard::prepare() else {
eprintln!("Skipping X11 integration test because DISPLAY is not set"); eprintln!("Skipping X11 integration test because DISPLAY is not set");
return Ok(()); return Ok(());
@ -84,7 +84,7 @@ fn list_windows_is_side_effect_free() -> Result<()> {
#[test] #[test]
fn daemon_start_recovers_from_stale_socket() -> Result<()> { fn daemon_start_recovers_from_stale_socket() -> Result<()> {
let _guard = env_lock().lock().unwrap(); let _guard = env_lock_guard();
let Some(_env) = SessionEnvGuard::prepare() else { let Some(_env) = SessionEnvGuard::prepare() else {
eprintln!("Skipping X11 integration test because DISPLAY is not set"); eprintln!("Skipping X11 integration test because DISPLAY is not set");
return Ok(()); return Ok(());
@ -116,7 +116,7 @@ fn daemon_start_recovers_from_stale_socket() -> Result<()> {
#[test] #[test]
fn wait_window_returns_matched_window_payload() -> Result<()> { fn wait_window_returns_matched_window_payload() -> Result<()> {
let _guard = env_lock().lock().unwrap(); let _guard = env_lock_guard();
let Some(_env) = SessionEnvGuard::prepare() else { let Some(_env) = SessionEnvGuard::prepare() else {
eprintln!("Skipping X11 integration test because DISPLAY is not set"); eprintln!("Skipping X11 integration test because DISPLAY is not set");
return Ok(()); return Ok(());
@ -158,7 +158,7 @@ fn wait_window_returns_matched_window_payload() -> Result<()> {
#[test] #[test]
fn ambiguous_fuzzy_selector_returns_candidates() -> Result<()> { fn ambiguous_fuzzy_selector_returns_candidates() -> Result<()> {
let _guard = env_lock().lock().unwrap(); let _guard = env_lock_guard();
let Some(_env) = SessionEnvGuard::prepare() else { let Some(_env) = SessionEnvGuard::prepare() else {
eprintln!("Skipping X11 integration test because DISPLAY is not set"); eprintln!("Skipping X11 integration test because DISPLAY is not set");
return Ok(()); return Ok(());
@ -194,7 +194,7 @@ fn ambiguous_fuzzy_selector_returns_candidates() -> Result<()> {
#[test] #[test]
fn wait_focus_timeout_is_structured() -> Result<()> { fn wait_focus_timeout_is_structured() -> Result<()> {
let _guard = env_lock().lock().unwrap(); let _guard = env_lock_guard();
let Some(_env) = SessionEnvGuard::prepare() else { let Some(_env) = SessionEnvGuard::prepare() else {
eprintln!("Skipping X11 integration test because DISPLAY is not set"); eprintln!("Skipping X11 integration test because DISPLAY is not set");
return Ok(()); return Ok(());