use std::sync::Arc;
use cljrs_reader::Parser;
use cljrs_runtime::env::env::{Env, GlobalEnv};
use cljrs_value::Value;
fn make_env() -> (Arc<GlobalEnv>, Env) {
let globals = cljrs_runtime::Runtime::builder()
.execution_mode(cljrs_runtime::ExecutionMode::TreeWalk)
.eager_clojure_test(true)
.build()
.expect("runtime")
.into_globals();
let env = Env::new(globals.clone(), "user");
(globals, env)
}
fn eval_src(src: &str, env: &mut Env) -> Value {
let mut parser = Parser::new(src.to_string(), "<test>".to_string());
let forms = parser.parse_all().expect("parse error");
let mut result = Value::Nil;
for form in forms {
result = cljrs_runtime::interp::eval::eval(&form, env).expect("eval error");
}
result
}
fn eval_fresh(src: &str) -> Value {
let (_, mut env) = make_env();
eval_src(src, &mut env)
}
#[test]
fn shared_atom_construct_and_deref() {
assert_eq!(eval_fresh("(deref (shared-atom 41))"), Value::Long(41));
assert_eq!(eval_fresh("@(shared-atom 41)"), Value::Long(41));
}
#[test]
fn shared_atom_predicate() {
assert_eq!(
eval_fresh("(shared-atom? (shared-atom 1))"),
Value::Bool(true)
);
assert_eq!(eval_fresh("(shared-atom? (atom 1))"), Value::Bool(false));
assert_eq!(eval_fresh("(shared-atom? 1)"), Value::Bool(false));
assert_eq!(eval_fresh("(atom? (shared-atom 1))"), Value::Bool(false));
}
#[test]
fn shared_atom_reset() {
let mut env = make_env().1;
eval_src("(def a (shared-atom 0))", &mut env);
assert_eq!(eval_src("(reset! a 99)", &mut env), Value::Long(99));
assert_eq!(eval_src("@a", &mut env), Value::Long(99));
}
#[test]
fn shared_atom_swap_counts() {
let mut env = make_env().1;
eval_src("(def a (shared-atom 0))", &mut env);
eval_src("(swap! a inc)", &mut env);
eval_src("(swap! a inc)", &mut env);
assert_eq!(eval_src("(swap! a + 10)", &mut env), Value::Long(12));
assert_eq!(eval_src("@a", &mut env), Value::Long(12));
}
#[test]
fn shared_atom_compare_and_set() {
let mut env = make_env().1;
eval_src("(def a (shared-atom 5))", &mut env);
assert_eq!(
eval_src("(compare-and-set! a 4 100)", &mut env),
Value::Bool(false)
);
assert_eq!(eval_src("@a", &mut env), Value::Long(5));
assert_eq!(
eval_src("(compare-and-set! a 5 100)", &mut env),
Value::Bool(true)
);
assert_eq!(eval_src("@a", &mut env), Value::Long(100));
}
#[test]
fn shared_atom_promotes_strings_and_keywords() {
let mut env = make_env().1;
eval_src("(def a (shared-atom \"hi\"))", &mut env);
assert_eq!(eval_src("@a", &mut env), Value::string("hi".to_string()));
eval_src("(reset! a :foo)", &mut env);
assert_eq!(eval_src("(= @a :foo)", &mut env), Value::Bool(true));
}
#[test]
fn shared_atom_rejects_non_promotable() {
let (_, mut env) = make_env();
let mut parser = Parser::new("(shared-atom (fn [] 1))".to_string(), "<test>".to_string());
let forms = parser.parse_all().expect("parse error");
let mut last = Ok(Value::Nil);
for form in forms {
last = cljrs_runtime::interp::eval::eval(&form, &mut env);
}
assert!(last.is_err(), "publishing a closure should fail");
}
#[test]
fn shared_atom_swap_rejects_non_promotable_result() {
let mut env = make_env().1;
eval_src("(def a (shared-atom 0))", &mut env);
let mut parser = Parser::new(
"(swap! a (fn [_] (fn [] 1)))".to_string(),
"<test>".to_string(),
);
let forms = parser.parse_all().expect("parse error");
let res = cljrs_runtime::interp::eval::eval(&forms[0], &mut env);
assert!(res.is_err(), "swapping in a closure should fail");
assert_eq!(eval_src("@a", &mut env), Value::Long(0));
}