use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn engines() -> Vec<&'static str> {
let mut v = vec!["tree", "--run-vm"];
if cfg!(feature = "cranelift") {
v.push("--jit");
}
v
}
fn run_ok(engine: &str, src: &str, fn_name: &str) -> String {
let out = match engine {
"tree" => ilo()
.args(["run", src, fn_name])
.output()
.expect("failed to run ilo"),
_ => ilo()
.args([src, engine, fn_name])
.output()
.expect("failed to run ilo"),
};
assert!(
out.status.success(),
"ilo {engine} failed for `{src}` fn={fn_name}: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn run_err(engine: &str, src: &str, fn_name: &str) -> String {
let out = match engine {
"tree" => ilo()
.args(["run", src, fn_name])
.output()
.expect("failed to run ilo"),
_ => ilo()
.args([src, engine, fn_name])
.output()
.expect("failed to run ilo"),
};
assert!(
!out.status.success(),
"ilo {engine} unexpectedly succeeded for `{src}`: stdout={}",
String::from_utf8_lossy(&out.stdout)
);
String::from_utf8_lossy(&out.stderr).to_string()
}
#[test]
fn ewm_basic_half_alpha() {
let src = "f>L n;ewm [1, 2, 3, 4, 5] 0.5";
for e in engines() {
let got = run_ok(e, src, "f");
assert_eq!(got, "[1, 1.5, 2.25, 3.125, 4.0625]", "engine={e}");
}
}
#[test]
fn ewm_empty_list_is_empty() {
let src = "f>L n;ewm [] 0.5";
for e in engines() {
assert_eq!(run_ok(e, src, "f"), "[]", "engine={e}");
}
}
#[test]
fn ewm_single_element_unchanged() {
let src = "f>L n;ewm [42] 0.3";
for e in engines() {
assert_eq!(run_ok(e, src, "f"), "[42]", "engine={e}");
}
}
#[test]
fn ewm_alpha_zero_freezes_first_value() {
let src = "f>L n;ewm [1, 2, 3, 4, 5] 0";
for e in engines() {
assert_eq!(run_ok(e, src, "f"), "[1, 1, 1, 1, 1]", "engine={e}");
}
}
#[test]
fn ewm_alpha_one_is_identity() {
let src = "f>L n;ewm [1, 2, 3, 4, 5] 1";
for e in engines() {
assert_eq!(run_ok(e, src, "f"), "[1, 2, 3, 4, 5]", "engine={e}");
}
}
#[test]
fn ewm_alpha_above_one_errors() {
let src = "f>L n;ewm [1, 2, 3] 1.5";
for e in engines() {
let stderr = run_err(e, src, "f");
assert!(
stderr.contains("ewm") && stderr.contains("[0, 1]"),
"engine={e}: stderr={stderr}"
);
assert!(
stderr.contains("ILO-R009"),
"engine={e}: expected ILO-R009, got stderr={stderr}"
);
}
}
#[test]
fn ewm_alpha_below_zero_errors() {
let src = "f>L n;a=0 - 0.1;ewm [1, 2, 3] a";
for e in engines() {
let stderr = run_err(e, src, "f");
assert!(
stderr.contains("ewm") && stderr.contains("[0, 1]"),
"engine={e}: stderr={stderr}"
);
assert!(
stderr.contains("ILO-R009"),
"engine={e}: expected ILO-R009, got stderr={stderr}"
);
}
}
#[test]
fn ewm_constant_input_is_constant() {
let src = "f>L n;ewm [7, 7, 7, 7] 0.3";
for e in engines() {
assert_eq!(run_ok(e, src, "f"), "[7, 7, 7, 7]", "engine={e}");
}
}
#[test]
fn ewm_alpha_nan_errors() {
let src = "f>L n;a=sqrt (0 - 1);ewm [1, 2, 3] a";
for e in engines() {
let stderr = run_err(e, src, "f");
assert!(
stderr.contains("ewm") && stderr.contains("[0, 1]"),
"engine={e}: stderr={stderr}"
);
assert!(
stderr.contains("ILO-R009"),
"engine={e}: expected ILO-R009, got stderr={stderr}"
);
assert!(
stderr.contains("NaN"),
"engine={e}: expected the NaN value in the message, got stderr={stderr}"
);
}
}
#[test]
fn ewm_alpha_above_one_message_mentions_value() {
let src = "f>L n;ewm [1, 2, 3] 1.5";
for e in engines() {
let stderr = run_err(e, src, "f");
assert!(
stderr.contains("1.5"),
"engine={e}: expected offending alpha 1.5 in message, got stderr={stderr}"
);
}
}
#[test]
fn ewm_propagates_nan_in_xs() {
let src = "f>L n;n=sqrt (0 - 1);ewm [1, n, 3] 0.5";
for e in engines() {
let got = run_ok(e, src, "f");
assert_eq!(got, "[1, NaN, NaN]", "engine={e}");
}
}
#[test]
fn ewm_xs_not_list_verify_error() {
let src = "f>L n;ewm 5 0.5";
let stderr = run_err("--run-vm", src, "f");
assert!(
stderr.contains("ILO-T013"),
"expected ILO-T013, got: {stderr}"
);
assert!(
stderr.contains("ewm") && stderr.contains("L n"),
"expected message to mention ewm and L n, got: {stderr}"
);
}
#[test]
fn ewm_xs_list_of_text_verify_error() {
let src = r#"f>L n;ewm ["a", "b"] 0.5"#;
let stderr = run_err("--run-vm", src, "f");
assert!(
stderr.contains("ILO-T013"),
"expected ILO-T013, got: {stderr}"
);
assert!(
stderr.contains("L t"),
"expected message to mention L t (the wrong inner type), got: {stderr}"
);
}
#[test]
fn ewm_alpha_not_number_verify_error() {
let src = r#"f>L n;ewm [1, 2] "x""#;
let stderr = run_err("--run-vm", src, "f");
assert!(
stderr.contains("ILO-T013"),
"expected ILO-T013, got: {stderr}"
);
assert!(
stderr.contains("second arg") && stderr.contains("a must be n"),
"expected message to flag the alpha arg, got: {stderr}"
);
}