#![allow(dead_code)]
use assert_cmd::cargo::cargo_bin_cmd;
use std::time::Duration;
pub const TIMEOUT_BASIC: Duration = Duration::from_secs(10);
pub const TIMEOUT_SNAPSHOT: Duration = Duration::from_secs(15);
pub fn orcs_cmd_raw() -> assert_cmd::Command {
let mut cmd: assert_cmd::Command = cargo_bin_cmd!("orcs");
cmd.timeout(TIMEOUT_BASIC);
cmd
}
pub fn orcs_cmd() -> (assert_cmd::Command, tempfile::TempDir) {
let tmp = tempfile::tempdir().expect("create temp dir for test isolation");
let mut cmd: assert_cmd::Command = cargo_bin_cmd!("orcs");
cmd.timeout(TIMEOUT_BASIC);
cmd.args(["--sandbox", tmp.path().to_str().expect("valid utf8")]);
(cmd, tmp)
}
pub fn orcs_cmd_snapshot() -> (assert_cmd::Command, tempfile::TempDir) {
let tmp = tempfile::tempdir().expect("create temp dir for builtins");
let mut cmd: assert_cmd::Command = cargo_bin_cmd!("orcs");
cmd.timeout(TIMEOUT_SNAPSHOT);
cmd.args(["--builtins-dir", tmp.path().to_str().expect("valid utf8")]);
(cmd, tmp)
}
pub fn orcs_cmd_with_builtins(dir: &str) -> assert_cmd::Command {
let mut cmd: assert_cmd::Command = cargo_bin_cmd!("orcs");
cmd.timeout(TIMEOUT_SNAPSHOT);
cmd.args(["--builtins-dir", dir]);
cmd
}
pub fn dump_test_output(label: &str, stdout: &str, stderr: &str) -> DumpGuard {
let (dir_path, _guard) = match std::env::var("ORCS_TEST_DUMP_DIR") {
Ok(base) => {
let dir = std::path::PathBuf::from(base).join(label);
std::fs::create_dir_all(&dir).expect("create persistent dump dir");
(dir, None)
}
Err(_) => {
let td = tempfile::tempdir().expect("create temp dump dir");
let dir = td.path().to_path_buf();
(dir, Some(td))
}
};
std::fs::write(dir_path.join("stdout.txt"), stdout.as_bytes()).expect("write stdout dump");
std::fs::write(dir_path.join("stderr.txt"), stderr.as_bytes()).expect("write stderr dump");
eprintln!("dump_dir={}", dir_path.display());
DumpGuard {
_guard,
path: dir_path,
}
}
pub struct DumpGuard {
_guard: Option<tempfile::TempDir>,
#[allow(dead_code)]
path: std::path::PathBuf,
}
pub fn extract_session_id(stdout: &str) -> Option<String> {
for line in stdout.lines() {
if let Some(pos) = line.find("Session saved: ") {
let id_start = pos + "Session saved: ".len();
let id = line[id_start..].trim();
if id.len() >= 36 {
return Some(id[..36].to_string());
}
}
}
None
}
pub fn spawn_and_wait_for(
gate: &str,
extra_args: &[&str],
sandbox_dir: &std::path::Path,
) -> (String, String) {
use std::io::{BufRead, BufReader, Read, Write};
use std::process::{Command, Stdio};
use std::sync::mpsc as std_mpsc;
use std::thread;
let bin = assert_cmd::cargo::cargo_bin!("orcs");
let mut cmd = Command::new(bin);
cmd.arg("--sandbox").arg(sandbox_dir).args(extra_args);
cmd.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = cmd.spawn().expect("spawn orcs process");
let mut stdin = child.stdin.take().expect("open stdin pipe");
let child_stdout = child.stdout.take().expect("open stdout pipe");
let child_stderr = child.stderr.take().expect("open stderr pipe");
let gate_owned = gate.to_string();
let (gate_tx, gate_rx) = std_mpsc::channel::<()>();
let stdout_thread = thread::spawn(move || {
let mut lines = Vec::new();
let reader = BufReader::new(child_stdout);
let mut notified = false;
for line in reader.lines() {
let line = line.expect("read stdout line");
if !notified && line.contains(&gate_owned) {
let _ = gate_tx.send(());
notified = true;
}
lines.push(line);
}
if !notified {
let _ = gate_tx.send(());
}
lines.join("\n")
});
let stderr_thread = thread::spawn(move || {
let mut buf = String::new();
let mut reader = BufReader::new(child_stderr);
reader
.read_to_string(&mut buf)
.expect("read stderr to string");
buf
});
let found = gate_rx.recv_timeout(TIMEOUT_BASIC).is_ok();
writeln!(stdin, "q").expect("send quit after gate");
drop(stdin);
let stdout = stdout_thread.join().expect("join stdout thread");
let stderr = stderr_thread.join().expect("join stderr thread");
let _ = child.wait();
assert!(
found && stdout.contains(gate),
"Gate string {gate:?} not observed within {TIMEOUT_BASIC:?}.\n\
stdout:\n{stdout}\nstderr:\n{stderr}"
);
(stdout, stderr)
}