fn eval(expr: &str) -> Result<sui_eval::value::Value, sui_eval::value::EvalError> {
sui_eval::eval::eval(expr)
}
fn eval_concrete(expr: &str) -> Result<sui_eval::value::Concrete, sui_eval::value::EvalError> {
eval(expr)?.demand()
}
#[test]
fn unused_let_binding_not_forced() {
let result = eval_concrete(r#"let x = throw "boom"; in 42"#);
assert_eq!(result.unwrap().as_int().unwrap(), 42);
}
#[test]
fn unused_attrset_value_not_forced() {
let result = eval_concrete(r#"{ a = 1; b = throw "boom"; }.a"#);
assert_eq!(result.unwrap().as_int().unwrap(), 1);
}
#[test]
fn unused_list_element_not_forced() {
let result = eval_concrete(r#"let xs = [ 1 (throw "boom") ]; in builtins.head xs"#);
let result2 = eval_concrete(r#"builtins.head [ 1 2 3 ]"#);
assert_eq!(result2.unwrap().as_int().unwrap(), 1);
}
#[test]
fn lambda_arg_not_forced_until_used() {
let result = eval_concrete(r#"(x: 42) (throw "boom")"#);
assert_eq!(result.unwrap().as_int().unwrap(), 42);
}
#[test]
fn short_circuit_and_does_not_force_rhs() {
let result = eval_concrete(r#"false && (throw "boom")"#);
assert_eq!(result.unwrap().as_bool().unwrap(), false);
}
#[test]
fn short_circuit_or_does_not_force_rhs() {
let result = eval_concrete(r#"true || (throw "boom")"#);
assert_eq!(result.unwrap().as_bool().unwrap(), true);
}
#[test]
fn short_circuit_implication_does_not_force_rhs() {
let result = eval_concrete(r#"false -> (throw "boom")"#);
assert_eq!(result.unwrap().as_bool().unwrap(), true);
}
#[test]
fn attr_names_does_not_force_values() {
let c = eval_concrete(r#"builtins.attrNames { a = throw "boom"; b = throw "boom2"; }"#).unwrap();
let list = c.as_list().unwrap();
assert_eq!(list.len(), 2);
}
#[test]
fn has_attr_does_not_force_value() {
let result = eval_concrete(r#"{ a = throw "boom"; } ? a"#);
assert_eq!(result.unwrap().as_bool().unwrap(), true);
}
#[test]
fn update_does_not_force_values() {
let result = eval_concrete(
r#"({ a = throw "boom"; } // { b = 1; }).b"#
);
assert_eq!(result.unwrap().as_int().unwrap(), 1);
}
#[test]
fn if_does_not_force_untaken_branch() {
let result = eval_concrete(r#"if true then 1 else (throw "boom")"#);
assert_eq!(result.unwrap().as_int().unwrap(), 1);
}
#[test]
fn thunk_forced_exactly_once() {
let result = eval_concrete(r#"let x = { a = 1; }; in x.a + x.a"#);
assert_eq!(result.unwrap().as_int().unwrap(), 2);
}
#[test]
fn fixpoint_simple() {
let result = eval_concrete(
r#"let fix = f: let x = f x; in x; in (fix (self: { a = 1; b = self.a + 1; })).b"#
);
assert_eq!(result.unwrap().as_int().unwrap(), 2);
}
#[test]
fn fixpoint_with_scope() {
let result = eval_concrete(
r#"let fix = f: let x = f x; in x; in (fix (self: with self; { a = 1; b = a + 1; })).b"#
);
assert_eq!(result.unwrap().as_int().unwrap(), 2);
}
#[test]
fn recursive_attrset_self_reference() {
let result = eval_concrete(r#"rec { a = 1; b = a + 1; }.b"#);
assert_eq!(result.unwrap().as_int().unwrap(), 2);
}
#[test]
fn infinite_recursion_detected() {
let result = eval(r#"let x = x; in x"#);
assert!(result.is_err());
}
#[test]
fn inherit_from_source_lazy() {
let result = eval_concrete(
r#"let x = { a = 1; b = throw "boom"; }; in let inherit (x) a; in a"#
);
assert_eq!(result.unwrap().as_int().unwrap(), 1);
}
#[test]
fn concrete_demand_on_int() {
let val = sui_eval::value::Value::Int(42);
let c = val.demand().unwrap();
assert_eq!(c.as_int().unwrap(), 42);
}
#[test]
fn concrete_demand_on_string() {
let val = sui_eval::value::Value::string("hello");
let c = val.demand().unwrap();
assert_eq!(c.as_str().unwrap(), "hello");
}
#[test]
fn concrete_demand_on_bool() {
let val = sui_eval::value::Value::Bool(true);
let c = val.demand().unwrap();
assert_eq!(c.as_bool().unwrap(), true);
}
#[test]
fn concrete_into_value_roundtrip() {
let val = sui_eval::value::Value::Int(99);
let c = val.demand().unwrap();
let back = c.into_value();
assert_eq!(back, sui_eval::value::Value::Int(99));
}
#[test]
fn foldl_strict_forces_accumulator() {
let result = eval_concrete(
r#"builtins.foldl' (a: b: a // b) {} [ { x = 1; } { y = 2; } ]"#
);
let c = result.unwrap();
let attrs = c.as_attrs().unwrap();
assert_eq!(attrs.get("x"), Some(&sui_eval::value::Value::Int(1)));
assert_eq!(attrs.get("y"), Some(&sui_eval::value::Value::Int(2)));
}
#[test]
fn map_attrs_is_lazy() {
let result = eval_concrete(
r#"(builtins.mapAttrs (n: v: v + 1) { a = 1; b = throw "boom"; }).a"#
);
assert_eq!(result.unwrap().as_int().unwrap(), 2);
}