use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn check_stdout(engine: &str, src: &str, expected: &str) {
let out = ilo()
.args([src, engine, "f"])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"engine={engine}: expected success for `{src}`, got stderr={}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
String::from_utf8_lossy(&out.stdout).trim(),
expected,
"engine={engine}: stdout mismatch for `{src}`"
);
}
fn check_all(src: &str, expected: &str) {
check_stdout("--vm", src, expected);
check_stdout("--vm", src, expected);
#[cfg(feature = "cranelift")]
check_stdout("--jit", src, expected);
}
#[test]
fn wr_then_rd_text_cross_engine() {
let path_tree = "/tmp/ilo-jit-batch7-wr-rd-tree.txt";
let path_vm = "/tmp/ilo-jit-batch7-wr-rd-vm.txt";
let path_cl = "/tmp/ilo-jit-batch7-wr-rd-cl.txt";
let _ = std::fs::remove_file(path_tree);
let _ = std::fs::remove_file(path_vm);
let _ = std::fs::remove_file(path_cl);
check_stdout(
"--vm",
&format!("f>t;w=wr!! \"{path_tree}\" \"hello\";rd!! \"{path_tree}\""),
"hello",
);
check_stdout(
"--vm",
&format!("f>t;w=wr!! \"{path_vm}\" \"hello\";rd!! \"{path_vm}\""),
"hello",
);
#[cfg(feature = "cranelift")]
check_stdout(
"--jit",
&format!("f>t;w=wr!! \"{path_cl}\" \"hello\";rd!! \"{path_cl}\""),
"hello",
);
}
#[test]
fn wrl_then_rdl_cross_engine() {
let path_tree = "/tmp/ilo-jit-batch7-wrl-rdl-tree.txt";
let path_vm = "/tmp/ilo-jit-batch7-wrl-rdl-vm.txt";
let path_cl = "/tmp/ilo-jit-batch7-wrl-rdl-cl.txt";
let _ = std::fs::remove_file(path_tree);
let _ = std::fs::remove_file(path_vm);
let _ = std::fs::remove_file(path_cl);
check_stdout(
"--vm",
&format!("f>n;w=wrl!! \"{path_tree}\" [\"a\" \"b\" \"c\"];es=rdl!! \"{path_tree}\";len es"),
"3",
);
check_stdout(
"--vm",
&format!("f>n;w=wrl!! \"{path_vm}\" [\"a\" \"b\" \"c\"];es=rdl!! \"{path_vm}\";len es"),
"3",
);
#[cfg(feature = "cranelift")]
check_stdout(
"--jit",
&format!("f>n;w=wrl!! \"{path_cl}\" [\"a\" \"b\" \"c\"];es=rdl!! \"{path_cl}\";len es"),
"3",
);
}
#[test]
fn jpar_valid_json_cross_engine() {
check_all("f>t;jpth!! \"{\\\"k\\\":\\\"v\\\"}\" \"k\"", "v");
}
#[test]
fn rdjl_reads_jsonl_cross_engine() {
let path_tree = "/tmp/ilo-jit-batch7-rdjl-tree.jsonl";
let path_vm = "/tmp/ilo-jit-batch7-rdjl-vm.jsonl";
let path_cl = "/tmp/ilo-jit-batch7-rdjl-cl.jsonl";
let _ = std::fs::remove_file(path_tree);
let _ = std::fs::remove_file(path_vm);
let _ = std::fs::remove_file(path_cl);
let prog_tree = format!(
"prep p:t>R t t;wrl p [\"{{\\\"k\\\":1}}\" \"{{\\\"k\\\":2}}\" \"{{\\\"k\\\":3}}\"]\nf>n;w=prep \"{path_tree}\";es=rdjl \"{path_tree}\";len es"
);
let prog_vm = format!(
"prep p:t>R t t;wrl p [\"{{\\\"k\\\":1}}\" \"{{\\\"k\\\":2}}\" \"{{\\\"k\\\":3}}\"]\nf>n;w=prep \"{path_vm}\";es=rdjl \"{path_vm}\";len es"
);
let prog_cl = format!(
"prep p:t>R t t;wrl p [\"{{\\\"k\\\":1}}\" \"{{\\\"k\\\":2}}\" \"{{\\\"k\\\":3}}\"]\nf>n;w=prep \"{path_cl}\";es=rdjl \"{path_cl}\";len es"
);
check_stdout("--vm", &prog_tree, "3");
check_stdout("--vm", &prog_vm, "3");
#[cfg(feature = "cranelift")]
check_stdout("--jit", &prog_cl, "3");
}
#[test]
fn dtfmt_epoch_zero_cross_engine() {
check_all("f>t;dtfmt!! 0 \"%Y-%m-%d\"", "1970-01-01");
}
#[test]
fn dtparse_round_trip_cross_engine() {
check_all("f>n;dtparse!! \"1970-01-01\" \"%Y-%m-%d\"", "0");
}
#[test]
#[cfg(feature = "cranelift")]
fn no_stale_jit_error_leak_after_hd_error_then_io() {
let first = ilo()
.args(["f>n;hd []", "--jit", "f"])
.output()
.expect("failed to run ilo");
assert!(!first.status.success(), "first call should error on hd []");
let path = "/tmp/ilo-jit-batch7-no-leak.txt";
let _ = std::fs::remove_file(path);
let src = format!("f>t;w=wr!! \"{path}\" \"ok\";rd!! \"{path}\"");
let second = ilo()
.args([src.as_str(), "--jit", "f"])
.output()
.expect("failed to run ilo");
assert!(
second.status.success(),
"second call should succeed, got stderr={}",
String::from_utf8_lossy(&second.stderr)
);
assert_eq!(
String::from_utf8_lossy(&second.stdout).trim(),
"ok",
"second call stdout mismatch"
);
}