use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
const OK_NUM_SRC: &str = "m>R n t;~7";
const OK_TEXT_SRC: &str = "m>R t t;~\"tasks.txt\"";
const PLAIN_SRC: &str = "m>n;42";
fn assert_ok_bare_plain(engine: &str, src: &str, expected_stdout: &str) {
let out = ilo()
.args([src, engine])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"{engine}: expected exit 0 for Value::Ok from main, got {:?}. stdout={:?} stderr={:?}",
out.status.code(),
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
);
let stdout = String::from_utf8_lossy(&out.stdout);
let stdout_trim = stdout.trim();
assert_eq!(
stdout_trim, expected_stdout,
"{engine}: expected bare {expected_stdout:?} on stdout (no `~` prefix), got {stdout_trim:?}",
);
assert!(
!stdout_trim.starts_with('~'),
"{engine}: stdout still leaks `~` wrapper: {stdout_trim:?}",
);
}
#[test]
fn main_ok_num_bare_tree() {
assert_ok_bare_plain("--vm", OK_NUM_SRC, "7");
}
#[test]
fn main_ok_num_bare_vm() {
assert_ok_bare_plain("--vm", OK_NUM_SRC, "7");
}
#[test]
#[cfg(feature = "cranelift")]
fn main_ok_num_bare_cranelift() {
assert_ok_bare_plain("--jit", OK_NUM_SRC, "7");
}
#[test]
fn main_ok_text_bare_tree() {
assert_ok_bare_plain("--vm", OK_TEXT_SRC, "tasks.txt");
}
#[test]
fn main_ok_text_bare_vm() {
assert_ok_bare_plain("--vm", OK_TEXT_SRC, "tasks.txt");
}
#[test]
#[cfg(feature = "cranelift")]
fn main_ok_text_bare_cranelift() {
assert_ok_bare_plain("--jit", OK_TEXT_SRC, "tasks.txt");
}
fn assert_plain_unchanged(engine: &str) {
let out = ilo()
.args([PLAIN_SRC, engine])
.output()
.expect("failed to run ilo");
assert!(out.status.success(), "{engine}: expected exit 0");
let stdout = String::from_utf8_lossy(&out.stdout);
assert_eq!(
stdout.trim(),
"42",
"{engine}: plain non-Result return should print via Display unchanged",
);
}
#[test]
fn main_plain_unchanged_tree() {
assert_plain_unchanged("--vm");
}
#[test]
fn main_plain_unchanged_vm() {
assert_plain_unchanged("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn main_plain_unchanged_cranelift() {
assert_plain_unchanged("--jit");
}
fn assert_ok_json_envelope(engine: &str) {
let out = ilo()
.args([OK_NUM_SRC, engine, "--json"])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"{engine} -j: expected exit 0, got {:?}",
out.status.code(),
);
let stdout = String::from_utf8_lossy(&out.stdout);
let parsed: serde_json::Value = serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("{engine} -j: stdout was not valid JSON ({e}): {stdout:?}"));
let ok = parsed.get("ok").unwrap_or_else(|| {
panic!("{engine} -j: expected `ok` key in {parsed:?}");
});
assert_eq!(
ok,
&serde_json::json!(7),
"{engine} -j: ok payload mismatch"
);
}
#[test]
fn main_ok_json_envelope_tree() {
assert_ok_json_envelope("--vm");
}
#[test]
fn main_ok_json_envelope_vm() {
assert_ok_json_envelope("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn main_ok_json_envelope_cranelift() {
assert_ok_json_envelope("--jit");
}
fn assert_prnt_wrapper_preserved(engine: &str) {
let out = ilo()
.args(["m>R t t;prnt ~\"x\"", engine])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"{engine}: expected exit 0, got {:?}. stderr={:?}",
out.status.code(),
String::from_utf8_lossy(&out.stderr),
);
let stdout = String::from_utf8_lossy(&out.stdout);
let lines: Vec<&str> = stdout.lines().collect();
assert_eq!(
lines.len(),
2,
"{engine}: expected 2 stdout lines (prnt + top-level), got {stdout:?}",
);
assert_eq!(
lines[0], "~x",
"{engine}: prnt must preserve the `~` wrapper (Display), got {:?}",
lines[0],
);
assert_eq!(
lines[1], "x",
"{engine}: top-level print must strip the `~` wrapper, got {:?}",
lines[1],
);
}
#[test]
fn prnt_wrapper_preserved_tree() {
assert_prnt_wrapper_preserved("--vm");
}
#[test]
fn prnt_wrapper_preserved_vm() {
assert_prnt_wrapper_preserved("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn prnt_wrapper_preserved_cranelift() {
assert_prnt_wrapper_preserved("--jit");
}