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 NUM_SRC: &str = "f>L n;lst [10,20,30] 1 99";
fn check_num(engine: &str) {
assert_eq!(run(engine, NUM_SRC, "f"), "[10, 99, 30]", "engine={engine}");
}
#[test]
fn lst_num_tree() {
check_num("--run-tree");
}
#[test]
fn lst_num_vm() {
check_num("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn lst_num_cranelift() {
check_num("--jit");
}
const TEXT_SRC: &str = "f>L t;lst [\"a\",\"b\",\"c\"] 2 \"X\"";
fn check_text(engine: &str) {
assert_eq!(run(engine, TEXT_SRC, "f"), "[a, b, X]", "engine={engine}");
}
#[test]
fn lst_text_tree() {
check_text("--run-tree");
}
#[test]
fn lst_text_vm() {
check_text("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn lst_text_cranelift() {
check_text("--jit");
}
#[test]
fn lst_first_tree() {
assert_eq!(
run("--run-tree", "f>L n;lst [10,20,30] 0 99", "f"),
"[99, 20, 30]"
);
}
#[test]
fn lst_first_vm() {
assert_eq!(
run("--run-vm", "f>L n;lst [10,20,30] 0 99", "f"),
"[99, 20, 30]"
);
}
#[test]
fn lst_last_tree() {
assert_eq!(
run("--run-tree", "f>L n;lst [10,20,30] 2 99", "f"),
"[10, 20, 99]"
);
}
#[test]
fn lst_last_vm() {
assert_eq!(
run("--run-vm", "f>L n;lst [10,20,30] 2 99", "f"),
"[10, 20, 99]"
);
}
const NO_MUT_SRC: &str = "f>L n;xs=[1,2,3];ys=lst xs 0 99;xs";
fn check_no_mut(engine: &str) {
assert_eq!(run(engine, NO_MUT_SRC, "f"), "[1, 2, 3]", "engine={engine}");
}
#[test]
fn lst_no_mutation_tree() {
check_no_mut("--run-tree");
}
#[test]
fn lst_no_mutation_vm() {
check_no_mut("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn lst_no_mutation_cranelift() {
check_no_mut("--jit");
}
const OOR_SRC: &str = "f>L n;lst [10,20,30] 99 42";
fn check_oor_error(engine: &str) {
let out = ilo()
.args([OOR_SRC, engine, "f"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"engine={engine}: expected runtime error, got stdout={}",
String::from_utf8_lossy(&out.stdout)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("lst") || stderr.contains("range") || stderr.contains("ILO-R"),
"engine={engine}: expected lst/range/ILO-R error, got stderr={stderr}"
);
}
#[test]
fn lst_out_of_range_tree() {
check_oor_error("--run-tree");
}
#[test]
fn lst_out_of_range_vm() {
check_oor_error("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn lst_out_of_range_cranelift() {
check_oor_error("--jit");
}
const NEG_SRC: &str = "f>L n;lst [10,20,30] -1 42";
#[test]
fn lst_negative_tree() {
let out = ilo()
.args([NEG_SRC, "--run-tree", "f"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"tree: expected error for lst xs -1 v, got stdout={}",
String::from_utf8_lossy(&out.stdout)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("non-negative") || stderr.contains("ILO-R"),
"tree: expected non-negative/ILO-R error, got stderr={stderr}"
);
}
#[test]
fn lst_negative_vm() {
let out = ilo()
.args([NEG_SRC, "--run-vm", "f"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"vm: expected error for lst xs -1 v, got stdout={}",
String::from_utf8_lossy(&out.stdout)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("non-negative") || stderr.contains("lst") || stderr.contains("ILO-R"),
"vm: expected non-negative/lst/ILO-R error, got stderr={stderr}"
);
}
#[test]
#[cfg(feature = "cranelift")]
fn lst_negative_cranelift() {
let out = ilo()
.args([NEG_SRC, "--jit", "f"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"cranelift: expected error for lst xs -1 v, got stdout={}",
String::from_utf8_lossy(&out.stdout)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("non-negative") || stderr.contains("lst") || stderr.contains("ILO-R"),
"cranelift: expected non-negative/lst/ILO-R error, got stderr={stderr}"
);
}
#[test]
fn lst_type_mismatch_rejected() {
let out = ilo()
.args(["f>L n;lst [10,20,30] 1 \"X\"", "--run-tree", "f"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"expected verifier error for type mismatch, got stdout={}",
String::from_utf8_lossy(&out.stdout)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("ILO-T013") || stderr.contains("does not match"),
"expected ILO-T013, got stderr={stderr}"
);
}
const HISTOGRAM_SRC: &str = "f>L n;bins=[0,0,0,0];samples=[0,2,1,2,3,1,2,0];@s samples{c=at bins s;bins=lst bins s +c 1};bins";
fn check_histogram(engine: &str) {
assert_eq!(
run(engine, HISTOGRAM_SRC, "f"),
"[2, 2, 3, 1]",
"engine={engine}"
);
}
#[test]
fn lst_histogram_tree() {
check_histogram("--run-tree");
}
#[test]
fn lst_histogram_vm() {
check_histogram("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn lst_histogram_cranelift() {
check_histogram("--jit");
}