use std::io::Write;
use std::process::{Command, Stdio};
const BIN: &str = env!("CARGO_BIN_EXE_hprof-analyzer");
fn philosophers() -> Option<String> {
let p = format!(
"{}/tests/fixtures/dump_4_philosophers.hprof",
env!("CARGO_MANIFEST_DIR")
);
match std::fs::metadata(&p) {
Ok(m) if m.len() >= 1024 => Some(p),
_ => None,
}
}
fn run_repl(hprof: &str, stdin: &str) -> (bool, String) {
let mut child = Command::new(BIN)
.arg("query")
.arg(hprof)
.arg("--repl")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap();
child
.stdin
.as_mut()
.unwrap()
.write_all(stdin.as_bytes())
.unwrap();
let out = child.wait_with_output().unwrap();
(
out.status.success(),
String::from_utf8_lossy(&out.stdout).into_owned(),
)
}
#[test]
#[ignore = "reedline suppresses banner on non-TTY stdin; run manually"]
fn repl_plan_then_quit() {
let Some(hprof) = philosophers() else { return };
let (ok, stdout) = run_repl(&hprof, "!plan SELECT * FROM java.lang.String\n!quit\n");
assert!(ok, "REPL should exit 0:\n{stdout}");
assert!(
stdout.contains("hprof-analyzer OQL REPL"),
"banner missing:\n{stdout}"
);
assert!(
stdout.contains("stage:"),
"plan explanation missing:\n{stdout}"
);
assert!(
stdout.contains("needs (armed):"),
"plan needs missing:\n{stdout}"
);
}
#[test]
fn repl_bad_query_stays_alive() {
let Some(hprof) = philosophers() else { return };
let (ok, stdout) = run_repl(&hprof, "SELCT * FROM C\n!quit\n");
assert!(ok, "REPL should survive a bad query and exit 0:\n{stdout}");
assert!(
stdout.contains("error:"),
"expected an error line:\n{stdout}"
);
}
#[test]
fn repl_runs_real_query() {
let Some(hprof) = philosophers() else { return };
let (ok, stdout) = run_repl(&hprof, "SELECT COUNT(*) FROM java.lang.String\n!quit\n");
assert!(ok, "REPL should exit 0:\n{stdout}");
assert!(
stdout.contains("COUNT(*)"),
"missing COUNT header:\n{stdout}"
);
assert!(
stdout.contains("1 row"),
"missing row-count line:\n{stdout}"
);
}