#![cfg(feature = "cranelift")]
use std::path::PathBuf;
use std::process::Command;
use std::sync::atomic::{AtomicU32, Ordering};
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
static COUNTER: AtomicU32 = AtomicU32::new(0);
fn tmp_paths(tag: &str) -> (PathBuf, PathBuf) {
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let pid = std::process::id();
let src = std::env::temp_dir().join(format!("ilo-aot-entry-{tag}-{pid}-{n}.ilo"));
let bin = std::env::temp_dir().join(format!("ilo-aot-entry-{tag}-{pid}-{n}.bin"));
(src, bin)
}
#[test]
fn aot_picks_main_over_first_declared_function() {
let (src, bin) = tmp_paths("picks-main");
std::fs::write(&src, "helper>n;42\nmain>n;helper\n").expect("write src");
let compile = ilo()
.args(["compile"])
.arg(&src)
.arg("-o")
.arg(&bin)
.output()
.expect("compile");
assert!(
compile.status.success(),
"compile failed: stderr={:?}",
String::from_utf8_lossy(&compile.stderr),
);
let run = Command::new(&bin).output().expect("run binary");
let stdout = String::from_utf8_lossy(&run.stdout);
let code = run.status.code().unwrap_or(-1);
assert_eq!(stdout.trim(), "42", "wrong stdout: {stdout:?}");
assert_eq!(code, 0, "wrong exit: {code}");
let _ = std::fs::remove_file(&src);
let _ = std::fs::remove_file(&bin);
}
#[test]
fn aot_errors_clearly_when_no_main_and_no_explicit_entry() {
let (src, bin) = tmp_paths("no-main");
std::fs::write(&src, "helper>n;42\ngo>n;helper\n").expect("write src");
let compile = ilo()
.args(["compile"])
.arg(&src)
.arg("-o")
.arg(&bin)
.output()
.expect("compile");
let stderr = String::from_utf8_lossy(&compile.stderr);
let code = compile.status.code().unwrap_or(-1);
assert_eq!(code, 1, "expected exit 1, got {code}; stderr={stderr:?}");
assert!(
stderr.contains("ILO-E801"),
"stderr should mention ILO-E801, got: {stderr:?}"
);
assert!(
stderr.contains("helper") && stderr.contains("go"),
"stderr should list available functions, got: {stderr:?}"
);
assert!(
!bin.exists(),
"no binary should be written on ILO-E801; found one at {bin:?}"
);
let _ = std::fs::remove_file(&src);
}
#[test]
fn aot_honours_explicit_entry_when_no_main_present() {
let (src, bin) = tmp_paths("explicit-entry");
std::fs::write(&src, "helper>n;42\ngo>n;helper\n").expect("write src");
let compile = ilo()
.args(["compile"])
.arg(&src)
.arg("-o")
.arg(&bin)
.arg("go")
.output()
.expect("compile");
assert!(
compile.status.success(),
"compile failed: stderr={:?}",
String::from_utf8_lossy(&compile.stderr),
);
let run = Command::new(&bin).output().expect("run binary");
assert_eq!(String::from_utf8_lossy(&run.stdout).trim(), "42");
assert_eq!(run.status.code().unwrap_or(-1), 0);
let _ = std::fs::remove_file(&src);
let _ = std::fs::remove_file(&bin);
}
#[test]
fn aot_uses_single_user_fn_when_only_one_defined() {
let (src, bin) = tmp_paths("single-fn");
std::fs::write(&src, "m>n;7\n").expect("write src");
let compile = ilo()
.args(["compile"])
.arg(&src)
.arg("-o")
.arg(&bin)
.output()
.expect("compile");
assert!(
compile.status.success(),
"compile failed: stderr={:?}",
String::from_utf8_lossy(&compile.stderr),
);
let run = Command::new(&bin).output().expect("run binary");
assert_eq!(String::from_utf8_lossy(&run.stdout).trim(), "7");
assert_eq!(run.status.code().unwrap_or(-1), 0);
let _ = std::fs::remove_file(&src);
let _ = std::fs::remove_file(&bin);
}