mirror of
https://github.com/openai/codex.git
synced 2026-04-24 22:54:54 +00:00
141 lines
4.9 KiB
Rust
141 lines
4.9 KiB
Rust
use std::io::ErrorKind;
|
|
use std::io::Read;
|
|
use std::io::Write;
|
|
use std::process::Command;
|
|
use std::process::Stdio;
|
|
use std::sync::mpsc;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
use std::time::Instant;
|
|
|
|
use anyhow::Context;
|
|
use anyhow::anyhow;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[cfg(unix)]
|
|
use std::os::unix::net::UnixListener;
|
|
|
|
#[cfg(windows)]
|
|
use uds_windows::UnixListener;
|
|
|
|
#[test]
|
|
fn pipes_stdin_and_stdout_through_socket() -> anyhow::Result<()> {
|
|
let dir = tempfile::TempDir::new().context("failed to create temp dir")?;
|
|
let socket_path = dir.path().join("socket");
|
|
let request = b"request";
|
|
let request_path = dir.path().join("request.txt");
|
|
std::fs::write(&request_path, request).context("failed to write child stdin fixture")?;
|
|
let listener = match UnixListener::bind(&socket_path) {
|
|
Ok(listener) => listener,
|
|
Err(err) if err.kind() == ErrorKind::PermissionDenied => {
|
|
eprintln!("skipping test: failed to bind unix socket: {err}");
|
|
return Ok(());
|
|
}
|
|
Err(err) => {
|
|
return Err(err).context("failed to bind test unix socket");
|
|
}
|
|
};
|
|
|
|
let (tx, rx) = mpsc::channel();
|
|
let (event_tx, event_rx) = mpsc::channel();
|
|
let server_thread = thread::spawn(move || -> anyhow::Result<()> {
|
|
let _ = event_tx.send("waiting for accept".to_string());
|
|
let (mut connection, _) = listener
|
|
.accept()
|
|
.context("failed to accept test connection")?;
|
|
let _ = event_tx.send("accepted connection".to_string());
|
|
let mut received = vec![0; request.len()];
|
|
connection
|
|
.read_exact(&mut received)
|
|
.context("failed to read data from client")?;
|
|
let _ = event_tx.send(format!("read {} bytes", received.len()));
|
|
tx.send(received)
|
|
.map_err(|_| anyhow!("failed to send received bytes to test thread"))?;
|
|
connection
|
|
.write_all(b"response")
|
|
.context("failed to write response to client")?;
|
|
let _ = event_tx.send("wrote response".to_string());
|
|
Ok(())
|
|
});
|
|
|
|
let stdin = std::fs::File::open(&request_path).context("failed to open child stdin fixture")?;
|
|
let mut child = Command::new(codex_utils_cargo_bin::cargo_bin("codex-stdio-to-uds")?)
|
|
.arg(&socket_path)
|
|
.stdin(Stdio::from(stdin))
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.context("failed to spawn codex-stdio-to-uds")?;
|
|
|
|
let mut child_stdout = child.stdout.take().context("missing child stdout")?;
|
|
let mut child_stderr = child.stderr.take().context("missing child stderr")?;
|
|
let (stdout_tx, stdout_rx) = mpsc::channel();
|
|
let (stderr_tx, stderr_rx) = mpsc::channel();
|
|
thread::spawn(move || {
|
|
let mut stdout = Vec::new();
|
|
let result = child_stdout.read_to_end(&mut stdout).map(|_| stdout);
|
|
let _ = stdout_tx.send(result);
|
|
});
|
|
thread::spawn(move || {
|
|
let mut stderr = Vec::new();
|
|
let result = child_stderr.read_to_end(&mut stderr).map(|_| stderr);
|
|
let _ = stderr_tx.send(result);
|
|
});
|
|
|
|
let mut server_events = Vec::new();
|
|
let deadline = Instant::now() + Duration::from_secs(5);
|
|
let status = loop {
|
|
while let Ok(event) = event_rx.try_recv() {
|
|
server_events.push(event);
|
|
}
|
|
|
|
if let Some(status) = child.try_wait().context("failed to poll child status")? {
|
|
break status;
|
|
}
|
|
|
|
if Instant::now() >= deadline {
|
|
let _ = child.kill();
|
|
let _ = child.wait();
|
|
let stderr = stderr_rx
|
|
.recv_timeout(Duration::from_secs(1))
|
|
.context("timed out waiting for child stderr after kill")?
|
|
.context("failed to read child stderr")?;
|
|
anyhow::bail!(
|
|
"codex-stdio-to-uds did not exit in time; server events: {:?}; stderr: {}",
|
|
server_events,
|
|
String::from_utf8_lossy(&stderr).trim_end()
|
|
);
|
|
}
|
|
|
|
thread::sleep(Duration::from_millis(25));
|
|
};
|
|
|
|
let stdout = stdout_rx
|
|
.recv_timeout(Duration::from_secs(1))
|
|
.context("timed out waiting for child stdout")?
|
|
.context("failed to read child stdout")?;
|
|
let stderr = stderr_rx
|
|
.recv_timeout(Duration::from_secs(1))
|
|
.context("timed out waiting for child stderr")?
|
|
.context("failed to read child stderr")?;
|
|
assert!(
|
|
status.success(),
|
|
"codex-stdio-to-uds exited with {status}; server events: {:?}; stderr: {}",
|
|
server_events,
|
|
String::from_utf8_lossy(&stderr).trim_end()
|
|
);
|
|
assert_eq!(stdout, b"response");
|
|
|
|
let received = rx
|
|
.recv_timeout(Duration::from_secs(1))
|
|
.context("server did not receive data in time")?;
|
|
assert_eq!(received, request);
|
|
|
|
let server_result = server_thread
|
|
.join()
|
|
.map_err(|_| anyhow!("server thread panicked"))?;
|
|
server_result.context("server failed")?;
|
|
|
|
Ok(())
|
|
}
|