use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run(engine: &str, src: &str, entry: &str) -> String {
let out = ilo()
.args([src, engine, entry])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} failed for `{src}`: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn run_err(engine: &str, src: &str, entry: &str) -> String {
let out = ilo()
.args([src, engine, entry])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"ilo {engine} unexpectedly succeeded for `{src}`: stdout={}",
String::from_utf8_lossy(&out.stdout)
);
String::from_utf8_lossy(&out.stderr).to_string()
}
const PRESENT_SRC: &str = r#"f>O n;m=mset mmap "k" 5;v=mget! m "k";v"#;
fn check_present(engine: &str) {
assert_eq!(run(engine, PRESENT_SRC, "f"), "5", "engine={engine}");
}
#[test]
fn mget_bang_present_tree() {
check_present("--vm");
}
#[test]
fn mget_bang_present_vm() {
check_present("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn mget_bang_present_cranelift() {
check_present("--jit");
}
const MISSING_SRC: &str = r#"f>O n;m=mmap;v=mget! m "missing";v"#;
fn check_missing(engine: &str) {
assert_eq!(run(engine, MISSING_SRC, "f"), "nil", "engine={engine}");
}
#[test]
fn mget_bang_missing_tree() {
check_missing("--vm");
}
#[test]
fn mget_bang_missing_vm() {
check_missing("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn mget_bang_missing_cranelift() {
check_missing("--jit");
}
const SHORTCIRCUIT_SRC: &str = r#"f>O n;m=mmap;v=mget! m "k";+v 99"#;
fn check_shortcircuit(engine: &str) {
assert_eq!(run(engine, SHORTCIRCUIT_SRC, "f"), "nil", "engine={engine}");
}
#[test]
fn mget_bang_shortcircuit_tree() {
check_shortcircuit("--vm");
}
#[test]
fn mget_bang_shortcircuit_vm() {
check_shortcircuit("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn mget_bang_shortcircuit_cranelift() {
check_shortcircuit("--jit");
}
#[test]
fn mget_bang_in_non_optional_fn_rejected() {
let stderr = run_err("--vm", r#"f>n;m=mmap;mget! m "x""#, "f");
assert!(
stderr.contains("ILO-T026") && stderr.contains("Optional"),
"expected ILO-T026 mentioning Optional, got: {stderr}"
);
}
const TWO_STEP_SRC: &str = r#"f>n;m=mset mmap "k" 5;r=mget m "k";v=r??0;v"#;
fn check_two_step(engine: &str) {
assert_eq!(run(engine, TWO_STEP_SRC, "f"), "5", "engine={engine}");
}
#[test]
fn mget_two_step_default_tree() {
check_two_step("--vm");
}
#[test]
fn mget_two_step_default_vm() {
check_two_step("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn mget_two_step_default_cranelift() {
check_two_step("--jit");
}
const TWO_STEP_MISS_SRC: &str = r#"f>n;m=mmap;r=mget m "k";v=r??42;v"#;
fn check_two_step_miss(engine: &str) {
assert_eq!(run(engine, TWO_STEP_MISS_SRC, "f"), "42", "engine={engine}");
}
#[test]
fn mget_two_step_default_miss_tree() {
check_two_step_miss("--vm");
}
#[test]
fn mget_two_step_default_miss_vm() {
check_two_step_miss("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn mget_two_step_default_miss_cranelift() {
check_two_step_miss("--jit");
}
const RESULT_BANG_OK_SRC: &str = r#"f>R n t;v=num! "42";~v"#;
const RESULT_BANG_ERR_SRC: &str = r#"f>R n t;v=num! "abc";~v"#;
fn check_result_ok(engine: &str) {
assert_eq!(
run(engine, RESULT_BANG_OK_SRC, "f"),
"42",
"engine={engine}"
);
}
fn check_result_err(engine: &str) {
let stderr = run_err(engine, RESULT_BANG_ERR_SRC, "f");
assert!(
stderr.contains("abc"),
"engine={engine}: expected err containing abc on stderr, got {stderr}"
);
}
#[test]
fn result_bang_ok_tree() {
check_result_ok("--vm");
}
#[test]
fn result_bang_ok_vm() {
check_result_ok("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn result_bang_ok_cranelift() {
check_result_ok("--jit");
}
#[test]
fn result_bang_err_tree() {
check_result_err("--vm");
}
#[test]
fn result_bang_err_vm() {
check_result_err("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn result_bang_err_cranelift() {
check_result_err("--jit");
}
const DTFMT_BANG_ERR_SRC: &str = r#"f>R t t;v=dtfmt! 99999999999999 "%Y";~v"#;
fn check_dtfmt_err(engine: &str) {
let stderr = run_err(engine, DTFMT_BANG_ERR_SRC, "f");
assert!(
stderr.contains("dtfmt") && stderr.contains("out of range"),
"engine={engine}: expected dtfmt err on stderr, got {stderr}"
);
}
#[test]
fn dtfmt_bang_err_tree() {
check_dtfmt_err("--vm");
}
#[test]
fn dtfmt_bang_err_vm() {
check_dtfmt_err("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn dtfmt_bang_err_cranelift() {
check_dtfmt_err("--jit");
}
const NUM_BANG_SHORTCIRCUIT_SRC: &str = r#"f>R n t;v=num! "abc";~99"#;
fn check_num_shortcircuit(engine: &str) {
let stderr = run_err(engine, NUM_BANG_SHORTCIRCUIT_SRC, "f");
assert!(
stderr.contains("abc"),
"engine={engine}: expected propagated err, got {stderr}"
);
}
#[test]
fn num_bang_shortcircuit_tree() {
check_num_shortcircuit("--vm");
}
#[test]
fn num_bang_shortcircuit_vm() {
check_num_shortcircuit("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn num_bang_shortcircuit_cranelift() {
check_num_shortcircuit("--jit");
}
const RD_BANG_SRC: &str = r#"f>R t t;v=rd! "/no/such/path/ilo-test";~v"#;
const RDL_BANG_SRC: &str = r#"f>R (L t) t;v=rdl! "/no/such/path/ilo-test";~v"#;
fn check_rd_err(engine: &str) {
let stderr = run_err(engine, RD_BANG_SRC, "f");
assert!(
stderr.to_lowercase().contains("no such")
|| stderr.contains("not found")
|| stderr.contains("/no/such/path"),
"engine={engine}: expected file-not-found err, got {stderr}"
);
}
fn check_rdl_err(engine: &str) {
let stderr = run_err(engine, RDL_BANG_SRC, "f");
assert!(
stderr.to_lowercase().contains("no such")
|| stderr.contains("not found")
|| stderr.contains("/no/such/path"),
"engine={engine}: expected file-not-found err, got {stderr}"
);
}
#[test]
fn rd_bang_err_tree() {
check_rd_err("--vm");
}
#[test]
fn rd_bang_err_vm() {
check_rd_err("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn rd_bang_err_cranelift() {
check_rd_err("--jit");
}
#[test]
fn rdl_bang_err_tree() {
check_rdl_err("--vm");
}
#[test]
fn rdl_bang_err_vm() {
check_rdl_err("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn rdl_bang_err_cranelift() {
check_rdl_err("--jit");
}