Files
codex/codex-rs/utils/pty/src/linux_child_tests.rs
T
jif 595534314f Use descriptor-safe helpers for executable test fixtures (#50018)
## Why

Concurrent Linux test spawns can inherit writable descriptors for executable fixtures, preventing those fixtures from being executed even after the writing test closes its descriptor.

## What changed

Replace direct executable fixture writes and copies across the Rust tests with `codex_utils_cargo_bin::write_executable` and `copy_executable`. These existing helpers complete writes and copies in separate processes on Linux.

Remove the empty temporary inode before creating fake bubblewrap executables, and stage tampered bubblewrap bytes in a separate data file before copying them into the executable fixture. Add the required test dependencies.

GitOrigin-RevId: c875c83088731a4933ef1574e8d79d8cecc12f54
2026-10-01 15:57:03 +00:00

60 lines
2.3 KiB
Rust

//! Linux executable lookup must match the compatibility launcher's PATH semantics.
use pretty_assertions::assert_eq;
use crate::Command;
use crate::ProcessMode;
#[tokio::test]
async fn path_search_stops_at_invalid_candidates() -> anyhow::Result<()> {
let root = tempfile::tempdir()?;
std::fs::create_dir(root.path().join("bin"))?;
std::os::unix::fs::symlink("loop", root.path().join("loop"))?;
let script = root.path().join("bin/server");
codex_utils_cargo_bin::write_executable(&script, "#!/bin/sh\nprintf later\n")?;
for candidate in ["loop".to_owned(), "x".repeat(256)] {
let mut command = Command::new("server");
command
.current_dir(root.path())
.env("PATH", format!("{candidate}:bin"));
let expected = command
.inner
.spawn()
.expect_err("invalid first candidate must stop lookup");
command.process_mode(ProcessMode::NewSession);
let actual = command.spawn().err().expect("native lookup must stop too");
assert_eq!(actual.raw_os_error(), expected.raw_os_error());
}
Ok(())
}
#[tokio::test]
async fn empty_path_entries_preserve_script_argv0() -> anyhow::Result<()> {
// A sibling test can fork while the script is open for writing, retaining
// the writable descriptor and making exec fail with ETXTBSY.
if std::env::var_os("CODEX_TEST_PATH_ARGV0").is_none() {
let output = std::process::Command::new(std::env::current_exe()?)
.args([
"--exact",
"child_command::linux_tests::empty_path_entries_preserve_script_argv0",
"--nocapture",
])
.env("CODEX_TEST_PATH_ARGV0", "1")
.output()?;
assert!(output.status.success(), "{output:?}");
return Ok(());
}
let root = tempfile::tempdir()?;
let script = root.path().join("server");
codex_utils_cargo_bin::write_executable(&script, "#!/bin/sh\nprintf '%s' \"$0\"\n")?;
for path in ["", ":missing", "missing:", ".", "./"] {
let mut command = Command::new("server");
command.current_dir(root.path()).env("PATH", path);
let expected = command.inner.output().await?;
command.process_mode(ProcessMode::NewSession);
let actual = command.spawn()?.wait_with_output().await?;
assert_eq!(actual, expected);
}
Ok(())
}