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()
}
const SLC_REPRO: &str = "f>L n;xs=[10,20,30];i=0;slc xs i +i 1";
fn check_slc(engine: &str) {
assert_eq!(run(engine, SLC_REPRO, "f"), "[10]", "engine={engine}");
}
#[test]
fn slc_with_prefix_arg_tree() {
check_slc("--vm");
}
#[test]
fn slc_with_prefix_arg_vm() {
check_slc("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn slc_with_prefix_arg_cranelift() {
check_slc("--jit");
}
const G_DEF: &str = "g a:n b:n c:n>n;+ +a b c";
fn check_prefix_in_position(engine: &str, args: &[&str], expected: &str) {
let mut cmd_args: Vec<String> = vec![G_DEF.to_string(), engine.to_string(), "g".to_string()];
for a in args {
cmd_args.push(a.to_string());
}
let out = ilo().args(&cmd_args).output().expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} failed: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
String::from_utf8_lossy(&out.stdout).trim(),
expected,
"engine={engine} args={args:?}"
);
}
fn check_three_arg_prefix(engine: &str) {
check_prefix_in_position(engine, &["1", "2", "3"], "6");
}
#[test]
fn three_arg_prefix_tree() {
check_three_arg_prefix("--vm");
}
#[test]
fn three_arg_prefix_vm() {
check_three_arg_prefix("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn three_arg_prefix_cranelift() {
check_three_arg_prefix("--jit");
}
fn check_infix_on_call(engine: &str) {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
"ilo_prefix_arg_t3_{}_{}.ilo",
std::process::id(),
seq
));
std::fs::write(&path, "g x:n>n;*x 2\nf>n;a=g 5;+a 3\n").unwrap();
let out = ilo()
.args([path.to_str().unwrap(), engine, "f"])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} failed: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
String::from_utf8_lossy(&out.stdout).trim(),
"13",
"engine={engine}"
);
}
#[test]
fn infix_on_call_result_tree() {
check_infix_on_call("--vm");
}
#[test]
fn infix_on_call_result_vm() {
check_infix_on_call("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn infix_on_call_result_cranelift() {
check_infix_on_call("--jit");
}
fn check_abs_guard(engine: &str) {
let src = "f a:n>n;>a 0{a}{- 0 a}";
let out_neg = ilo()
.args([src, engine, "f", "-5"])
.output()
.expect("failed");
let out_pos = ilo()
.args([src, engine, "f", "7"])
.output()
.expect("failed");
assert!(
out_neg.status.success(),
"neg: {}",
String::from_utf8_lossy(&out_neg.stderr)
);
assert!(
out_pos.status.success(),
"pos: {}",
String::from_utf8_lossy(&out_pos.stderr)
);
assert_eq!(
String::from_utf8_lossy(&out_neg.stdout).trim(),
"5",
"engine={engine} abs(-5)"
);
assert_eq!(
String::from_utf8_lossy(&out_pos.stdout).trim(),
"7",
"engine={engine} abs(7)"
);
}
#[test]
fn abs_guard_tree() {
check_abs_guard("--vm");
}
#[test]
fn abs_guard_vm() {
check_abs_guard("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn abs_guard_cranelift() {
check_abs_guard("--jit");
}
fn check_single_atom_after_op(engine: &str) {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
"ilo_prefix_arg_single_{}_{}.ilo",
std::process::id(),
seq
));
std::fs::write(&path, "f a:n>n;a\ng x:n>n;f +x\n").unwrap();
let out = ilo()
.args([path.to_str().unwrap(), engine, "g", "3"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"engine={engine}: expected `f +x` to fail (parsed as infix on function ref); \
got success with stdout={}",
String::from_utf8_lossy(&out.stdout)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("ILO-T009") || stderr.contains("expects matching"),
"engine={engine}: expected ILO-T009 type error, got stderr={stderr}"
);
}
#[test]
fn single_atom_after_op_tree() {
check_single_atom_after_op("--vm");
}
#[test]
fn single_atom_after_op_vm() {
check_single_atom_after_op("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn single_atom_after_op_cranelift() {
check_single_atom_after_op("--jit");
}