use aethershell::{env::Env, eval, parser, safety, value::Value};
use std::time::{Duration, Instant};
fn eval(code: &str) -> anyhow::Result<Value> {
let stmts = parser::parse_program(code)?;
let mut env = Env::default();
eval::eval_program(&stmts, &mut env)
}
const LONG_RUNNING: &str =
"range(0, 400000) | map(fn(x) => range(0, 50) | map(fn(y) => y + x) | length()) | length()";
#[test]
fn a_deadline_stops_a_long_evaluation() {
let started = Instant::now();
let result = {
let _guard = safety::enter_deadline(Duration::from_millis(300));
eval(LONG_RUNNING)
};
let elapsed = started.elapsed();
assert!(
result.is_err(),
"evaluation ran to completion despite an expired deadline — the \
interpreter is not checking it (took {elapsed:?})"
);
assert!(
elapsed < Duration::from_secs(20),
"evaluation stopped only after finishing its work, not at the \
deadline — took {elapsed:?}"
);
let msg = result.unwrap_err().to_string();
assert!(
msg.contains("time limit"),
"the error must say the work was cancelled for time, so a caller can \
tell it apart from a genuine failure; got {msg:?}"
);
}
#[test]
fn ordinary_evaluation_is_unaffected() {
let _guard = safety::enter_deadline(Duration::from_secs(60));
let out = eval("[1, 2, 3] | map(fn(x) => x * 2)").expect("ordinary work must not be cancelled");
assert!(format!("{out:?}").contains('6'), "got {out:?}");
}
#[test]
fn evaluation_without_a_deadline_is_never_interrupted() {
let out = eval("range(0, 20000) | map(fn(x) => x + 1) | length()")
.expect("with no deadline set, nothing may be cancelled");
assert!(format!("{out:?}").contains("20000"), "got {out:?}");
}
#[test]
fn a_deadline_does_not_leak_to_later_work_on_the_same_thread() {
{
let _guard = safety::enter_deadline(Duration::from_millis(1));
std::thread::sleep(Duration::from_millis(20));
let _ = eval(LONG_RUNNING); }
eval("[1, 2, 3] | map(fn(x) => x * 2)")
.expect("a dropped deadline must not poison later work on this thread");
}
#[test]
fn unbounded_recursion_is_refused_rather_than_fatal() {
let err = safety::with_eval_stack(|| eval("let f = fn(x) => f(x)\nf(1)"))
.expect_err("unbounded recursion must not be allowed to run");
assert!(
err.to_string().contains("recursion too deep"),
"it must fail for depth, not incidentally; got {err}"
);
}
#[test]
fn legitimately_deep_recursion_still_works() {
let out = safety::with_eval_stack(|| {
eval("let f = fn(n) => match n { 0 => 0, _ => f(n - 1) }\nf(1500)")
})
.expect("1500 levels is a reasonable program and must not be refused");
assert!(format!("{out:?}").contains('0'), "got {out:?}");
}