diff options
| author | JJ Liebig <jonathan.liebig@gmail.com> | 2026-09-05 20:39:32 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-05 21:39:32 +0300 |
| commit | 50ce4ec2ddb8ec621a77d8773aa6df6d9e7441ff (patch) | |
| tree | b61a6e636e31394334ba2ba8e04a34c6e46d4d1b /src | |
| parent | 6c52aad511b0fb601e6223bd6ad944f48cbdba6d (diff) | |
fix: run the full applicable test suite on windows (#3660)
* fix: run the full applicable test suite on windows
* test: remove redundant test shell assignment
* test: make endpoint and metadata fixtures deterministic
* test: tolerate exited servers during windows probe cleanup
Diffstat (limited to 'src')
| -rw-r--r-- | src/api/server/pane_graphics_stream.rs | 21 | ||||
| -rw-r--r-- | src/app/api.rs | 1 | ||||
| -rw-r--r-- | src/app/api/layouts.rs | 4 | ||||
| -rw-r--r-- | src/app/api/worktrees.rs | 7 | ||||
| -rw-r--r-- | src/app/mod.rs | 22 | ||||
| -rw-r--r-- | src/client/shell/preferences.rs | 2 | ||||
| -rw-r--r-- | src/client/shell/tests/agents_worktrees_notifications.rs | 4 | ||||
| -rw-r--r-- | src/detect/manifest_update.rs | 27 | ||||
| -rw-r--r-- | src/integration/assets/herdr-agent-state.test.ts | 6 | ||||
| -rw-r--r-- | src/integration/tests.rs | 136 | ||||
| -rw-r--r-- | src/pane/terminal.rs | 10 | ||||
| -rw-r--r-- | src/platform/mod.rs | 8 | ||||
| -rw-r--r-- | src/platform/windows.rs | 33 | ||||
| -rw-r--r-- | src/server/headless/tests/mod.rs | 6 | ||||
| -rw-r--r-- | src/terminal/metadata.rs | 9 |
15 files changed, 89 insertions, 207 deletions
diff --git a/src/api/server/pane_graphics_stream.rs b/src/api/server/pane_graphics_stream.rs index e4d3315d..0df857fc 100644 --- a/src/api/server/pane_graphics_stream.rs +++ b/src/api/server/pane_graphics_stream.rs @@ -393,7 +393,7 @@ fn read_line( total_deadline, "timed out reading stream frame header", )?; - match stream.read(&mut byte) { + match wait.read(stream, &mut byte) { Ok(0) => return Ok(None), Ok(_) => { wait.on_progress(); @@ -455,7 +455,7 @@ fn read_exact( )?; let remaining = len - data.len(); let read_len = remaining.min(chunk.len()); - match stream.read(&mut chunk[..read_len]) { + match wait.read(stream, &mut chunk[..read_len]) { Ok(0) if data.is_empty() => return Ok(None), Ok(0) => { return Err(io::Error::new( @@ -494,6 +494,17 @@ enum ReadWait { } impl ReadWait { + fn read(&self, stream: &mut LocalStream, buffer: &mut [u8]) -> io::Result<usize> { + if matches!(self, Self::SocketTimeout) { + return stream.read(buffer); + } + match crate::ipc::poll_local_stream_read_count(stream, buffer)? { + crate::ipc::LocalStreamReadCount::Data(count) => Ok(count), + crate::ipc::LocalStreamReadCount::Pending => Err(io::ErrorKind::WouldBlock.into()), + crate::ipc::LocalStreamReadCount::Closed => Ok(0), + } + } + fn after_retry(&mut self, idle_deadline: Option<Instant>, total_deadline: Option<Instant>) { if let Self::Poll(backoff) = self { sleep_until_poll(idle_deadline, total_deadline, backoff.interval); @@ -545,9 +556,11 @@ fn with_timed_reads<T>( finish_timed_read(result, || stream.set_recv_timeout(None)) } Err(err) if err.kind() == io::ErrorKind::Unsupported => { - stream.set_nonblocking(true)?; + crate::ipc::set_local_stream_polling(stream, true)?; let result = read(stream, ReadWait::Poll(PollBackoff::new())); - finish_timed_read(result, || stream.set_nonblocking(false)) + finish_timed_read(result, || { + crate::ipc::set_local_stream_polling(stream, false) + }) } // A peer can disconnect after the caller's running check but before // setsockopt. macOS reports that closed-socket race as EINVAL. diff --git a/src/app/api.rs b/src/app/api.rs index 598356b0..c504b4c0 100644 --- a/src/app/api.rs +++ b/src/app/api.rs @@ -2207,6 +2207,7 @@ mod tests { crate::api::EventHub::default(), ); let workspace = crate::workspace::Workspace::test_new("restored"); + app.state.default_shell = test_support::exiting_test_command().into(); let pane_id = workspace.tabs[0].root_pane; let terminal_id = workspace.terminal_id(pane_id).cloned().unwrap(); app.state.workspaces = vec![workspace]; diff --git a/src/app/api/layouts.rs b/src/app/api/layouts.rs index 943ed835..74190434 100644 --- a/src/app/api/layouts.rs +++ b/src/app/api/layouts.rs @@ -747,7 +747,7 @@ mod tests { second: Box::new(LayoutNode::Pane { pane: LayoutPane { label: Some("tests".into()), - command: Some(vec!["sh".into(), "-c".into(), "true".into()]), + command: Some(vec![exiting_test_command().into()]), env: std::collections::HashMap::from([( "HERDR_ROLE".into(), "tests".into(), @@ -789,7 +789,7 @@ mod tests { assert_eq!(second_pane.label.as_deref(), Some("tests")); assert_eq!( second_pane.command, - Some(vec!["sh".into(), "-c".into(), "true".into()]) + Some(vec![exiting_test_command().into()]) ); assert!(matches!( &app.event_hub.events_after(0).last().expect("layout event").1.data, diff --git a/src/app/api/worktrees.rs b/src/app/api/worktrees.rs index fd7bb0fb..3c8407b9 100644 --- a/src/app/api/worktrees.rs +++ b/src/app/api/worktrees.rs @@ -794,13 +794,15 @@ mod tests { fn test_app_with_event_hub(event_hub: crate::api::EventHub) -> App { let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); - App::new( + let mut app = App::new( &Config::default(), crate::app::AppPolicy::TEST, None, api_rx, event_hub, - ) + ); + app.state.default_shell = test_shell().into(); + app } #[cfg(windows)] @@ -815,7 +817,6 @@ mod tests { fn app_with_parent(repo: &Path) -> App { let mut app = test_app(); - app.state.default_shell = test_shell().into(); let mut parent = Workspace::test_new("main"); parent.identity_cwd = repo.to_path_buf(); app.state.workspaces = vec![parent]; diff --git a/src/app/mod.rs b/src/app/mod.rs index ed2c5fd9..0f78cf8b 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -9,6 +9,8 @@ pub(crate) mod agent_view; mod agents; pub(crate) use agents::{AGENT_START_SETTLE_DELAY, MAX_AGENT_START_TIMEOUT}; mod api; +#[cfg(test)] +pub(crate) use api::test_support::exiting_test_command; mod api_helpers; pub(crate) use api_helpers::limit_snapshot_lines; mod creation; @@ -975,13 +977,15 @@ mod tests { fn test_app() -> App { let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); - App::new( + let mut app = App::new( &Config::default(), crate::app::AppPolicy::TEST, None, api_rx, crate::api::EventHub::default(), - ) + ); + app.state.default_shell = exiting_test_command().into(); + app } fn unique_temp_path(name: &str) -> std::path::PathBuf { @@ -992,16 +996,6 @@ mod tests { std::env::temp_dir().join(format!("herdr-{name}-{}-{stamp}", std::process::id())) } - #[cfg(windows)] - fn exiting_test_command() -> &'static str { - "C:\\Windows\\System32\\whoami.exe" - } - - #[cfg(not(windows))] - fn exiting_test_command() -> &'static str { - "/usr/bin/true" - } - fn config_env_lock() -> &'static Mutex<()> { crate::config::test_config_env_lock() } @@ -2657,7 +2651,7 @@ mod tests { async fn pane_split_request_applies_ratio() { let _guard = config_env_lock().lock().unwrap(); let original_shell = std::env::var_os("SHELL"); - std::env::set_var("SHELL", "/usr/bin/true"); + std::env::set_var("SHELL", exiting_test_command()); let mut app = test_app(); let workspace = Workspace::test_new("api-pane-split-ratio"); @@ -2713,7 +2707,7 @@ mod tests { async fn pane_split_request_uses_active_focused_pane_when_target_is_omitted() { let _guard = config_env_lock().lock().unwrap(); let original_shell = std::env::var_os("SHELL"); - std::env::set_var("SHELL", "/usr/bin/true"); + std::env::set_var("SHELL", exiting_test_command()); let mut app = test_app(); let workspace = Workspace::test_new("api-pane-split-current"); diff --git a/src/client/shell/preferences.rs b/src/client/shell/preferences.rs index dc6cc1ac..f657dfbf 100644 --- a/src/client/shell/preferences.rs +++ b/src/client/shell/preferences.rs @@ -54,7 +54,7 @@ pub(super) fn store(path: &Path, preferences: ClientChromePreferences) -> Result let temp_path = parent.join(temp_name); std::fs::write(&temp_path, content) .map_err(|error| format!("failed to write client shell state: {error}"))?; - crate::platform::replace_file(&temp_path, path).map_err(|error| { + std::fs::rename(&temp_path, path).map_err(|error| { let _ = std::fs::remove_file(&temp_path); format!("failed to replace client shell state: {error}") }) diff --git a/src/client/shell/tests/agents_worktrees_notifications.rs b/src/client/shell/tests/agents_worktrees_notifications.rs index 055b594d..2ccfe77b 100644 --- a/src/client/shell/tests/agents_worktrees_notifications.rs +++ b/src/client/shell/tests/agents_worktrees_notifications.rs @@ -966,7 +966,9 @@ fn worktree_create_previews_the_endpoint_owned_checkout_path() { &state.overlay, Some(ClientShellOverlay::WorktreeCreate(create)) if create.checkout_path - == "/tmp/herdr-worktrees/repo/feature-client-shell" + == std::path::Path::new("/tmp/herdr-worktrees") + .join("repo") + .join("feature-client-shell").to_string_lossy() )); let submit = state.handle_input_bytes(b"\r"); let [ClientShellAction::Endpoint { request, .. }] = &submit.actions[..] else { diff --git a/src/detect/manifest_update.rs b/src/detect/manifest_update.rs index 4e165fe9..33493059 100644 --- a/src/detect/manifest_update.rs +++ b/src/detect/manifest_update.rs @@ -699,7 +699,14 @@ path = "codex.toml" .unwrap(); std::env::set_var( CATALOG_URL_ENV, - format!("file://{}", web_dir.join("index.toml").display()), + format!( + "file:///{}", + web_dir + .join("index.toml") + .to_string_lossy() + .replace('\\', "/") + .trim_start_matches('/') + ), ); let (tx, mut rx) = tokio::sync::mpsc::channel(1); @@ -762,7 +769,14 @@ path = "codex.toml" fs::write(remote_manifest_path(Agent::Codex), current).unwrap(); std::env::set_var( CATALOG_URL_ENV, - format!("file://{}", web_dir.join("index.toml").display()), + format!( + "file:///{}", + web_dir + .join("index.toml") + .to_string_lossy() + .replace('\\', "/") + .trim_start_matches('/') + ), ); let (tx, mut rx) = tokio::sync::mpsc::channel(1); @@ -833,7 +847,14 @@ path = "missing-cursor.toml" .unwrap(); std::env::set_var( CATALOG_URL_ENV, - format!("file://{}", web_dir.join("index.toml").display()), + format!( + "file:///{}", + web_dir + .join("index.toml") + .to_string_lossy() + .replace('\\', "/") + .trim_start_matches('/') + ), ); let (tx, mut rx) = tokio::sync::mpsc::channel(1); diff --git a/src/integration/assets/herdr-agent-state.test.ts b/src/integration/assets/herdr-agent-state.test.ts index 2a86f0ca..7720e088 100644 --- a/src/integration/assets/herdr-agent-state.test.ts +++ b/src/integration/assets/herdr-agent-state.test.ts @@ -121,7 +121,7 @@ async function startRecordingServer(name: string): Promise<unknown[]> { server = recordingServer; await new Promise<void>((resolve, reject) => { recordingServer.once("error", reject); - recordingServer.listen(recordingSocketPath, resolve); + recordingServer.listen(originalPlatform === "win32" ? `\\\\.\\pipe\\${recordingSocketPath}` : recordingSocketPath, resolve); }); configureIntegrationEnvironment(recordingSocketPath); return requests; @@ -378,7 +378,7 @@ test("Pi waits for a replacement session report before publishing state", async server = recordingServer; await new Promise<void>((resolve, reject) => { recordingServer.once("error", reject); - recordingServer.listen(recordingSocketPath, resolve); + recordingServer.listen(originalPlatform === "win32" ? `\\\\.\\pipe\\${recordingSocketPath}` : recordingSocketPath, resolve); }); configureIntegrationEnvironment(recordingSocketPath); @@ -457,7 +457,7 @@ async function startDroppedFirstResponseServer(name: string) { server = recordingServer; await new Promise<void>((resolve, reject) => { recordingServer.once("error", reject); - recordingServer.listen(recordingSocketPath, resolve); + recordingServer.listen(originalPlatform === "win32" ? `\\\\.\\pipe\\${recordingSocketPath}` : recordingSocketPath, resolve); }); configureIntegrationEnvironment(recordingSocketPath); diff --git a/src/integration/tests.rs b/src/integration/tests.rs index a02a549e..b2210074 100644 --- a/src/integration/tests.rs +++ b/src/integration/tests.rs @@ -2082,8 +2082,8 @@ fn install_droid_writes_hook_to_settings_and_cleans_legacy_hooks_json() { fs::write( droid_dir.join("hooks.json"), format!( - r#"{{"hooks":{{"SessionStart":[{{"hooks":[{{"type":"command","command":"{}","timeout":10}}]}}],"PreToolUse":[{{"matcher":"Read","hooks":[{{"type":"command","command":"echo keep","timeout":10}}]}}]}}}}"#, - legacy_command, + r#"{{"hooks":{{"SessionStart":[{{"hooks":[{{"type":"command","command":{},"timeout":10}}]}}],"PreToolUse":[{{"matcher":"Read","hooks":[{{"type":"command","command":"echo keep","timeout":10}}]}}]}}}}"#, + serde_json::to_string(&legacy_command).unwrap(), ), ) .unwrap(); @@ -2207,16 +2207,16 @@ fn uninstall_droid_removes_herdr_hooks_and_preserves_others() { fs::write( droid_dir.join("hooks.json"), format!( - r#"{{"hooks":{{"SessionStart":[{{"hooks":[{{"type":"command","command":"{}","timeout":10}},{{"type":"command","command":"echo keep","timeout":10}}]}}],"PreToolUse":[{{"matcher":"Read","hooks":[{{"type":"command","command":"echo read","timeout":10}}]}}]}}}}"#, - command, + r#"{{"hooks":{{"SessionStart":[{{"hooks":[{{"type":"command","command":{},"timeout":10}},{{"type":"command","command":"echo keep","timeout":10}}]}}],"PreToolUse":[{{"matcher":"Read","hooks":[{{"type":"command","command":"echo read","timeout":10}}]}}]}}}}"#, + serde_json::to_string(&command).unwrap(), ), ) .unwrap(); fs::write( droid_dir.join("settings.json"), format!( - r#"{{"hooks":{{"SessionStart":[{{"hooks":[{{"type":"command","command":"{}","timeout":10}}]}}],"PostToolUse":[{{"matcher":"Edit","hooks":[{{"type":"command","command":"echo post","timeout":10}}]}}]}}}}"#, - command, + r#"{{"hooks":{{"SessionStart":[{{"hooks":[{{"type":"command","command":{},"timeout":10}}]}}],"PostToolUse":[{{"matcher":"Edit","hooks":[{{"type":"command","command":"echo post","timeout":10}}]}}]}}}}"#, + serde_json::to_string(&command).unwrap(), ), ) .unwrap(); @@ -2812,128 +2812,6 @@ fn bundled_integration_asset_versions_match_expected_versions() { } #[test] -fn bundled_integration_assets_report_session_refs() { - assert!(PI_EXTENSION_ASSET.contains("agent_session_path")); - assert!(PI_EXTENSION_ASSET.contains("agent_session_id")); - assert!(PI_EXTENSION_ASSET.contains("ctx?.mode !== \"tui\"")); - assert!(PI_EXTENSION_ASSET.contains("pane.report_agent_session")); - assert!(PI_EXTENSION_ASSET.contains("pane.report_agent\"")); - assert!(PI_EXTENSION_ASSET.contains("pi.on(\"agent_start\"")); - assert!(PI_EXTENSION_ASSET.contains("pi.on(\"agent_settled\"")); - assert!(!PI_EXTENSION_ASSET.contains("pi.on(\"session_shutdown\"")); - assert!(OMP_EXTENSION_ASSET.contains("agent_session_path")); - assert!(OMP_EXTENSION_ASSET.contains("agent_session_id")); - assert!(OMP_EXTENSION_ASSET.contains("ctx?.hasUI !== true")); - assert!(OMP_EXTENSION_ASSET.contains("pane.report_agent_session")); - assert!(OMP_EXTENSION_ASSET.contains("pane.report_agent\"")); - assert!(OMP_EXTENSION_ASSET.contains("pi.on(\"agent_start\"")); - assert!(OMP_EXTENSION_ASSET.contains("pi.on(\"agent_end\"")); - assert!(OMP_EXTENSION_ASSET.contains("pi.on(\"session_shutdown\"")); - assert!( - CLAUDE_HOOK_ASSET.contains("agent_session_id") - || CLAUDE_HOOK_ASSET.contains("--agent-session-id") - ); - assert!( - CLAUDE_HOOK_ASSET.contains("agent_session_path") - || CLAUDE_HOOK_ASSET.contains("--agent-session-path") - ); - assert!(CLAUDE_HOOK_ASSET.contains("agent_id")); - assert!( - CLAUDE_HOOK_ASSET.contains("session_start_source") - || CLAUDE_HOOK_ASSET.contains("--session-start-source") - ); - assert!( - CLAUDE_HOOK_ASSET.contains("pane.report_agent_session") - || CLAUDE_HOOK_ASSET.contains("report-agent-session") - ); - assert!(!CLAUDE_HOOK_ASSET.contains("\"state\": action")); - assert!(!CLAUDE_HOOK_ASSET.contains("pane.release_agent")); - assert!( - CODEX_HOOK_ASSET.contains("HERDR_HOOK_INPUT_FILE") - || CODEX_HOOK_ASSET.contains("In.ReadToEnd") - ); - assert!( - CODEX_HOOK_ASSET.contains("agent_session_id") - || CODEX_HOOK_ASSET.contains("--agent-session-id") - ); - assert!( - CODEX_HOOK_ASSET.contains("session_start_source") - || CODEX_HOOK_ASSET.contains("--session-start-source") - ); - assert!(CODEX_HOOK_ASSET.contains("CODEX_THREAD_ID")); - assert!( - CODEX_HOOK_ASSET.contains("pane.report_agent_session") - || CODEX_HOOK_ASSET.contains("report-agent-session") - ); - assert!(!CODEX_HOOK_ASSET.contains("\"state\": action")); - assert!(!CODEX_HOOK_ASSET.contains("pane.release_agent")); - assert!(KIMI_HOOK_ASSET.contains("source\": \"herdr:kimi")); - assert!(KIMI_HOOK_ASSET.contains("agent_session_id")); - assert!(KIMI_HOOK_ASSET.contains("method = \"pane.report_agent_session\"")); - assert!(KIMI_HOOK_ASSET.contains("params[\"session_start_source\"] = \"startup\"")); - assert!(KIMI_HOOK_ASSET.contains("method = \"pane.report_agent\"")); - assert!(KIMI_HOOK_ASSET.contains("params[\"state\"] = action")); - assert!(!KIMI_HOOK_ASSET.contains("pane.release_agent")); - assert!(COPILOT_HOOK_ASSET.contains("agent_session_id")); - assert!(COPILOT_HOOK_ASSET.contains("pane.report_agent_session")); - assert!(!COPILOT_HOOK_ASSET.contains("\"state\":")); - assert!(!COPILOT_HOOK_ASSET.contains("pane.release_agent")); - assert!(DEVIN_HOOK_ASSET.contains("HERDR_DEVIN_LIST_JSON")); - assert!(DEVIN_HOOK_ASSET.contains("\"method\": \"pane.report_agent_session\"")); - assert!(!DEVIN_HOOK_ASSET.contains("\"method\": \"pane.report_agent\"")); - assert!(!DEVIN_HOOK_ASSET.contains("\"state\":")); - assert!(!DEVIN_HOOK_ASSET.contains("pane.release_agent")); - assert!(DEVIN_HOOK_ASSET.contains("agent_session_id")); - assert!(DROID_HOOK_ASSET.contains("agent_session_id")); - assert!(DROID_HOOK_ASSET.contains("pane.report_agent_session")); - assert!(!DROID_HOOK_ASSET.contains("\"state\": action")); - assert!(!DROID_HOOK_ASSET.contains("pane.release_agent")); - assert!(OPENCODE_PLUGIN_ASSET.contains("properties?.sessionID")); - assert!(OPENCODE_PLUGIN_ASSET.contains("params.agent_session_id = sessionID")); - assert!(OPENCODE_PLUGIN_ASSET.contains("pane.report_agent_session")); - assert!(OPENCODE_PLUGIN_ASSET.contains("reportState")); - assert!(!OPENCODE_PLUGIN_ASSET.contains("pane.release_agent")); - assert!(KILO_PLUGIN_ASSET.contains("SOURCE = \"herdr:kilo\"")); - assert!(KILO_PLUGIN_ASSET.contains("AGENT = \"kilo\"")); - assert!(KILO_PLUGIN_ASSET.contains("pane.report_agent_session")); - assert!(KILO_PLUGIN_ASSET.contains("session_start_source: \"startup\"")); - assert!(KILO_PLUGIN_ASSET.contains("reportState")); - assert!(!KILO_PLUGIN_ASSET.contains("pane.release_agent")); - assert!(QODERCLI_HOOK_ASSET.contains("HERDR_PANE_ID")); - assert!(QODERCLI_HOOK_ASSET.contains("session_id")); - assert!(QODERCLI_HOOK_ASSET.contains("report-agent-session")); - assert!(QODERCLI_HOOK_ASSET.contains("--agent-session-id")); - assert!(!QODERCLI_HOOK_ASSET.contains("report-agent\"")); - assert!(!QODERCLI_HOOK_ASSET.contains("release-agent")); - assert!(CURSOR_HOOK_ASSET.contains("HERDR_INTEGRATION_ID=cursor")); - assert!(CURSOR_HOOK_ASSET.contains("conversation_id")); - assert!(CURSOR_HOOK_ASSET.contains("conversationId")); - assert!(CURSOR_HOOK_ASSET.contains("sessionId")); - assert!(CURSOR_HOOK_ASSET.contains("agent_session_id")); - assert!(CURSOR_HOOK_ASSET.contains("pane.report_agent_session")); - assert!(CURSOR_HOOK_ASSET.contains("hook_event_name")); - assert!(CURSOR_HOOK_ASSET.contains("sessionStart")); - assert!(!CURSOR_HOOK_ASSET.contains("\"state\":")); - assert!(!CURSOR_HOOK_ASSET.contains("pane.release_agent")); - assert!(MASTRACODE_HOOK_ASSET.contains("HERDR_INTEGRATION_ID=mastracode")); - assert!(MASTRACODE_HOOK_ASSET.contains("HERDR_INTEGRATION_VERSION=2")); - assert!(MASTRACODE_HOOK_ASSET.contains("session_id")); - assert!(!MASTRACODE_HOOK_ASSET.contains("run_id")); - assert!(MASTRACODE_HOOK_ASSET.contains("agent_session_id")); - assert!(MASTRACODE_HOOK_ASSET.contains("pane.report_agent_session")); - assert!(MASTRACODE_HOOK_ASSET.contains("session_start_source")); - assert!(MASTRACODE_HOOK_ASSET.contains("pane.report_agent")); - assert!(GROK_HOOK_ASSET.contains("HERDR_INTEGRATION_ID=grok")); - assert!(GROK_HOOK_ASSET.contains("GROK_SESSION_ID")); - assert!(GROK_HOOK_ASSET.contains("sessionId")); - assert!(GROK_HOOK_ASSET.contains("agent_session_id")); - assert!(GROK_HOOK_ASSET.contains("pane.report_agent_session")); - assert!(GROK_HOOK_ASSET.contains("herdr:grok")); - assert!(!GROK_HOOK_ASSET.contains("\"state\":")); - assert!(!GROK_HOOK_ASSET.contains("pane.release_agent")); -} - -#[test] fn process_owned_integration_assets_do_not_report_release() { for (name, asset) in [ ("pi", PI_EXTENSION_ASSET), @@ -4061,7 +3939,7 @@ fn install_antigravity_cli_rewrites_stale_herdr_block() { assert!(entries[0] .get("command") .and_then(Value::as_str) - .is_some_and(|command| command.contains("herdr-agent-state"))); + .is_some_and(|command| command != "stale" && command != "stale idle")); std::env::remove_var(ANTIGRAVITY_CLI_CONFIG_DIR_ENV_VAR); let _ = fs::remove_dir_all(base); diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index ecc12ed5..a804a7fb 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -4681,10 +4681,18 @@ mod tests { unicode: u16::from(b'/'), control_key_state: 0x0010, }); + #[cfg(windows)] + let legacy_expected = b"\x1b[55;8;47;1;16;3_".as_slice(); + #[cfg(not(windows))] + let legacy_expected = b"///".as_slice(); assert_eq!( pane.encode_terminal_key(shifted.clone(), crate::input::KeyboardProtocol::Legacy,), - b"///" + legacy_expected ); + let mut terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); + terminal.write(b"\x1b[>15u"); + let (tx, _rx) = mpsc::channel(4); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); assert_eq!( pane.encode_terminal_key(shifted, crate::input::KeyboardProtocol::Kitty { flags: 15 },), b"\x1b[47;2:1u\x1b[47;2:2u\x1b[47;2:2u" diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f15c71ef..402b695d 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -227,14 +227,6 @@ mod unix_common; #[cfg(unix)] pub(crate) use unix_common::{begin_cli_output, end_cli_output}; -#[cfg(not(windows))] -pub(crate) fn replace_file( - source: &std::path::Path, - destination: &std::path::Path, -) -> std::io::Result<()> { - std::fs::rename(source, destination) -} - #[cfg(not(unix))] pub(crate) fn begin_cli_output() {} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 8d370964..10fbb6f0 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -19,39 +19,6 @@ pub(super) fn read_terminal_grid_size() -> std::io::Result<(u16, u16)> { crossterm::terminal::size() } -pub(crate) fn replace_file( - source: &std::path::Path, - destination: &std::path::Path, -) -> std::io::Result<()> { - use std::os::windows::ffi::OsStrExt; - use windows_sys::Win32::Storage::FileSystem::{ - MoveFileExW, MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH, - }; - - let source = source - .as_os_str() - .encode_wide() - .chain(std::iter::once(0)) - .collect::<Vec<_>>(); - let destination = destination - .as_os_str() - .encode_wide() - .chain(std::iter::once(0)) - .collect::<Vec<_>>(); - let moved = unsafe { - MoveFileExW( - source.as_ptr(), - destination.as_ptr(), - MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH, - ) - }; - if moved == 0 { - Err(std::io::Error::last_os_error()) - } else { - Ok(()) - } -} - pub(crate) fn set_default_plugin_pane_pwd( _env: &mut Vec<(String, String)>, _cwd: &std::path::Path, diff --git a/src/server/headless/tests/mod.rs b/src/server/headless/tests/mod.rs index 1e4249b5..e7e73e8b 100644 --- a/src/server/headless/tests/mod.rs +++ b/src/server/headless/tests/mod.rs @@ -18,7 +18,7 @@ fn test_headless_server() -> HeadlessServer { fn test_headless_server_with_event_hub(event_hub: api::EventHub) -> HeadlessServer { let config = crate::config::Config::default(); let (_api_tx, api_rx) = tokio::sync::mpsc::unbounded_channel(); - let app = crate::app::App::new( + let mut app = crate::app::App::new( &config, crate::app::AppPolicy::TEST, None, @@ -26,6 +26,7 @@ fn test_headless_server_with_event_hub(event_hub: api::EventHub) -> HeadlessServ event_hub, ); + app.state.default_shell = crate::app::exiting_test_command().into(); let dir = std::env::temp_dir().join(format!( "hh-{}-{}", std::process::id(), @@ -619,6 +620,9 @@ async fn client_shell_attach_seeds_workspace() { #[tokio::test] async fn client_shell_endpoint_request_uses_the_selected_connection() { let mut server = test_headless_server(); + server.app.state.workspaces = vec![crate::workspace::Workspace::test_new("endpoint")]; + server.app.state.ensure_test_terminals(); + server.app.state.active = Some(0); let (writer, control_rx, _render_rx) = test_client_writer(); let client_id = 41; assert!( diff --git a/src/terminal/metadata.rs b/src/terminal/metadata.rs index 2a912b27..91b1d582 100644 --- a/src/terminal/metadata.rs +++ b/src/terminal/metadata.rs @@ -881,11 +881,12 @@ mod tests { }); assert_eq!(terminal.next_agent_metadata_expiry(), Some(old_deadline)); - assert_eq!( - terminal.effective_presentation().title.as_deref(), - Some("Prompt title") + let presentation = terminal.effective_presentation_for_state_at( + terminal.state, + old_deadline - Duration::from_millis(1), ); - assert_eq!(terminal.effective_presentation().display_agent, None); + assert_eq!(presentation.title.as_deref(), Some("Prompt title")); + assert_eq!(presentation.display_agent, None); let mutation = terminal .expire_agent_metadata_at(old_deadline, old_deadline) |
