use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run_ok(engine: &str, src: &str) -> String {
let out = ilo()
.args([src, engine, "f"])
.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) -> String {
let out = ilo()
.args([src, engine, "f"])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"ilo {engine} unexpectedly succeeded for `{src}`: stdout={}",
String::from_utf8_lossy(&out.stdout)
);
format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
)
}
fn check_all_str(src: &str, expected: &str) {
assert_eq!(run_ok("--run-tree", src), expected, "tree engine");
assert_eq!(run_ok("--run-vm", src), expected, "vm engine");
#[cfg(feature = "cranelift")]
assert_eq!(run_ok("--run-cranelift", src), expected, "cranelift engine");
}
fn check_all_num(src: &str, expected: f64) {
for engine in &["--run-tree", "--run-vm"] {
let actual = run_ok(engine, src).parse::<f64>().expect("number");
assert!(
(actual - expected).abs() < 1e-10,
"engine={engine} src=`{src}`: got {actual}, expected {expected}"
);
}
#[cfg(feature = "cranelift")]
{
let actual = run_ok("--run-cranelift", src)
.parse::<f64>()
.expect("number");
assert!(
(actual - expected).abs() < 1e-10,
"engine=cranelift src=`{src}`: got {actual}, expected {expected}"
);
}
}
#[test]
fn transpose_2x2() {
check_all_str("f>L (L n);transpose [[1,2],[3,4]]", "[[1, 3], [2, 4]]");
}
#[test]
fn transpose_2x3_yields_3x2() {
check_all_str(
"f>L (L n);transpose [[1,2,3],[4,5,6]]",
"[[1, 4], [2, 5], [3, 6]]",
);
}
#[test]
fn transpose_ragged_errors() {
let err_tree = run_err("--run-tree", "f>L (L n);transpose [[1,2],[3]]");
assert!(
err_tree.contains("transpose") || err_tree.contains("ragged"),
"tree: got: {err_tree}"
);
let err_vm = run_err("--run-vm", "f>L (L n);transpose [[1,2],[3]]");
assert!(
err_vm.contains("transpose") || err_vm.contains("ragged"),
"vm: got: {err_vm}"
);
}
#[test]
fn matmul_2x3_by_3x2_gives_2x2() {
check_all_str(
"f>L (L n);matmul [[1,2,3],[4,5,6]] [[7,8],[9,10],[11,12]]",
"[[58, 64], [139, 154]]",
);
}
#[test]
fn matmul_identity_2x2() {
check_all_str(
"f>L (L n);matmul [[1,0],[0,1]] [[5,6],[7,8]]",
"[[5, 6], [7, 8]]",
);
}
#[test]
fn matmul_shape_mismatch_errors() {
let err_tree = run_err(
"--run-tree",
"f>L (L n);matmul [[1,2,3],[4,5,6]] [[1,2],[3,4]]",
);
assert!(
err_tree.contains("matmul") || err_tree.contains("shape"),
"tree: got: {err_tree}"
);
let err_vm = run_err(
"--run-vm",
"f>L (L n);matmul [[1,2,3],[4,5,6]] [[1,2],[3,4]]",
);
assert!(
err_vm.contains("matmul") || err_vm.contains("shape"),
"vm: got: {err_vm}"
);
}
#[test]
fn dot_basic() {
check_all_num("f>n;dot [1,2,3] [4,5,6]", 32.0);
}
#[test]
fn dot_with_negatives() {
check_all_num("f>n;dot [1,2] [-1,1]", 1.0);
}
#[test]
fn dot_length_mismatch_errors() {
let err_tree = run_err("--run-tree", "f>n;dot [1,2,3] [1,2]");
assert!(
err_tree.contains("dot") || err_tree.contains("length"),
"tree: got: {err_tree}"
);
let err_vm = run_err("--run-vm", "f>n;dot [1,2,3] [1,2]");
assert!(
err_vm.contains("dot") || err_vm.contains("length"),
"vm: got: {err_vm}"
);
}