use std::path::PathBuf;
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
static COUNTER: AtomicUsize = AtomicUsize::new(0);
fn temp_out(label: &str) -> PathBuf {
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
let pid = std::process::id();
let mut p = std::env::temp_dir();
p.push(format!("ilo_cov_cl_{}_{}_{}", label, pid, n));
p
}
fn aot_compile(label: &str, src: &str) -> (bool, String) {
let out = temp_out(label);
let out_s = out.to_string_lossy().into_owned();
let res = ilo()
.args(["compile", src, "-o", &out_s])
.output()
.expect("failed to invoke ilo compile");
let stderr = String::from_utf8_lossy(&res.stderr).into_owned();
let _ = std::fs::remove_file(&out);
let _ = std::fs::remove_file(format!("{}.o", out_s));
(res.status.success(), stderr)
}
fn expect_codegen_ok(label: &str, src: &str) {
let (ok, stderr) = aot_compile(label, src);
if ok {
return;
}
let acceptable = stderr.contains("libilo")
|| stderr.contains("linker")
|| stderr.contains("cc")
|| stderr.contains("ld:")
|| stderr.contains("link")
|| stderr.contains("cannot find")
|| stderr.contains("AOT compile error");
assert!(
acceptable,
"{label}: codegen failed before reaching link step; stderr=\n{stderr}"
);
}
#[test]
fn aot_cov_simple_return_number() {
expect_codegen_ok("simple_n", "f>n;42");
}
#[test]
fn aot_cov_simple_args_arithmetic() {
expect_codegen_ok("args_arith", "f a:n b:n>n;+a *b -a /a b");
}
#[test]
fn aot_cov_transpose() {
expect_codegen_ok("transpose", "f m:L (L n)>L (L n);transpose m");
}
#[test]
fn aot_cov_matmul() {
expect_codegen_ok("matmul", "f a:L (L n) b:L (L n)>L (L n);matmul a b");
}
#[test]
fn aot_cov_dot() {
expect_codegen_ok("dot", "f a:L n b:L n>n;dot a b");
}
#[test]
fn aot_cov_det() {
expect_codegen_ok("det", "f m:L (L n)>n;det m");
}
#[test]
fn aot_cov_inv() {
expect_codegen_ok("inv", "f m:L (L n)>L (L n);inv m");
}
#[test]
fn aot_cov_solve() {
expect_codegen_ok("solve", "f a:L (L n) b:L n>L n;solve a b");
}
#[test]
fn aot_cov_sqrt_log_exp() {
expect_codegen_ok("sle", "f x:n>n;+(sqrt x) +(log x) (exp x)");
}
#[test]
fn aot_cov_sin_cos_tan() {
expect_codegen_ok("sct", "f x:n>n;+(sin x) +(cos x) (tan x)");
}
#[test]
fn aot_cov_log10_log2() {
expect_codegen_ok("logs", "f x:n>n;+(log10 x) (log2 x)");
}
#[test]
fn aot_cov_asin_acos_atan() {
expect_codegen_ok("inv_trig", "f x:n>n;+(asin x) +(acos x) (atan x)");
}
#[test]
fn aot_cov_atan2() {
expect_codegen_ok("atan2", "f y:n x:n>n;atan2 y x");
}
#[test]
fn aot_cov_fft() {
expect_codegen_ok("fft", "f xs:L n>L (L n);fft xs");
}
#[test]
fn aot_cov_ifft() {
expect_codegen_ok("ifft", "f p:L (L n)>L n;ifft p");
}
#[test]
fn aot_cov_cumsum() {
expect_codegen_ok("cumsum", "f xs:L n>L n;cumsum xs");
}
#[test]
fn aot_cov_median() {
expect_codegen_ok("median", "f xs:L n>n;median xs");
}
#[test]
fn aot_cov_quantile() {
expect_codegen_ok("quantile", "f xs:L n p:n>n;quantile xs p");
}
#[test]
fn aot_cov_stdev() {
expect_codegen_ok("stdev", "f xs:L n>n;stdev xs");
}
#[test]
fn aot_cov_variance() {
expect_codegen_ok("variance", "f xs:L n>n;variance xs");
}
#[test]
fn aot_cov_setunion() {
expect_codegen_ok("setunion", "f a:L n b:L n>L n;setunion a b");
}
#[test]
fn aot_cov_setinter() {
expect_codegen_ok("setinter", "f a:L n b:L n>L n;setinter a b");
}
#[test]
fn aot_cov_setdiff() {
expect_codegen_ok("setdiff", "f a:L n b:L n>L n;setdiff a b");
}
#[test]
fn aot_cov_window() {
expect_codegen_ok("window", "f xs:L n n:n>L (L n);window n xs");
}
#[test]
fn aot_cov_chunks() {
expect_codegen_ok("chunks", "f xs:L n n:n>L (L n);chunks n xs");
}
#[test]
fn aot_cov_zip() {
expect_codegen_ok("zip", "f xs:L n ys:L n>L (L n);zip xs ys");
}
#[test]
fn aot_cov_enumerate() {
expect_codegen_ok("enumerate", "f xs:L n>L (L n);enumerate xs");
}
#[test]
fn aot_cov_range() {
expect_codegen_ok("range", "f a:n b:n>L n;range a b");
}
#[test]
fn aot_cov_upr() {
expect_codegen_ok("upr", "f s:t>t;upr s");
}
#[test]
fn aot_cov_lwr() {
expect_codegen_ok("lwr", "f s:t>t;lwr s");
}
#[test]
fn aot_cov_cap() {
expect_codegen_ok("cap", "f s:t>t;cap s");
}
#[test]
fn aot_cov_ord() {
expect_codegen_ok("ord", "f s:t>n;ord s");
}
#[test]
fn aot_cov_chr() {
expect_codegen_ok("chr", "f n:n>t;chr n");
}
#[test]
fn aot_cov_chars() {
expect_codegen_ok("chars", "f s:t>L t;chars s");
}
#[test]
fn aot_cov_padl() {
expect_codegen_ok("padl", "f s:t w:n>t;padl s w");
}
#[test]
fn aot_cov_padr() {
expect_codegen_ok("padr", "f s:t w:n>t;padr s w");
}
#[test]
fn aot_cov_padlc() {
expect_codegen_ok("padlc", "f s:t w:n p:t>t;padl s w p");
}
#[test]
fn aot_cov_padrc() {
expect_codegen_ok("padrc", "f s:t w:n p:t>t;padr s w p");
}
#[test]
fn aot_cov_dtfmt() {
expect_codegen_ok("dtfmt", "f ts:n fm:t>R t t;dtfmt ts fm");
}
#[test]
fn aot_cov_dtparse() {
expect_codegen_ok("dtparse", "f s:t fm:t>R n t;dtparse s fm");
}
#[test]
fn aot_cov_rdjl() {
expect_codegen_ok("rdjl", "f p:t>L (R t t);rdjl p");
}
#[test]
fn aot_cov_getmany() {
expect_codegen_ok("getmany", "f urls:L t>L (R t t);get-many urls");
}
#[test]
fn aot_cov_posth() {
expect_codegen_ok(
"posth",
"f u:t b:t>R t t;h=mmap;h=mset h \"x-api-key\" \"secret\";pst u b h",
);
}
#[test]
fn aot_cov_srtdesc() {
expect_codegen_ok("rsrt", "f xs:L n>L n;rsrt xs");
}
#[test]
fn aot_cov_sum_avg() {
expect_codegen_ok("sum_avg", "f xs:L n>n;+(sum xs) (avg xs)");
}
#[test]
fn aot_cov_take_drop() {
expect_codegen_ok("takedrop", "f xs:L n n:n>L n;take n (drop n xs)");
}
#[test]
fn aot_cov_rgxsub() {
expect_codegen_ok("rgxsub", "f p:t r:t s:t>t;rgxsub p r s");
}
#[test]
fn aot_cov_frq() {
expect_codegen_ok("frq", "f xs:L n>M n n;frq xs");
}
#[test]
fn aot_cov_uniqby() {
expect_codegen_ok("uniqby", "key x:n>n;mod x 2;main xs:L n>L n;uniqby key xs");
}
#[test]
fn aot_cov_partition() {
expect_codegen_ok(
"partition",
"ev x:n>b;= 0 (mod x 2);main xs:L n>L (L n);partition ev xs",
);
}
#[test]
fn aot_cov_record_safe_field() {
expect_codegen_ok("rec_safe", "f x:t>R (O t) t;r=jpar! x;~r.?score");
}
#[test]
fn aot_cov_record_with_update() {
expect_codegen_ok(
"recwith",
"type pt{x:n;y:n}\nmkp>n;p=pt x:1 y:2;q=p with x:99 y:100;q.x",
);
}
#[test]
fn aot_cov_result_ops_chain() {
expect_codegen_ok("result_chain", "f x:n>R n t;>x 0 ~x;^\"neg\"");
}
#[test]
fn aot_cov_unwrap_default_via_num_bang() {
expect_codegen_ok("unwrap_default", "f s:t>R n t;v=num! s;~v");
}
#[test]
fn aot_cov_foreach_record_list() {
expect_codegen_ok(
"foreach_rec",
"type pt{x:n}\nsum-x xs:L pt>n;s=0;@p xs{s=+s p.x};s",
);
}
#[test]
fn aot_cov_nested_guards_loop() {
expect_codegen_ok(
"nested",
"f n:n>n;s=0;i=0;wh <i n{v=?>i 5 *i 2 i;s=+s v;i=+i 1};s",
);
}
#[test]
fn aot_cov_fmt2_with_padl() {
expect_codegen_ok("fmt2_padl", r#"f v:n>t;padl (fmt2 v 2) 8 "0""#);
}
#[test]
fn aot_cov_jpth_jdmp_jpar_chain() {
expect_codegen_ok("jchain", r#"f s:t>R t t;r=jpar! s;~jdmp r"#);
}
#[test]
fn aot_cov_jpar_list_codegen() {
expect_codegen_ok("jparlist", r#"f s:t>R n t;xs=jpar-list! s;~len xs"#);
}
#[test]
fn aot_cov_jpar_list_foreach_codegen() {
expect_codegen_ok(
"jparlist_foreach",
r#"f s:t>R n t;total=0;@x (jpar-list! s){total=+total x};~total"#,
);
}
#[test]
fn aot_cov_map_ops_full() {
expect_codegen_ok(
"map_full",
r#"f>L t;m=mmap;m=mset m "a" 1;m=mset m "b" 2;m=mdel m "a";?(mhas m "b"){mkeys m}{[]}"#,
);
}
#[test]
fn aot_cov_jmpnn_coalesce() {
expect_codegen_ok("coalesce", "f x:O n>n;x??42");
}
#[test]
fn aot_cov_bench_compile() {
let out = temp_out("bench");
let out_s = out.to_string_lossy().into_owned();
let res = ilo()
.args(["compile", "f x:n>n;+x 1", "--bench", "-o", &out_s])
.output()
.expect("failed to run ilo compile --bench");
let stderr = String::from_utf8_lossy(&res.stderr).into_owned();
let _ = std::fs::remove_file(&out);
let _ = std::fs::remove_file(format!("{}.o", out_s));
let _ = std::fs::remove_file(format!("{}_bench.c", out_s));
let _ = std::fs::remove_file(format!("{}_bench_c.o", out_s));
if !res.status.success() {
let acceptable = stderr.contains("libilo")
|| stderr.contains("linker")
|| stderr.contains("cc")
|| stderr.contains("ld:")
|| stderr.contains("link")
|| stderr.contains("bench")
|| stderr.contains("AOT compile error");
assert!(
acceptable,
"bench compile failed before reaching link step; stderr=\n{stderr}"
);
}
}
#[test]
fn aot_cov_named_entry() {
let out = temp_out("named_entry");
let out_s = out.to_string_lossy().into_owned();
let res = ilo()
.args([
"compile",
"helper x:n>n;*x 2\nmain>n;helper 21",
"-o",
&out_s,
"main",
])
.output()
.expect("failed to run ilo compile <named-entry>");
let stderr = String::from_utf8_lossy(&res.stderr).into_owned();
let _ = std::fs::remove_file(&out);
let _ = std::fs::remove_file(format!("{}.o", out_s));
if !res.status.success() {
let acceptable = stderr.contains("libilo")
|| stderr.contains("linker")
|| stderr.contains("cc")
|| stderr.contains("ld:")
|| stderr.contains("link")
|| stderr.contains("AOT compile error");
assert!(
acceptable,
"named-entry compile failed before reaching link step; stderr=\n{stderr}"
);
}
}
#[test]
fn aot_cov_undefined_entry_fails() {
let out = temp_out("noent");
let out_s = out.to_string_lossy().into_owned();
let res = ilo()
.args(["compile", "f x:n>n;+x 1", "-o", &out_s, "no_such_func"])
.output()
.expect("failed to run ilo compile");
let stderr = String::from_utf8_lossy(&res.stderr).into_owned();
let _ = std::fs::remove_file(&out);
let _ = std::fs::remove_file(format!("{}.o", out_s));
assert!(
!res.status.success(),
"compile with undefined entry should fail"
);
assert!(
stderr.contains("undefined function") || stderr.contains("AOT compile error"),
"expected undefined-function error, stderr=\n{stderr}"
);
}