diff --git a/.codex/skills/remote-tests/SKILL.md b/.codex/skills/remote-tests/SKILL.md index f0253370d..7596535b8 100644 --- a/.codex/skills/remote-tests/SKILL.md +++ b/.codex/skills/remote-tests/SKILL.md @@ -11,7 +11,15 @@ Docker container is built and initialized via ./scripts/test-remote-env.sh On x86-64 Linux, run Wine exec with `bazel test //codex-rs/core:core-all-wine-exec-test --test_output=errors`. -Temporary blockers belong beside the test in `skip_if_wine_exec!` calls. + +Local execution targets the host OS, Docker targets Linux, and Wine exec targets +Windows. Choose the skip macro by what the test depends on: + +- `skip_if_target_windows!`: Windows target behavior. +- `skip_if_host_windows!`: Windows host constraints. +- `skip_if_remote!`: Local-only test behavior. +- `skip_if_no_remote_env!`: Remote-only test behavior. +- `skip_if_wine_exec!`: Wine-specific runner debt. You can list devboxes via `applied_devbox ls`, pick the one with `codex` in the name. Connect to devbox via `ssh `. diff --git a/codex-rs/app-server/tests/suite/v2/hooks_list.rs b/codex-rs/app-server/tests/suite/v2/hooks_list.rs index 1fd1340a6..510ed324b 100644 --- a/codex-rs/app-server/tests/suite/v2/hooks_list.rs +++ b/codex-rs/app-server/tests/suite/v2/hooks_list.rs @@ -25,7 +25,7 @@ use codex_app_server_protocol::UserInput as V2UserInput; use codex_core::config::set_project_trust_level; use codex_protocol::config_types::TrustLevel; use codex_utils_absolute_path::AbsolutePathBuf; -use core_test_support::skip_if_windows; +use core_test_support::skip_if_host_windows; use pretty_assertions::assert_eq; use serde::Serialize; use tempfile::TempDir; @@ -651,7 +651,7 @@ async fn config_batch_write_toggles_user_hook() -> Result<()> { #[tokio::test] async fn config_batch_write_updates_hook_trust_for_loaded_session() -> Result<()> { - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let responses = vec![ create_final_assistant_message_sse_response("Warmup")?, @@ -902,7 +902,7 @@ command = "python3 {hook_script_path}" #[tokio::test] async fn config_batch_write_disables_hook_for_loaded_session() -> Result<()> { - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let responses = vec![ create_final_assistant_message_sse_response("Warmup")?, diff --git a/codex-rs/core/README.md b/codex-rs/core/README.md index e992059b5..278c614df 100644 --- a/codex-rs/core/README.md +++ b/codex-rs/core/README.md @@ -5,8 +5,16 @@ This crate implements the business logic for Codex. It is designed to be used by ## Wine-exec integration tests On x86-64 Linux, run the shared suite against the Windows exec server with -`bazel test //codex-rs/core:core-all-wine-exec-test`. Temporary blockers use a -source-local `skip_if_wine_exec!` call and reason. +`bazel test //codex-rs/core:core-all-wine-exec-test`. + +Local execution targets the host OS, Docker targets Linux, and Wine exec targets +Windows. Choose the skip macro by what the test depends on: + +- `skip_if_target_windows!`: Windows target behavior. +- `skip_if_host_windows!`: Windows host constraints. +- `skip_if_remote!`: Local-only test behavior. +- `skip_if_no_remote_env!`: Remote-only test behavior. +- `skip_if_wine_exec!`: Wine-specific runner debt. ## Dependencies diff --git a/codex-rs/core/src/unified_exec/mod_tests.rs b/codex-rs/core/src/unified_exec/mod_tests.rs index 2dc0afd26..54ee788e3 100644 --- a/codex-rs/core/src/unified_exec/mod_tests.rs +++ b/codex-rs/core/src/unified_exec/mod_tests.rs @@ -23,7 +23,7 @@ use codex_sandboxing::SandboxType; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_output_truncation::TruncationPolicy; use codex_utils_output_truncation::approx_token_count; -use core_test_support::get_remote_test_env; +use core_test_support::skip_if_no_remote_env; use core_test_support::skip_if_sandbox; use core_test_support::test_codex::test_env as remote_test_env; use pretty_assertions::assert_eq; @@ -836,9 +836,7 @@ async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_uses_remote_exec_server_when_configured() -> anyhow::Result<()> { skip_if_sandbox!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let remote_test_env = remote_test_env().await?; let (_, turn) = make_session_and_context().await; @@ -888,9 +886,7 @@ async fn unified_exec_uses_remote_exec_server_when_configured() -> anyhow::Resul #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_exec_server_rejects_inherited_fd_launches() -> anyhow::Result<()> { skip_if_sandbox!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let remote_test_env = remote_test_env().await?; let (_, mut turn) = make_session_and_context().await; diff --git a/codex-rs/core/tests/common/lib.rs b/codex-rs/core/tests/common/lib.rs index 523d7f854..29ed4875e 100644 --- a/codex-rs/core/tests/common/lib.rs +++ b/codex-rs/core/tests/common/lib.rs @@ -36,9 +36,15 @@ mod test_environment; pub mod tracing; pub mod zsh_fork; -pub use test_environment::TestEnvironment; -pub use test_environment::get_remote_test_env; -pub use test_environment::test_environment; +pub(crate) use test_environment::TestEnvironment; +pub use test_environment::TestTargetOs; +pub use test_environment::is_remote_test_environment; +#[doc(hidden)] +pub use test_environment::is_wine_exec_test_environment; +#[doc(hidden)] +pub use test_environment::test_docker_container_name; +pub(crate) use test_environment::test_environment; +pub use test_environment::test_target_os; static TEST_ARG0_PATH_ENTRY: OnceLock> = OnceLock::new(); @@ -574,21 +580,18 @@ macro_rules! skip_if_no_network { } // Exported so the public skip macros can expand in downstream test crates. -// Call `skip_if_remote!` or `skip_if_wine_exec!` instead. #[macro_export] #[doc(hidden)] -macro_rules! skip_if_test_environment { - ($pattern:pat, $reason:expr $(,)?) => {{ - let environment = $crate::test_environment(); - if ::std::matches!(&environment, $pattern) { - eprintln!("Skipping test in {environment:?}: {}", $reason); +macro_rules! skip_if_test_condition { + ($condition:expr, $environment:expr, $reason:expr $(,)?) => {{ + if $condition { + eprintln!("Skipping test in {}: {}", $environment, $reason); return; } }}; - ($return_value:expr, $pattern:pat, $reason:expr $(,)?) => {{ - let environment = $crate::test_environment(); - if ::std::matches!(&environment, $pattern) { - eprintln!("Skipping test in {environment:?}: {}", $reason); + ($return_value:expr, $condition:expr, $environment:expr, $reason:expr $(,)?) => {{ + if $condition { + eprintln!("Skipping test in {}: {}", $environment, $reason); return $return_value; } }}; @@ -597,29 +600,71 @@ macro_rules! skip_if_test_environment { #[macro_export] macro_rules! skip_if_remote { ($reason:expr $(,)?) => {{ - $crate::skip_if_test_environment!( - $crate::TestEnvironment::Docker { .. } | $crate::TestEnvironment::WineExec, + $crate::skip_if_test_condition!( + $crate::is_remote_test_environment(), + "a remote test environment", $reason, ); }}; ($return_value:expr, $reason:expr $(,)?) => {{ - $crate::skip_if_test_environment!( + $crate::skip_if_test_condition!( $return_value, - $crate::TestEnvironment::Docker { .. } | $crate::TestEnvironment::WineExec, + $crate::is_remote_test_environment(), + "a remote test environment", $reason, ); }}; } +#[macro_export] +macro_rules! skip_if_no_remote_env { + () => {{ + if !$crate::is_remote_test_environment() { + eprintln!("Skipping test because it requires a remote test environment."); + return; + } + }}; + ($return_value:expr $(,)?) => {{ + if !$crate::is_remote_test_environment() { + eprintln!("Skipping test because it requires a remote test environment."); + return $return_value; + } + }}; +} + #[macro_export] macro_rules! skip_if_wine_exec { ($reason:expr $(,)?) => {{ - $crate::skip_if_test_environment!($crate::TestEnvironment::WineExec, $reason); + $crate::skip_if_test_condition!( + $crate::is_wine_exec_test_environment(), + "the Wine-exec test environment", + $reason, + ); }}; ($return_value:expr, $reason:expr $(,)?) => {{ - $crate::skip_if_test_environment!( + $crate::skip_if_test_condition!( $return_value, - $crate::TestEnvironment::WineExec, + $crate::is_wine_exec_test_environment(), + "the Wine-exec test environment", + $reason, + ); + }}; +} + +#[macro_export] +macro_rules! skip_if_target_windows { + ($reason:expr $(,)?) => {{ + $crate::skip_if_test_condition!( + $crate::test_target_os() == $crate::TestTargetOs::Windows, + "a Windows target environment", + $reason, + ); + }}; + ($return_value:expr, $reason:expr $(,)?) => {{ + $crate::skip_if_test_condition!( + $return_value, + $crate::test_target_os() == $crate::TestTargetOs::Windows, + "a Windows target environment", $reason, ); }}; @@ -662,7 +707,7 @@ macro_rules! codex_linux_sandbox_exe_or_skip { } #[macro_export] -macro_rules! skip_if_windows { +macro_rules! skip_if_host_windows { ($return_value:expr $(,)?) => {{ if cfg!(target_os = "windows") { println!("Skipping test because it cannot execute on Windows."); diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 8784a4d4a..5a23da417 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -59,13 +59,13 @@ use wiremock::MockServer; use crate::TempDirExt; use crate::TestEnvironment; -use crate::get_remote_test_env; use crate::load_default_config_for_test; use crate::load_default_config_for_test_with_cloud_config_bundle; use crate::responses::WebSocketTestServer; use crate::responses::output_value_to_text; use crate::responses::start_mock_server; use crate::streaming_sse::StreamingSseServer; +use crate::test_environment; use crate::wait_for_event_match; use crate::wait_for_event_with_timeout; use wiremock::Match; @@ -163,8 +163,8 @@ impl Drop for TestEnv { } pub async fn test_env() -> Result { - match get_remote_test_env() { - Some(remote_env) => { + match test_environment() { + remote_env @ (TestEnvironment::Docker { .. } | TestEnvironment::WineExec) => { let websocket_url = remote_exec_server_url()?; let environment = codex_exec_server::Environment::create_for_tests(Some(websocket_url.clone()))?; @@ -202,7 +202,7 @@ pub async fn test_env() -> Result { remote_container_name: remote_env.docker_container_name().map(str::to_owned), }) } - None => TestEnv::local().await, + TestEnvironment::Local => TestEnv::local().await, } } diff --git a/codex-rs/core/tests/common/test_environment.rs b/codex-rs/core/tests/common/test_environment.rs index ad2a335f5..b170da1d3 100644 --- a/codex-rs/core/tests/common/test_environment.rs +++ b/codex-rs/core/tests/common/test_environment.rs @@ -9,25 +9,61 @@ pub const TEST_ENVIRONMENT_ENV_VAR: &str = "CODEX_TEST_ENVIRONMENT"; pub const LEGACY_REMOTE_ENV_ENV_VAR: &str = "CODEX_TEST_REMOTE_ENV"; pub const DOCKER_CONTAINER_ENV_VAR: &str = "CODEX_TEST_REMOTE_ENV_CONTAINER_NAME"; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TestTargetOs { + Linux, + MacOs, + Windows, +} + +impl TestTargetOs { + const fn host() -> Self { + if cfg!(target_os = "macos") { + Self::MacOs + } else if cfg!(target_os = "windows") { + Self::Windows + } else if cfg!(target_os = "linux") { + Self::Linux + } else { + unreachable!() + } + } + + const fn path_convention(self) -> PathConvention { + match self { + Self::Linux | Self::MacOs => PathConvention::Posix, + Self::Windows => PathConvention::Windows, + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] -pub enum TestEnvironment { +pub(crate) enum TestEnvironment { Local, Docker { container_name: String }, WineExec, } impl TestEnvironment { - pub fn is_remote(&self) -> bool { + pub(crate) fn is_remote(&self) -> bool { !matches!(self, Self::Local) } - pub fn docker_container_name(&self) -> Option<&str> { + pub(crate) fn docker_container_name(&self) -> Option<&str> { match self { Self::Docker { container_name } => Some(container_name), Self::Local | Self::WineExec => None, } } + pub(crate) const fn target_os(&self) -> TestTargetOs { + match self { + Self::Local => TestTargetOs::host(), + Self::Docker { .. } => TestTargetOs::Linux, + Self::WineExec => TestTargetOs::Windows, + } + } + pub(crate) fn remote_cwd(&self, instance_id: &str) -> Result> { let path_uri = match self { Self::Local => return Ok(None), @@ -47,15 +83,11 @@ impl TestEnvironment { } pub(crate) fn path_convention(&self) -> PathConvention { - match self { - Self::Local => PathConvention::native(), - Self::Docker { .. } => PathConvention::Posix, - Self::WineExec => PathConvention::Windows, - } + self.target_os().path_convention() } } -pub fn test_environment() -> TestEnvironment { +pub(crate) fn test_environment() -> TestEnvironment { let environment = parse_test_environment( std::env::var_os(TEST_ENVIRONMENT_ENV_VAR).as_deref(), std::env::var_os(LEGACY_REMOTE_ENV_ENV_VAR).as_deref(), @@ -70,9 +102,29 @@ pub fn test_environment() -> TestEnvironment { environment } -pub fn get_remote_test_env() -> Option { - let environment = test_environment(); - environment.is_remote().then_some(environment) +/// Returns the operating system used by the selected test execution environment. +pub fn test_target_os() -> TestTargetOs { + test_environment().target_os() +} + +/// Returns whether the selected test execution environment is remote. +pub fn is_remote_test_environment() -> bool { + test_environment().is_remote() +} + +/// Returns the selected Docker test container, when the harness requires direct access to it. +#[doc(hidden)] +pub fn test_docker_container_name() -> Option { + match test_environment() { + TestEnvironment::Docker { container_name } => Some(container_name), + TestEnvironment::Local | TestEnvironment::WineExec => None, + } +} + +/// Returns whether the Wine-backed executor is selected. +#[doc(hidden)] +pub fn is_wine_exec_test_environment() -> bool { + matches!(test_environment(), TestEnvironment::WineExec) } fn parse_test_environment( diff --git a/codex-rs/core/tests/common/test_environment_tests.rs b/codex-rs/core/tests/common/test_environment_tests.rs index 9c16ff85b..eaaa225d5 100644 --- a/codex-rs/core/tests/common/test_environment_tests.rs +++ b/codex-rs/core/tests/common/test_environment_tests.rs @@ -116,3 +116,30 @@ fn rejects_invalid_or_incomplete_configuration() { )) ); } + +#[test] +fn derives_target_operating_system_and_placement() { + #[cfg(target_os = "linux")] + let expected_local_target_os = TestTargetOs::Linux; + #[cfg(target_os = "macos")] + let expected_local_target_os = TestTargetOs::MacOs; + #[cfg(target_os = "windows")] + let expected_local_target_os = TestTargetOs::Windows; + + let environments = [ + TestEnvironment::Local, + TestEnvironment::Docker { + container_name: "container-1".to_string(), + }, + TestEnvironment::WineExec, + ]; + + assert_eq!( + environments.map(|environment| (environment.target_os(), environment.is_remote())), + [ + (expected_local_target_os, false), + (TestTargetOs::Linux, true), + (TestTargetOs::Windows, true), + ] + ); +} diff --git a/codex-rs/core/tests/suite/agents_md.rs b/codex-rs/core/tests/suite/agents_md.rs index 9094461aa..d26e53942 100644 --- a/codex-rs/core/tests/suite/agents_md.rs +++ b/codex-rs/core/tests/suite/agents_md.rs @@ -16,7 +16,6 @@ use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; use core_test_support::PathBufExt; use core_test_support::create_directory_symlink; -use core_test_support::get_remote_test_env; use core_test_support::load_default_config_for_test; use core_test_support::responses; use core_test_support::responses::ev_completed; @@ -25,6 +24,7 @@ use core_test_support::responses::mount_sse_once; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; +use core_test_support::skip_if_no_remote_env; use core_test_support::test_codex::RecordingUserInstructionsProvider; use core_test_support::test_codex::TestCodexBuilder; use core_test_support::test_codex::test_codex; @@ -597,9 +597,7 @@ async fn fresh_thread_composes_global_before_project_and_reports_sources() -> Re #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn multi_environment_thread_loads_every_project_and_keeps_creation_snapshot() -> Result<()> { skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let server = responses::start_mock_server().await; let response_mock = responses::mount_sse_sequence( diff --git a/codex-rs/core/tests/suite/apply_patch_cli.rs b/codex-rs/core/tests/suite/apply_patch_cli.rs index c348dd6f9..c2fca62aa 100644 --- a/codex-rs/core/tests/suite/apply_patch_cli.rs +++ b/codex-rs/core/tests/suite/apply_patch_cli.rs @@ -38,7 +38,6 @@ use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; use core_test_support::PathBufExt; use core_test_support::assert_regex_match; -use core_test_support::get_remote_test_env; use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_function_call; @@ -48,8 +47,9 @@ use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; +use core_test_support::skip_if_no_remote_env; use core_test_support::skip_if_remote; -use core_test_support::skip_if_wine_exec; +use core_test_support::skip_if_target_windows; use core_test_support::test_codex::TestCodexBuilder; use core_test_support::test_codex::TestCodexHarness; use core_test_support::test_codex::local; @@ -911,7 +911,7 @@ async fn apply_patch_cli_preserves_existing_hard_link_outside_workspace() -> Res #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn apply_patch_cli_rejects_move_path_traversal_outside_workspace() -> Result<()> { // TODO(anp): Remove after apply-patch fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "asserts POSIX workspace traversal behavior"); + skip_if_target_windows!(Ok(()), "asserts POSIX workspace traversal behavior"); skip_if_no_network!(Ok(())); let harness = apply_patch_harness().await?; @@ -1568,14 +1568,12 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Result<()> { // TODO(anp): Remove after shared-cwd helpers use target-native paths. - skip_if_wine_exec!( + skip_if_target_windows!( Ok(()), "requires a cwd valid in local POSIX and remote Windows environments" ); skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let mut builder = test_codex(); diff --git a/codex-rs/core/tests/suite/hooks.rs b/codex-rs/core/tests/suite/hooks.rs index 71c0acfe8..a5c82d576 100644 --- a/codex-rs/core/tests/suite/hooks.rs +++ b/codex-rs/core/tests/suite/hooks.rs @@ -38,8 +38,8 @@ use core_test_support::responses::mount_sse_once; use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; +use core_test_support::skip_if_host_windows; use core_test_support::skip_if_no_network; -use core_test_support::skip_if_windows; use core_test_support::streaming_sse::StreamingSseChunk; use core_test_support::streaming_sse::start_streaming_sse_server; use core_test_support::test_codex::test_codex; @@ -4012,7 +4012,7 @@ async fn post_tool_use_spills_large_feedback_message() -> Result<()> { #[tokio::test] async fn post_tool_use_blocks_when_exec_session_completes_via_write_stdin() -> Result<()> { skip_if_no_network!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; let start_call_id = "posttooluse-exec-session-start"; diff --git a/codex-rs/core/tests/suite/network_approval.rs b/codex-rs/core/tests/suite/network_approval.rs index e644fcf6b..6b96c0d63 100644 --- a/codex-rs/core/tests/suite/network_approval.rs +++ b/codex-rs/core/tests/suite/network_approval.rs @@ -22,7 +22,6 @@ use codex_protocol::user_input::UserInput; use codex_utils_path_uri::PathUri; use core_test_support::PathBufExt; use core_test_support::PathExt; -use core_test_support::get_remote_test_env; use core_test_support::managed_network_requirements_loader; use core_test_support::responses::ResponseMock; use core_test_support::responses::ev_assistant_message; @@ -32,10 +31,11 @@ use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; +use core_test_support::skip_if_host_windows; use core_test_support::skip_if_no_network; +use core_test_support::skip_if_no_remote_env; use core_test_support::skip_if_sandbox; -use core_test_support::skip_if_windows; -use core_test_support::skip_if_wine_exec; +use core_test_support::skip_if_target_windows; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::local; use core_test_support::test_codex::test_codex; @@ -58,13 +58,11 @@ const NETWORK_TEST_TARGET: &str = "http://codex-network-test.invalid:80"; #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn approved_network_host_for_one_environment_still_prompts_in_another() -> Result<()> { - skip_if_wine_exec!(Ok(()), "uses the POSIX/Python network fixture"); + skip_if_target_windows!(Ok(()), "uses the POSIX/Python network fixture"); + skip_if_host_windows!(Ok(())); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let test = managed_network_unified_exec_test(&server).await?; diff --git a/codex-rs/core/tests/suite/remote_env.rs b/codex-rs/core/tests/suite/remote_env.rs index 619678159..221757f67 100644 --- a/codex-rs/core/tests/suite/remote_env.rs +++ b/codex-rs/core/tests/suite/remote_env.rs @@ -34,8 +34,7 @@ use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; use core_test_support::PathBufExt; use core_test_support::PathExt; -use core_test_support::TestEnvironment; -use core_test_support::get_remote_test_env; +use core_test_support::TestTargetOs; use core_test_support::responses::ev_apply_patch_custom_tool_call; use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; @@ -47,11 +46,14 @@ use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; -use core_test_support::skip_if_wine_exec; +use core_test_support::skip_if_no_remote_env; +use core_test_support::skip_if_target_windows; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::local; use core_test_support::test_codex::test_codex; use core_test_support::test_codex::test_env; +use core_test_support::test_docker_container_name; +use core_test_support::test_target_os; use core_test_support::wait_for_event; use core_test_support::wait_for_event_match; use futures::SinkExt; @@ -167,9 +169,7 @@ async fn wait_for_completion_without_patch_approval(test: &TestCodex) { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_test_env_can_connect_and_use_filesystem() -> Result<()> { - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let test_env = test_env().await?; let file_system = test_env.environment().get_filesystem(); @@ -202,9 +202,7 @@ async fn remote_test_env_can_connect_and_use_filesystem() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_test_env_exposes_target_shell_to_model() -> Result<()> { - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let response_mock = mount_sse_once( @@ -228,10 +226,10 @@ async fn remote_test_env_exposes_target_shell_to_model() -> Result<()> { .context("environment context should be model visible")?; // TODO(anp): Assert Wine-exec exposes a `C:\\...` cwd after model-visible paths preserve // target-native spelling instead of the Linux orchestrator's `/C:/...` representation. - let expected_shell = match core_test_support::test_environment() { - TestEnvironment::Docker { .. } => "bash", - TestEnvironment::WineExec => "powershell", - TestEnvironment::Local => unreachable!("test requires a remote environment"), + let expected_shell = match test_target_os() { + TestTargetOs::Linux => "bash", + TestTargetOs::Windows => "powershell", + TestTargetOs::MacOs => unreachable!("remote test targets do not run macOS"), }; assert_eq!( environment_context @@ -248,16 +246,18 @@ async fn remote_test_env_exposes_target_shell_to_model() -> Result<()> { async fn explicit_remote_shell_runs_in_remote_cwd() -> Result<()> { const CALL_ID: &str = "remote-explicit-shell"; - let (shell, command) = match core_test_support::test_environment() { - TestEnvironment::Docker { .. } => ( + skip_if_no_remote_env!(Ok(())); + + let (shell, command) = match test_target_os() { + TestTargetOs::Linux => ( "bash", r#"case "$PWD" in /tmp/codex-core-test-cwd-*) ;; *) echo "unexpected cwd: $PWD" >&2; exit 1 ;; esac"#, ), - TestEnvironment::WineExec => ( + TestTargetOs::Windows => ( "powershell", r#"$cwd = (Get-Location).Path; if ($cwd -notlike 'C:\codex-core-test-cwd-*') { Write-Error "unexpected cwd: $cwd"; exit 1 }"#, ), - TestEnvironment::Local => return Ok(()), + TestTargetOs::MacOs => unreachable!("remote test targets do not run macOS"), }; let server = start_mock_server().await; @@ -771,12 +771,10 @@ fn assert_normalized_path_rejected(error: &std::io::Error) { } fn remote_exec(script: &str) -> Result<()> { - let remote_env = get_remote_test_env().context("remote env should be configured")?; - let container_name = remote_env - .docker_container_name() + let container_name = test_docker_container_name() .context("test requires direct access to the Docker container")?; let output = Command::new("docker") - .args(["exec", container_name, "sh", "-lc", script]) + .args(["exec", container_name.as_str(), "sh", "-lc", script]) .output()?; assert!( output.status.success(), @@ -823,10 +821,8 @@ async fn exec_command_routing_output( async fn exec_command_routes_to_selected_remote_environment() -> Result<()> { skip_if_no_network!(Ok(())); // TODO(anp): Remove after remote path fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "requires the Docker-backed POSIX executor"); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_target_windows!(Ok(()), "requires the Docker-backed POSIX executor"); + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let test = unified_exec_test(&server).await?; @@ -900,10 +896,8 @@ async fn exec_command_routes_to_selected_remote_environment() -> Result<()> { async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result<()> { skip_if_no_network!(Ok(())); // TODO(anp): Remove after remote path fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "requires the Docker-backed POSIX executor"); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_target_windows!(Ok(()), "requires the Docker-backed POSIX executor"); + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let mut builder = test_codex().with_config(|config| { @@ -1107,10 +1101,8 @@ async fn remote_request_permissions_grant_unblocks_later_remote_exec() -> Result async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result<()> { skip_if_no_network!(Ok(())); // TODO(anp): Remove after remote path fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "requires the Docker-backed POSIX executor"); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_target_windows!(Ok(()), "requires the Docker-backed POSIX executor"); + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let mut builder = test_codex(); @@ -1195,10 +1187,8 @@ async fn apply_patch_freeform_routes_to_selected_remote_environment() -> Result< async fn apply_patch_approvals_are_remembered_per_environment() -> Result<()> { skip_if_no_network!(Ok(())); // TODO(anp): Remove after remote path fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "requires the Docker-backed POSIX executor"); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_target_windows!(Ok(()), "requires the Docker-backed POSIX executor"); + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let mut builder = test_codex().with_config(|config| { @@ -1386,10 +1376,8 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm { skip_if_no_network!(Ok(())); // TODO(anp): Remove after remote path fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "requires the Docker-backed POSIX executor"); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_target_windows!(Ok(()), "requires the Docker-backed POSIX executor"); + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let test = unified_exec_test(&server).await?; @@ -1482,11 +1470,9 @@ async fn apply_patch_intercepted_exec_command_routes_to_selected_remote_environm #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_test_env_sandboxed_read_allows_readable_root() -> Result<()> { // TODO(anp): Remove after remote sandbox fixtures use target-native paths. - skip_if_wine_exec!(Ok(()), "requires the Docker-backed POSIX executor"); + skip_if_target_windows!(Ok(()), "requires the Docker-backed POSIX executor"); skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let test_env = test_env().await?; let file_system = test_env.environment().get_filesystem(); @@ -1532,11 +1518,9 @@ async fn remote_test_env_sandboxed_read_allows_readable_root() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_test_env_sandboxed_read_rejects_symlink_parent_dotdot_escape() -> Result<()> { - skip_if_wine_exec!(Ok(()), "tests POSIX symlink and parent traversal semantics"); + skip_if_target_windows!(Ok(()), "tests POSIX symlink and parent traversal semantics"); skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let test_env = test_env().await?; let file_system = test_env.environment().get_filesystem(); @@ -1568,11 +1552,9 @@ async fn remote_test_env_sandboxed_read_rejects_symlink_parent_dotdot_escape() - #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_test_env_remove_removes_symlink_not_target() -> Result<()> { - skip_if_wine_exec!(Ok(()), "tests POSIX symlink removal semantics"); + skip_if_target_windows!(Ok(()), "tests POSIX symlink removal semantics"); skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let test_env = test_env().await?; let file_system = test_env.environment().get_filesystem(); @@ -1642,11 +1624,9 @@ async fn remote_test_env_remove_removes_symlink_not_target() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn remote_test_env_copy_preserves_symlink_source() -> Result<()> { - skip_if_wine_exec!(Ok(()), "tests POSIX symlink copy semantics"); + skip_if_target_windows!(Ok(()), "tests POSIX symlink copy semantics"); skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let test_env = test_env().await?; let file_system = test_env.environment().get_filesystem(); @@ -1678,14 +1658,12 @@ async fn remote_test_env_copy_preserves_symlink_source() -> Result<()> { ) .await?; - let remote_env = get_remote_test_env().context("remote env should be configured")?; - let container_name = remote_env - .docker_container_name() + let container_name = test_docker_container_name() .context("test requires direct access to the Docker container")?; let link_target = Command::new("docker") .args([ "exec", - container_name, + container_name.as_str(), "readlink", copied_symlink .to_str() diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 8000ed1c0..05cc0793f 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -48,17 +48,19 @@ use codex_protocol::user_input::UserInput; use codex_utils_cargo_bin::cargo_bin; use codex_utils_path_uri::PathUri; use core_test_support::assert_regex_match; +use core_test_support::is_remote_test_environment; use core_test_support::responses; use core_test_support::responses::mount_models_once; use core_test_support::responses::mount_sse_once; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; +use core_test_support::skip_if_no_remote_env; use core_test_support::skip_if_wine_exec; use core_test_support::stdio_server_bin; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::test_codex; use core_test_support::test_codex::turn_permission_fields; -use core_test_support::test_environment; +use core_test_support::test_docker_container_name; use core_test_support::wait_for_event; use core_test_support::wait_for_mcp_server; use image::DynamicImage; @@ -165,7 +167,7 @@ enum McpCallEvent { const REMOTE_MCP_ENVIRONMENT: &str = "remote"; fn remote_aware_environment_id() -> String { - if test_environment().is_remote() { + if is_remote_test_environment() { REMOTE_MCP_ENVIRONMENT.to_string() } else { codex_config::DEFAULT_MCP_SERVER_ENVIRONMENT_ID.to_string() @@ -181,8 +183,7 @@ fn remote_aware_environment_id() -> String { /// container and return that in-container path instead. fn remote_aware_stdio_server_bin() -> anyhow::Result { let bin = stdio_server_bin()?; - let environment = test_environment(); - let Some(container_name) = environment.docker_container_name() else { + let Some(container_name) = test_docker_container_name() else { return Ok(bin); }; @@ -195,7 +196,7 @@ fn remote_aware_stdio_server_bin() -> anyhow::Result { // path instead of the host build artifact path. // Several remote-aware MCP tests can run in parallel; give each copied // binary its own path so one test cannot replace another test's executable. - copy_binary_to_remote_env(container_name, Path::new(&bin), "test_stdio_server") + copy_binary_to_remote_env(&container_name, Path::new(&bin), "test_stdio_server") } /// Builds a collision-resistant in-container path for copied test binaries. @@ -506,7 +507,7 @@ fn assert_cwd_tool_output(structured: &Value, expected_cwd: &Path) { .and_then(Value::as_str) .expect("cwd tool should return a string cwd"); - if test_environment().is_remote() { + if is_remote_test_environment() { assert_eq!( structured, &json!({ @@ -1986,9 +1987,7 @@ async fn remote_stdio_env_var_source_does_not_copy_local_env() -> anyhow::Result "requires a Windows test_stdio_server in the Wine-exec environment" ); skip_if_no_network!(Ok(())); - if !test_environment().is_remote() { - return Ok(()); - } + skip_if_no_remote_env!(Ok(())); let server = responses::start_mock_server().await; let call_id = "call-remote-source"; @@ -2501,11 +2500,10 @@ async fn start_streamable_http_test_server( } }; - let environment = test_environment(); - if let Some(container_name) = environment.docker_container_name() { + if let Some(container_name) = test_docker_container_name() { return Ok(Some( start_remote_streamable_http_test_server( - container_name, + &container_name, &rmcp_http_server_bin, expected_env_value, expected_token, diff --git a/codex-rs/core/tests/suite/shell_command.rs b/codex-rs/core/tests/suite/shell_command.rs index 6c865425f..fd6befd85 100644 --- a/codex-rs/core/tests/suite/shell_command.rs +++ b/codex-rs/core/tests/suite/shell_command.rs @@ -8,8 +8,8 @@ use core_test_support::responses::ev_function_call; use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; +use core_test_support::skip_if_host_windows; use core_test_support::skip_if_no_network; -use core_test_support::skip_if_windows; use core_test_support::test_codex::TestCodexBuilder; use core_test_support::test_codex::TestCodexHarness; use core_test_support::test_codex::test_codex; @@ -181,7 +181,7 @@ async fn multi_line_output_with_login() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn pipe_output_with_login() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let harness = shell_command_harness_with(|builder| builder.with_model("gpt-5.4")).await?; @@ -204,7 +204,7 @@ async fn pipe_output_with_login() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn pipe_output_without_login() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let harness = shell_command_harness_with(|builder| builder.with_model("gpt-5.4")).await?; diff --git a/codex-rs/core/tests/suite/shell_serialization.rs b/codex-rs/core/tests/suite/shell_serialization.rs index 47faf4c8b..a428014ed 100644 --- a/codex-rs/core/tests/suite/shell_serialization.rs +++ b/codex-rs/core/tests/suite/shell_serialization.rs @@ -11,7 +11,7 @@ use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; -use core_test_support::skip_if_wine_exec; +use core_test_support::skip_if_target_windows; use core_test_support::test_codex::test_codex; use pretty_assertions::assert_eq; use regex_lite::Regex; @@ -236,7 +236,7 @@ M {file_name} #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn apply_patch_custom_tool_call_reports_failure_output() -> Result<()> { // TODO(anp): Remove after apply-patch assertions use target-native paths. - skip_if_wine_exec!(Ok(()), "asserts POSIX apply_patch failure text"); + skip_if_target_windows!(Ok(()), "asserts POSIX apply_patch failure text"); skip_if_no_network!(Ok(())); let harness = apply_patch_harness().await?; diff --git a/codex-rs/core/tests/suite/skills.rs b/codex-rs/core/tests/suite/skills.rs index 7beaa1710..02c643152 100644 --- a/codex-rs/core/tests/suite/skills.rs +++ b/codex-rs/core/tests/suite/skills.rs @@ -17,7 +17,7 @@ use core_test_support::responses::mount_sse_once; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; -use core_test_support::skip_if_wine_exec; +use core_test_support::skip_if_target_windows; use core_test_support::test_codex::local_selections; use core_test_support::test_codex::test_codex; use core_test_support::test_codex::turn_permission_fields; @@ -49,7 +49,7 @@ async fn write_repo_skill( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn user_turn_includes_skill_instructions() -> Result<()> { // TODO(anp): Remove after skill-path helpers use target-native paths. - skip_if_wine_exec!(Ok(()), "requires native cross-OS skill paths"); + skip_if_target_windows!(Ok(()), "requires native cross-OS skill paths"); skip_if_no_network!(Ok(())); let server = start_mock_server().await; diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index fe6ce7fbb..2414a280a 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -31,9 +31,10 @@ use core_test_support::responses::ev_response_created; use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; +use core_test_support::skip_if_host_windows; use core_test_support::skip_if_no_network; use core_test_support::skip_if_sandbox; -use core_test_support::skip_if_windows; +use core_test_support::skip_if_target_windows; use core_test_support::skip_if_wine_exec; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::TestCodexHarness; @@ -244,7 +245,7 @@ async fn create_workspace_directory( async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let builder = test_codex().with_config(|config| { config.use_experimental_unified_exec_tool = true; @@ -386,13 +387,12 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_emits_exec_command_begin_event() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!( + skip_if_target_windows!( Ok(()), "uses a POSIX command and does not assert successful execution" ); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -450,13 +450,12 @@ async fn unified_exec_emits_exec_command_begin_event() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_resolves_relative_workdir() -> Result<()> { // TODO(anp): Remove after workdir helpers use target-native paths. - skip_if_wine_exec!( + skip_if_target_windows!( Ok(()), "does not assert successful native-Windows workdir execution" ); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -524,7 +523,7 @@ async fn unified_exec_resolves_relative_workdir() -> Result<()> { async fn unified_exec_respects_workdir_override() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; @@ -588,10 +587,9 @@ async fn unified_exec_respects_workdir_override() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_emits_exec_command_end_event() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -663,10 +661,9 @@ async fn unified_exec_emits_exec_command_end_event() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_emits_output_delta_for_exec_command() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -723,10 +720,9 @@ async fn unified_exec_emits_output_delta_for_exec_command() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_full_lifecycle_with_background_end_event() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -820,10 +816,9 @@ async fn unified_exec_full_lifecycle_with_background_end_event() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_network_denial_emits_failed_background_end_event() -> Result<()> { // TODO(anp): Remove after network-denial fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses the POSIX/Python network-denial fixture"); + skip_if_target_windows!(Ok(()), "uses the POSIX/Python network-denial fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; let (test, sandbox_policy) = unified_exec_network_denial_test(&server).await?; @@ -865,10 +860,9 @@ async fn unified_exec_network_denial_emits_failed_background_end_event() -> Resu #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_short_lived_network_denial_emits_failed_end_event() -> Result<()> { // TODO(anp): Remove after network-denial fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses the POSIX/Python network-denial fixture"); + skip_if_target_windows!(Ok(()), "uses the POSIX/Python network-denial fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; let (test, sandbox_policy) = unified_exec_network_denial_test(&server).await?; @@ -1020,10 +1014,9 @@ async fn wait_for_unified_exec_end( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_emits_terminal_interaction_for_write_stdin() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "uses POSIX interactive-process and EOF semantics"); + skip_if_target_windows!(Ok(()), "uses POSIX interactive-process and EOF semantics"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1105,10 +1098,9 @@ async fn unified_exec_emits_terminal_interaction_for_write_stdin() -> Result<()> #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_terminal_interaction_captures_delayed_output() -> Result<()> { // TODO(anp): Remove after timing fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX sleep/echo timing fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX sleep/echo timing fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1287,7 +1279,7 @@ async fn unified_exec_terminal_interaction_captures_delayed_output() -> Result<( async fn unified_exec_emits_one_begin_and_one_end_event() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; @@ -1409,10 +1401,9 @@ async fn unified_exec_emits_one_begin_and_one_end_event() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1504,10 +1495,9 @@ async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn exec_command_clamps_model_requested_max_output_tokens_to_policy() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1568,10 +1558,9 @@ async fn exec_command_clamps_model_requested_max_output_tokens_to_policy() -> Re #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn write_stdin_clamps_model_requested_max_output_tokens_to_policy() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "uses POSIX read/while and Unix TTY semantics"); + skip_if_target_windows!(Ok(()), "uses POSIX read/while and Unix TTY semantics"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1659,10 +1648,9 @@ async fn write_stdin_clamps_model_requested_max_output_tokens_to_policy() -> Res #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_defaults_to_pipe() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "requires Python/Unix PTY support in the target"); + skip_if_target_windows!(Ok(()), "requires Python/Unix PTY support in the target"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1730,10 +1718,9 @@ async fn unified_exec_defaults_to_pipe() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_can_enable_tty() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "requires Python/Unix PTY support in the target"); + skip_if_target_windows!(Ok(()), "requires Python/Unix PTY support in the target"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1798,10 +1785,9 @@ async fn unified_exec_can_enable_tty() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_respects_early_exit_notifications() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -1883,10 +1869,9 @@ async fn unified_exec_respects_early_exit_notifications() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "uses POSIX interactive-process and EOF semantics"); + skip_if_target_windows!(Ok(()), "uses POSIX interactive-process and EOF semantics"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2037,8 +2022,8 @@ async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn write_stdin_ctrl_c_interrupts_non_tty_session() -> Result<()> { - // TODO(anp): Add a Wine-exec test for explicit interrupt handling on Windows. - skip_if_wine_exec!(Ok(()), "asserts Unix SIGINT and trap semantics"); + // TODO(anp): Add a target-Windows test for explicit interrupt handling. + skip_if_target_windows!(Ok(()), "asserts Unix SIGINT and trap semantics"); assert_write_stdin_ctrl_c_interrupts_non_tty_session( "trap", "trap 'echo INT-TRAP; exit 42' INT; echo READY; while true; do sleep 30; done", @@ -2050,8 +2035,8 @@ async fn write_stdin_ctrl_c_interrupts_non_tty_session() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn write_stdin_ctrl_c_default_interrupt_reports_130_for_non_tty_session() -> Result<()> { - // TODO(anp): Add a Wine-exec test for Windows Ctrl+C termination and exit reporting. - skip_if_wine_exec!(Ok(()), "asserts Unix SIGINT and exit-code semantics"); + // TODO(anp): Add a target-Windows test for Ctrl+C termination and exit reporting. + skip_if_target_windows!(Ok(()), "asserts Unix SIGINT and exit-code semantics"); assert_write_stdin_ctrl_c_interrupts_non_tty_session( "default", "echo READY; exec sleep 30", @@ -2069,7 +2054,6 @@ async fn assert_write_stdin_ctrl_c_interrupts_non_tty_session( ) -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2286,10 +2270,9 @@ async fn write_stdin_ctrl_c_reports_unsupported_interrupt_to_model_on_windows() #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_emits_end_event_when_session_dies_via_stdin() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "uses POSIX interactive-process and EOF semantics"); + skip_if_target_windows!(Ok(()), "uses POSIX interactive-process and EOF semantics"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2381,7 +2364,7 @@ async fn unified_exec_emits_end_event_when_session_dies_via_stdin() -> Result<() async fn unified_exec_keeps_long_running_session_after_turn_end() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; @@ -2491,7 +2474,7 @@ async fn unified_exec_keeps_long_running_session_after_turn_end() -> Result<()> async fn unified_exec_interrupt_preserves_long_running_session() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; @@ -2588,10 +2571,9 @@ async fn unified_exec_interrupt_preserves_long_running_session() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_reuses_session_via_stdin() -> Result<()> { // TODO(anp): Remove after unified-exec interactive fixtures support Windows/ConPTY. - skip_if_wine_exec!(Ok(()), "uses POSIX interactive-process and EOF semantics"); + skip_if_target_windows!(Ok(()), "uses POSIX interactive-process and EOF semantics"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2688,10 +2670,9 @@ async fn unified_exec_reuses_session_via_stdin() -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_streams_after_lagged_output() -> Result<()> { // TODO(anp): Remove after output fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "requires Python/Unix PTY support in the target"); + skip_if_target_windows!(Ok(()), "requires Python/Unix PTY support in the target"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2806,10 +2787,9 @@ PY #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn unified_exec_timeout_and_followup_poll() -> Result<()> { // TODO(anp): Remove after unified-exec fixtures use target-native commands. - skip_if_wine_exec!(Ok(()), "uses a POSIX-only command fixture"); + skip_if_target_windows!(Ok(()), "uses a POSIX-only command fixture"); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2897,13 +2877,12 @@ async fn unified_exec_timeout_and_followup_poll() -> Result<()> { #[cfg(not(target_arch = "arm"))] async fn unified_exec_formats_large_output_summary() -> Result<()> { // TODO(anp): Remove after output fixtures use target-native commands. - skip_if_wine_exec!( + skip_if_target_windows!( Ok(()), "requires Python and POSIX heredoc support in the target" ); skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); let server = start_mock_server().await; @@ -2974,7 +2953,7 @@ PY async fn unified_exec_runs_under_sandbox() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; @@ -3408,7 +3387,7 @@ async fn unified_exec_runs_on_all_platforms() -> Result<()> { async fn unified_exec_prunes_exited_sessions_first() -> Result<()> { skip_if_no_network!(Ok(())); skip_if_sandbox!(Ok(())); - skip_if_windows!(Ok(())); + skip_if_host_windows!(Ok(())); let server = start_mock_server().await; diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index e0e52172f..7a48d9ff2 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -30,7 +30,7 @@ use codex_protocol::protocol::TurnEnvironmentSelection; use codex_protocol::user_input::UserInput; use codex_utils_path_uri::PathUri; use core_test_support::PathExt; -use core_test_support::get_remote_test_env; +use core_test_support::is_remote_test_environment; use core_test_support::responses; use core_test_support::responses::ev_assistant_message; use core_test_support::responses::ev_completed; @@ -41,10 +41,12 @@ use core_test_support::responses::mount_sse_sequence; use core_test_support::responses::sse; use core_test_support::responses::start_mock_server; use core_test_support::skip_if_no_network; +use core_test_support::skip_if_no_remote_env; use core_test_support::test_codex::TestCodex; use core_test_support::test_codex::local; use core_test_support::test_codex::test_codex; use core_test_support::test_codex::turn_permission_fields; +use core_test_support::test_target_os; use core_test_support::wait_for_event_with_timeout; use image::DynamicImage; use image::GenericImageView; @@ -570,9 +572,7 @@ async fn view_image_tool_applies_local_sandbox_read_denies() -> anyhow::Result<( #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); - let Some(_remote_env) = get_remote_test_env() else { - return Ok(()); - }; + skip_if_no_remote_env!(Ok(())); let server = start_mock_server().await; let mut builder = test_codex(); @@ -1255,8 +1255,11 @@ async fn view_image_tool_turns_invalid_image_into_placeholder() -> anyhow::Resul async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()> { skip_if_no_network!(Ok(())); - let remote_test_env = get_remote_test_env(); - println!("view_image missing-file test exec-server environment: {remote_test_env:?}"); + println!( + "view_image missing-file test target: {:?}, remote: {}", + test_target_os(), + is_remote_test_environment() + ); let server = start_mock_server().await;