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_all(env: &mut Env, src: &str) -> 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
}
#[test]
fn defn_docstring_is_readable_via_doc() {
let (_, mut env) = make_env();
let result = eval_all(
&mut env,
r#"(defn add1 "Adds one to x." [x] (+ x 1)) (doc add1)"#,
);
assert_eq!(result, Value::string("Adds one to x.".to_string()));
}
#[test]
fn def_docstring_is_readable_via_doc() {
let (_, mut env) = make_env();
let result = eval_all(&mut env, r#"(def answer "The answer." 42) (doc answer)"#);
assert_eq!(result, Value::string("The answer.".to_string()));
}
#[test]
fn defmacro_docstring_is_readable_via_doc() {
let (_, mut env) = make_env();
let result = eval_all(
&mut env,
r#"(defmacro my-when "Like when." [test & body] (list 'if test (cons 'do body) nil))
(doc my-when)"#,
);
assert_eq!(result, Value::string("Like when.".to_string()));
}
#[test]
fn doc_returns_nil_for_unresolvable_symbol() {
let (_, mut env) = make_env();
let result = eval_all(&mut env, "(doc definitely-not-a-thing-xyz)");
assert_eq!(result, Value::Nil);
}
#[test]
fn doc_works_on_native_builtins() {
let (_, mut env) = make_env();
let result = eval_all(&mut env, "(doc +)");
let Value::Str(s) = result else {
panic!("expected a docstring, got {result:?}");
};
assert!(s.get().contains("sum"));
}
#[test]
fn defn_without_docstring_has_no_doc() {
let (_, mut env) = make_env();
let result = eval_all(&mut env, "(defn plain [x] x) (doc plain)");
assert_eq!(result, Value::Nil);
}
#[test]
fn doc_data_reports_doc_and_arities_for_defn() {
let (_, mut env) = make_env();
let result = eval_all(
&mut env,
r#"(defn add
"Adds two numbers."
([x] x)
([x y] (+ x y)))
(doc-data (var add))"#,
);
let Value::Map(m) = result else {
panic!("expected a map, got {result:?}");
};
let doc = m
.get(&Value::keyword(cljrs_value::Keyword::parse("doc")))
.expect("expected :doc key");
assert_eq!(doc, Value::string("Adds two numbers.".to_string()));
let arities = m
.get(&Value::keyword(cljrs_value::Keyword::parse("arities")))
.expect("expected :arities key");
let Value::Vector(v) = arities else {
panic!("expected a vector of arities, got {arities:?}");
};
assert_eq!(v.get().count(), 2);
}
#[test]
fn doc_data_on_macro_hides_implicit_form_and_env_params() {
let (_, mut env) = make_env();
let result = eval_all(
&mut env,
r#"(defmacro my-macro "does a thing" [x] x)
(doc-data (var my-macro))"#,
);
let Value::Map(m) = result else {
panic!("expected a map, got {result:?}");
};
let arities = m
.get(&Value::keyword(cljrs_value::Keyword::parse("arities")))
.expect("expected :arities key");
let Value::Vector(v) = arities else {
panic!("expected a vector of arities, got {arities:?}");
};
let first = v
.get()
.nth(0)
.cloned()
.expect("expected at least one arity");
let Value::Vector(first_arity) = first else {
panic!("expected first arity to be a vector");
};
assert_eq!(first_arity.get().count(), 1);
}
#[test]
fn doc_data_on_native_fn_reports_synthetic_arities() {
let (_, mut env) = make_env();
let result = eval_all(&mut env, "(doc-data (var cons))");
let Value::Map(m) = result else {
panic!("expected a map, got {result:?}");
};
let arities = m
.get(&Value::keyword(cljrs_value::Keyword::parse("arities")))
.expect("expected :arities key");
assert_ne!(arities, Value::Nil);
}