//! Custom API tests. use std::sync::{Arc, Mutex}; use expy::{Context, EvalError, Value, eval_in}; #[test] fn function_constant() { let mut ctx = Context::new(); ctx.set("foo", |_: &mut Context, _: &_| Ok(Value::Integer(42))); assert_eq!(eval_in(&mut ctx, "foo()").unwrap().unwrap_integer(), 42); } #[test] fn function_pure() { let mut ctx = Context::new(); ctx.set("foo", |_: &mut Context, args: &[Value]| { let Some(&Value::Integer(x)) = args.get(0) else { return Err(EvalError::custom("wrong arguments")); }; Ok(Value::Integer(x * 2 + 1)) }); assert_eq!(eval_in(&mut ctx, "foo(3)").unwrap().unwrap_integer(), 7); assert_eq!(eval_in(&mut ctx, "foo(11)").unwrap().unwrap_integer(), 23); } #[test] fn function_pure_external() { fn foo(_: &mut Context, args: &[Value]) -> Result { let Some(&Value::Integer(x)) = args.get(0) else { return Err(EvalError::custom("wrong arguments")); }; Ok(Value::Integer(x * 2 + 1)) } let mut ctx = Context::new(); ctx.set("foo", foo); assert_eq!(eval_in(&mut ctx, "foo(3)").unwrap().unwrap_integer(), 7); assert_eq!(eval_in(&mut ctx, "foo(11)").unwrap().unwrap_integer(), 23); } #[test] fn function_stateful() { let mut ctx = Context::new(); ctx.set("inc", |ctx: &mut Context, _: &_| { let mut c = ctx.get("counter").and_then(Value::as_integer).copied().unwrap_or_default(); c += 1; ctx.set("counter", c); Ok(Value::Integer(c)) }); for c in 1..=100 { assert_eq!(eval_in(&mut ctx, "inc()").unwrap().unwrap_integer(), c); } } #[test] fn function_local() { let mut ctx = Context::new(); // we'd normally use atomics for counters but this is meant to be general let counter = Arc::new(Mutex::new(0)); ctx.set("inc", { let counter = counter.clone(); move |_: &mut Context, _: &_| { let mut c = counter.lock().unwrap(); *c += 1; Ok(Value::Integer(*c)) } }); for c in 1..=100 { assert_eq!(eval_in(&mut ctx, "inc()").unwrap().unwrap_integer(), c); } }