use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn write_src(name: &str, src: &str) -> std::path::PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let mut path = std::env::temp_dir();
path.push(format!("ilo_p2hof_{name}_{}_{n}.ilo", std::process::id()));
std::fs::write(&path, src).expect("write src");
path
}
fn run_ok(engine: &str, src: &str, entry: &str, args: &[&str]) -> String {
let path = write_src(entry, src);
let mut cmd = ilo();
cmd.arg(&path).arg(engine).arg(entry);
for a in args {
cmd.arg(a);
}
let out = cmd.output().expect("failed to run ilo");
let _ = std::fs::remove_file(&path);
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_all(src: &str, entry: &str, args: &[&str], expected: &str) {
#[cfg(feature = "cranelift")]
let engines: &[&str] = &["--vm", "--jit"];
#[cfg(not(feature = "cranelift"))]
let engines: &[&str] = &["--vm"];
for engine in engines {
let actual = run_ok(engine, src, entry, args);
assert_eq!(
actual, expected,
"engine {engine} produced {actual:?}, expected {expected:?} for src `{src}`"
);
}
}
fn run_err(engine: &str, src: &str, entry: &str, args: &[&str]) -> String {
let path = write_src(entry, src);
let mut cmd = ilo();
cmd.arg(&path).arg(engine).arg(entry);
for a in args {
cmd.arg(a);
}
let out = cmd.output().expect("failed to run ilo");
let _ = std::fs::remove_file(&path);
assert!(
!out.status.success(),
"ilo {engine} unexpectedly succeeded for `{src}`",
);
String::from_utf8_lossy(&out.stderr).to_string()
}
#[test]
fn partition_inline_lambda_non_capturing() {
let src = "f xs:L n>L L n;partition (x:n>b;>x 0) xs";
run_all(src, "f", &["[1,-2,3,-4,5]"], "[[1, 3, 5], [-2, -4]]");
}
#[test]
fn partition_inline_lambda_single_capture() {
let src = "f xs:L n t:n>L L n;partition (x:n>b;>x t) xs";
run_all(src, "f", &["[1,2,3,4,5]", "3"], "[[4, 5], [1, 2, 3]]");
}
#[test]
fn partition_inline_lambda_two_captures() {
let src = "f xs:L n lo:n hi:n>L L n;partition (x:n>b;&(>=x lo) <=x hi) xs";
run_all(
src,
"f",
&["[1,3,5,7,9,11]", "3", "7"],
"[[3, 5, 7], [1, 9, 11]]",
);
}
#[test]
fn partition_named_fn_ref() {
let src = "pos x:n>b;>x 0\nf xs:L n>L L n;partition pos xs";
run_all(src, "f", &["[1,-2,3,-4,5]"], "[[1, 3, 5], [-2, -4]]");
}
#[test]
fn partition_text_values() {
let src = "f ws:L t>L L t;partition (w:t>b;has w \"a\") ws";
run_all(
src,
"f",
&["[\"cat\",\"dog\",\"ant\",\"fish\"]"],
"[[cat, ant], [dog, fish]]",
);
}
#[test]
fn partition_empty_input() {
let src = "f xs:L n>L L n;partition (x:n>b;>x 0) xs";
run_all(src, "f", &["[]"], "[[], []]");
}
#[test]
fn partition_all_pass() {
let src = "f xs:L n>L L n;partition (x:n>b;>x 0) xs";
run_all(src, "f", &["[1,2,3]"], "[[1, 2, 3], []]");
}
#[test]
fn partition_all_fail() {
let src = "f xs:L n>L L n;partition (x:n>b;>x 100) xs";
run_all(src, "f", &["[1,2,3]"], "[[], [1, 2, 3]]");
}
#[test]
fn partition_non_bool_predicate_errors() {
let src = "bad x:n>n;+x 1\nf xs:L n>L L n;partition bad xs";
#[cfg(feature = "cranelift")]
let engines: &[&str] = &["--vm", "--jit"];
#[cfg(not(feature = "cranelift"))]
let engines: &[&str] = &["--vm"];
for engine in engines {
let stderr = run_err(engine, src, "f", &["[1,2,3]"]);
assert!(
stderr.contains("partition") && stderr.contains("bool"),
"engine {engine}: expected error mentioning partition + bool, got {stderr}"
);
}
}