use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run(engine: &str, src: &str, entry: &str) -> (bool, String) {
let out = ilo()
.args([src, engine, entry])
.output()
.expect("failed to run ilo");
(
out.status.success(),
String::from_utf8_lossy(&out.stderr).into_owned(),
)
}
fn check_top_level_binding(engine: &str, name: &str) {
let src = format!("{name}=5\nmain>n;42");
let (ok, stderr) = run(engine, &src, "main");
assert!(
!ok,
"engine={engine} name={name}: expected parse failure for top-level `{name}=...`"
);
assert!(
stderr.contains("ILO-P011"),
"engine={engine} name={name}: missing ILO-P011, stderr={stderr}"
);
assert!(
stderr.contains(&format!(
"`{name}` is a builtin and cannot be used as a binding name"
)),
"engine={engine} name={name}: missing friendly message, stderr={stderr}"
);
assert!(
stderr.contains("rename to something like"),
"engine={engine} name={name}: missing rename hint, stderr={stderr}"
);
}
fn check_in_fn_binding(engine: &str, name: &str) {
let src = format!("main>n;{name}=5;42");
let (ok, stderr) = run(engine, &src, "main");
assert!(
!ok,
"engine={engine} name={name}: expected parse failure for in-fn `{name}=...`"
);
assert!(
stderr.contains("ILO-P011"),
"engine={engine} name={name}: missing ILO-P011, stderr={stderr}"
);
assert!(
stderr.contains(&format!(
"`{name}` is a builtin and cannot be used as a binding name"
)),
"engine={engine} name={name}: missing friendly message, stderr={stderr}"
);
let p011_pos = stderr.find("ILO-P011").unwrap();
if let Some(t006_pos) = stderr.find("ILO-T006") {
assert!(
p011_pos < t006_pos,
"engine={engine} name={name}: ILO-P011 must come before any ILO-T006 cascade, stderr={stderr}"
);
}
}
const BINDING_NAMES: &[&str] = &[
"flat", "frq", "map", "flt", "cat", "len", "hd", "tl", "at", "ord", "srt", "sum",
];
#[test]
fn builtin_binding_rejected_in_fn_tree() {
for name in BINDING_NAMES {
check_in_fn_binding("--vm", name);
}
}
#[test]
fn builtin_binding_rejected_in_fn_vm() {
for name in BINDING_NAMES {
check_in_fn_binding("--vm", name);
}
}
#[test]
#[cfg(feature = "cranelift")]
fn builtin_binding_rejected_in_fn_cranelift() {
for name in BINDING_NAMES {
check_in_fn_binding("--jit", name);
}
}
#[test]
fn builtin_binding_rejected_top_level_tree() {
for name in BINDING_NAMES {
check_top_level_binding("--vm", name);
}
}
#[test]
fn builtin_binding_rejected_top_level_vm() {
for name in BINDING_NAMES {
check_top_level_binding("--vm", name);
}
}
#[test]
#[cfg(feature = "cranelift")]
fn builtin_binding_rejected_top_level_cranelift() {
for name in BINDING_NAMES {
check_top_level_binding("--jit", name);
}
}
const FLAT_REPRO: &str = "main>n;flat=5;flat";
fn check_flat_repro(engine: &str) {
let (ok, stderr) = run(engine, FLAT_REPRO, "main");
assert!(!ok, "engine={engine}: expected parse failure");
assert!(
stderr.contains("ILO-P011"),
"engine={engine}: missing ILO-P011, stderr={stderr}"
);
assert!(
stderr.contains("`flat` is a builtin"),
"engine={engine}: missing friendly message, stderr={stderr}"
);
let p011_pos = stderr.find("ILO-P011").unwrap();
if let Some(t006_pos) = stderr.find("ILO-T006") {
assert!(
p011_pos < t006_pos,
"engine={engine}: ILO-P011 must come before ILO-T006, stderr={stderr}"
);
}
}
#[test]
fn flat_repro_tree() {
check_flat_repro("--vm");
}
#[test]
fn flat_repro_vm() {
check_flat_repro("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn flat_repro_cranelift() {
check_flat_repro("--jit");
}
fn check_fld_keeps_specific_message(engine: &str) {
let (ok, stderr) = run(engine, "main>n;fld=5;fld", "main");
assert!(!ok, "engine={engine}: expected parse failure");
assert!(
stderr.contains("ILO-P011"),
"engine={engine}: missing ILO-P011, stderr={stderr}"
);
assert!(
stderr.contains("`fld` is reserved for the fold builtin"),
"engine={engine}: expected fld-specific message, stderr={stderr}"
);
}
#[test]
fn fld_specific_message_preserved_tree() {
check_fld_keeps_specific_message("--vm");
}
#[test]
fn fld_specific_message_preserved_vm() {
check_fld_keeps_specific_message("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn fld_specific_message_preserved_cranelift() {
check_fld_keeps_specific_message("--jit");
}
fn check_renamed_binding_works(engine: &str) {
let out = ilo()
.args(["main>n;myflat=5;myflat", engine, "main"])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"engine={engine}: rename should compile, stderr={}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("5"),
"engine={engine}: expected 5, got: {stdout}"
);
}
#[test]
fn rename_workaround_binding_tree() {
check_renamed_binding_works("--vm");
}
#[test]
fn rename_workaround_binding_vm() {
check_renamed_binding_works("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn rename_workaround_binding_cranelift() {
check_renamed_binding_works("--jit");
}