1use std::sync::Arc;
14
15use cljrs_eval::GlobalEnv;
16#[cfg(not(target_arch = "wasm32"))]
17use cljrs_gc::GcConfig;
18
19mod core_async;
20#[cfg(not(target_arch = "wasm32"))]
22mod edn;
23#[cfg(not(target_arch = "wasm32"))]
24pub mod io;
25mod set;
26mod string;
27const CLOJURE_TEST_SRC: &str = include_str!("clojure/test.cljrs");
30const CLOJURE_STRING_SRC: &str = include_str!("clojure/string.cljrs");
31const CLOJURE_SET_SRC: &str = include_str!("clojure/set.cljrs");
32const CLOJURE_TEMPLATE_SRC: &str = include_str!("clojure/template.cljrs");
33#[cfg(not(target_arch = "wasm32"))]
34const CLOJURE_RUST_IO_SRC: &str = include_str!("clojure/rust/io.cljrs");
35#[cfg(not(target_arch = "wasm32"))]
36const CLOJURE_EDN_SRC: &str = include_str!("clojure/edn.cljrs");
37const CLOJURE_WALK_SRC: &str = include_str!("clojure/walk.cljrs");
38const CLOJURE_DATA_SRC: &str = include_str!("clojure/data.cljrs");
39const COLJURE_ZIP_SRC: &str = include_str!("clojure/zip.cljrs");
40const CLOJURE_SPEC_ALPHA_SRC: &str = include_str!("clojure/spec/alpha.cljrs");
41const CLOJURE_SPEC_GEN_ALPHA_SRC: &str = include_str!("clojure/spec/gen/alpha.cljrs");
42const CLOJURE_SPEC_TEST_ALPHA_SRC: &str = include_str!("clojure/spec/test/alpha.cljrs");
43
44macro_rules! register_fns {
49 ($globals:expr, $ns:expr, [ $( ($name:expr, $arity:expr, $func:expr) ),* $(,)? ]) => {{
50 use cljrs_gc::GcPtr;
51 use cljrs_value::{NativeFn, Value};
52 let ns: &str = $ns;
53 $(
54 {
55 let nf = NativeFn::new($name, $arity, $func);
56 $globals.intern(ns, std::sync::Arc::from($name), Value::NativeFunction(GcPtr::new(nf)));
57 }
58 )*
59 }};
60}
61
62pub(crate) use register_fns;
63
64pub fn register(globals: &Arc<GlobalEnv>) {
73 string::register(globals, "clojure.string");
76 globals.register_builtin_source("clojure.string", CLOJURE_STRING_SRC);
77
78 set::register(globals, "clojure.set");
80 globals.register_builtin_source("clojure.set", CLOJURE_SET_SRC);
81
82 globals.register_builtin_source("clojure.template", CLOJURE_TEMPLATE_SRC);
84
85 globals.register_builtin_source("clojure.test", CLOJURE_TEST_SRC);
87
88 #[cfg(not(target_arch = "wasm32"))]
90 {
91 io::register(globals, "clojure.rust.io");
92 globals.register_builtin_source("clojure.rust.io", CLOJURE_RUST_IO_SRC);
93
94 edn::register(globals, "clojure.edn");
95 globals.register_builtin_source("clojure.edn", CLOJURE_EDN_SRC);
96 }
97
98 globals.register_builtin_source("clojure.walk", CLOJURE_WALK_SRC);
100
101 globals.register_builtin_source("clojure.data", CLOJURE_DATA_SRC);
103
104 globals.register_builtin_source("clojure.zip", COLJURE_ZIP_SRC);
106
107 globals.register_builtin_source("clojure.spec.alpha", CLOJURE_SPEC_ALPHA_SRC);
109
110 globals.register_builtin_source("clojure.spec.gen.alpha", CLOJURE_SPEC_GEN_ALPHA_SRC);
112
113 globals.register_builtin_source("clojure.spec.test.alpha", CLOJURE_SPEC_TEST_ALPHA_SRC);
115}
116
117#[cfg(not(target_arch = "wasm32"))]
127pub fn standard_env_no_ir() -> Arc<GlobalEnv> {
128 let globals = cljrs_eval::standard_env_minimal_no_ir();
129 register(&globals);
130
131 cljrs_gc::HEAP.set_config_from_env();
132 let roots_gc = globals.clone();
133 cljrs_gc::HEAP.register_root_tracer(move |visitor| {
134 use cljrs_gc::GcVisitor as _;
135 let namespaces = roots_gc.namespaces.read().unwrap();
136 for ns_ptr in namespaces.values() {
137 visitor.visit(ns_ptr);
138 }
139 });
140
141 globals
142}
143
144#[cfg(not(target_arch = "wasm32"))]
150pub fn standard_env() -> Arc<GlobalEnv> {
151 let globals = cljrs_eval::standard_env_minimal();
152 register(&globals);
153
154 cljrs_gc::HEAP.set_config_from_env();
158 let roots_gc = globals.clone();
159 cljrs_gc::HEAP.register_root_tracer(move |visitor| {
160 use cljrs_gc::GcVisitor as _;
161 let namespaces = roots_gc.namespaces.read().unwrap();
162 for ns_ptr in namespaces.values() {
163 visitor.visit(ns_ptr);
164 }
165 });
166
167 cljrs_eval::mark_compiler_ready(&globals);
169
170 globals
171}
172
173#[cfg(not(target_arch = "wasm32"))]
175pub fn standard_env_with_paths(source_paths: Vec<std::path::PathBuf>) -> Arc<GlobalEnv> {
176 let globals = standard_env();
177 globals.set_source_paths(source_paths);
178 globals
179}
180
181#[cfg(not(target_arch = "wasm32"))]
183pub fn standard_env_with_paths_and_config(
184 source_paths: Vec<std::path::PathBuf>,
185 gc_config: Arc<GcConfig>,
186) -> Arc<GlobalEnv> {
187 let globals = standard_env();
188 globals.set_source_paths(source_paths);
189 globals.set_gc_config(gc_config.clone());
190 cljrs_gc::HEAP.set_config(gc_config);
193 globals
194}
195
196#[cfg(test)]
199mod tests {
200 use super::*;
201 use cljrs_eval::{Env, EvalResult, eval};
202 use cljrs_reader::Parser;
203 use cljrs_value::{Keyword, Value};
204
205 fn make_env() -> (Arc<GlobalEnv>, Env) {
206 let globals = standard_env();
207 let env = Env::new(globals.clone(), "user");
208 (globals, env)
209 }
210
211 #[allow(clippy::result_large_err)]
212 fn run(src: &str, env: &mut Env) -> EvalResult {
213 let mut parser = Parser::new(src.to_string(), "<test>".to_string());
214 let forms = parser.parse_all().expect("parse error");
215 let mut result = Value::Nil;
216 for form in forms {
217 result = eval(&form, env)?;
218 }
219 Ok(result)
220 }
221
222 #[test]
225 fn test_string_upper_lower() {
226 let (_, mut env) = make_env();
227 run("(require '[clojure.string :as str])", &mut env).unwrap();
228 assert_eq!(
229 run("(str/upper-case \"hello\")", &mut env).unwrap(),
230 Value::string("HELLO")
231 );
232 assert_eq!(
233 run("(str/lower-case \"WORLD\")", &mut env).unwrap(),
234 Value::string("world")
235 );
236 }
237
238 #[test]
239 fn test_string_trim() {
240 let (_, mut env) = make_env();
241 run("(require '[clojure.string :as str])", &mut env).unwrap();
242 assert_eq!(
243 run("(str/trim \" hello \")", &mut env).unwrap(),
244 Value::string("hello")
245 );
246 assert_eq!(
247 run("(str/triml \" hi\")", &mut env).unwrap(),
248 Value::string("hi")
249 );
250 assert_eq!(
251 run("(str/trimr \"hi \")", &mut env).unwrap(),
252 Value::string("hi")
253 );
254 }
255
256 #[test]
257 fn test_string_predicates() {
258 let (_, mut env) = make_env();
259 run("(require '[clojure.string :as str])", &mut env).unwrap();
260 assert_eq!(
261 run("(str/blank? \" \")", &mut env).unwrap(),
262 Value::Bool(true)
263 );
264 assert_eq!(
265 run("(str/blank? \"x\")", &mut env).unwrap(),
266 Value::Bool(false)
267 );
268 assert_eq!(
269 run("(str/starts-with? \"hello\" \"hel\")", &mut env).unwrap(),
270 Value::Bool(true)
271 );
272 assert_eq!(
273 run("(str/ends-with? \"hello\" \"llo\")", &mut env).unwrap(),
274 Value::Bool(true)
275 );
276 assert_eq!(
277 run("(str/includes? \"hello\" \"ell\")", &mut env).unwrap(),
278 Value::Bool(true)
279 );
280 }
281
282 #[test]
283 fn test_string_replace() {
284 let (_, mut env) = make_env();
285 run("(require '[clojure.string :as str])", &mut env).unwrap();
286 assert_eq!(
287 run("(str/replace \"aabbcc\" \"bb\" \"XX\")", &mut env).unwrap(),
288 Value::string("aaXXcc")
289 );
290 assert_eq!(
291 run("(str/replace-first \"aabbcc\" \"a\" \"X\")", &mut env).unwrap(),
292 Value::string("Xabbcc")
293 );
294 assert_eq!(
296 run("(str/replace \"--host\" #\"^--\" \"\")", &mut env).unwrap(),
297 Value::string("host")
298 );
299 assert_eq!(
300 run("(str/replace \"aaa\" #\"a\" \"b\")", &mut env).unwrap(),
301 Value::string("bbb")
302 );
303 assert_eq!(
304 run("(str/replace-first \"aaa\" #\"a\" \"b\")", &mut env).unwrap(),
305 Value::string("baa")
306 );
307 assert_eq!(
308 run("(str/replace-first \"--host\" #\"^--\" \"\")", &mut env).unwrap(),
309 Value::string("host")
310 );
311 }
312
313 #[test]
314 fn test_string_split_join() {
315 let (_, mut env) = make_env();
316 run("(require '[clojure.string :as str])", &mut env).unwrap();
317 let v = run("(str/split \"a,b,c\" \",\")", &mut env).unwrap();
318 assert!(matches!(v, Value::Vector(_)));
319 assert_eq!(
320 run("(str/join \"-\" [\"a\" \"b\" \"c\"])", &mut env).unwrap(),
321 Value::string("a-b-c")
322 );
323 }
324
325 #[test]
326 fn test_string_join_char_elements() {
327 let (_, mut env) = make_env();
328 run("(require '[clojure.string :as str])", &mut env).unwrap();
329 assert_eq!(
331 run(r"(str/join [\8 \0])", &mut env).unwrap(),
332 Value::string("80")
333 );
334 assert_eq!(
335 run(r"(str/join \- [\8 \0])", &mut env).unwrap(),
336 Value::string("8-0")
337 );
338 assert_eq!(
340 run(r#"(str/join "-" [nil "a" nil])"#, &mut env).unwrap(),
341 Value::string("-a-")
342 );
343 }
344
345 #[test]
346 fn test_string_capitalize() {
347 let (_, mut env) = make_env();
348 run("(require '[clojure.string :as str])", &mut env).unwrap();
349 assert_eq!(
350 run("(str/capitalize \"hello world\")", &mut env).unwrap(),
351 Value::string("Hello world")
352 );
353 }
354
355 #[test]
356 fn test_string_split_lines() {
357 let (_, mut env) = make_env();
358 run("(require '[clojure.string :as str])", &mut env).unwrap();
359 let v = run("(str/split-lines \"a\\nb\\nc\")", &mut env).unwrap();
360 assert!(matches!(v, Value::Vector(_)));
361 }
362
363 #[test]
366 fn test_set_union() {
367 let (_, mut env) = make_env();
368 run("(require '[clojure.set :as s])", &mut env).unwrap();
369 let v = run("(s/union #{1 2} #{2 3})", &mut env).unwrap();
370 match v {
371 Value::Set(s) => assert_eq!(s.count(), 3),
372 other => panic!("expected set, got {other:?}"),
373 }
374 }
375
376 #[test]
377 fn test_set_intersection() {
378 let (_, mut env) = make_env();
379 run("(require '[clojure.set :as s])", &mut env).unwrap();
380 let v = run("(s/intersection #{1 2 3} #{2 3 4})", &mut env).unwrap();
381 match v {
382 Value::Set(s) => assert_eq!(s.count(), 2),
383 other => panic!("expected set, got {other:?}"),
384 }
385 }
386
387 #[test]
388 fn test_set_difference() {
389 let (_, mut env) = make_env();
390 run("(require '[clojure.set :as s])", &mut env).unwrap();
391 let v = run("(s/difference #{1 2 3} #{2 3})", &mut env).unwrap();
392 match v {
393 Value::Set(s) => assert_eq!(s.count(), 1),
394 other => panic!("expected set, got {other:?}"),
395 }
396 }
397
398 #[test]
399 fn test_set_subset_superset() {
400 let (_, mut env) = make_env();
401 run("(require '[clojure.set :as s])", &mut env).unwrap();
402 assert_eq!(
403 run("(s/subset? #{1 2} #{1 2 3})", &mut env).unwrap(),
404 Value::Bool(true)
405 );
406 assert_eq!(
407 run("(s/superset? #{1 2 3} #{1 2})", &mut env).unwrap(),
408 Value::Bool(true)
409 );
410 }
411
412 #[test]
413 fn test_set_map_invert() {
414 let (_, mut env) = make_env();
415 run("(require '[clojure.set :as s])", &mut env).unwrap();
416 let v = run("(s/map-invert {:a 1 :b 2})", &mut env).unwrap();
417 assert!(matches!(v, Value::Map(_)));
418 }
419
420 #[test]
423 fn test_clojure_test_lazy_load() {
424 std::thread::Builder::new()
428 .stack_size(16 * 1024 * 1024)
429 .spawn(|| {
430 let (_, mut env) = make_env();
431 run(
434 "(require '[clojure.test :refer [is deftest run-tests]])",
435 &mut env,
436 )
437 .unwrap();
438 let v = run("(is (= 1 1))", &mut env).unwrap();
439 assert_eq!(v, Value::Bool(true));
440 })
441 .unwrap()
442 .join()
443 .unwrap();
444 }
445
446 fn run_with_big_stack<F: FnOnce() + Send + 'static>(body: F) {
452 std::thread::Builder::new()
453 .stack_size(16 * 1024 * 1024)
454 .spawn(body)
455 .unwrap()
456 .join()
457 .unwrap();
458 }
459
460 #[test]
461 fn test_spec_def_and_valid_conform() {
462 run_with_big_stack(|| {
463 let (_, mut env) = make_env();
464 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
465 assert_eq!(
466 run("(s/def ::x even?)", &mut env).unwrap(),
467 Value::keyword(Keyword::qualified("user", "x"))
468 );
469 assert_eq!(
470 run("(s/valid? ::x 4)", &mut env).unwrap(),
471 Value::Bool(true)
472 );
473 assert_eq!(
474 run("(s/valid? ::x 3)", &mut env).unwrap(),
475 Value::Bool(false)
476 );
477 assert_eq!(run("(s/conform ::x 4)", &mut env).unwrap(), Value::Long(4));
478 assert_eq!(
479 run("(s/invalid? (s/conform ::x 3))", &mut env).unwrap(),
480 Value::Bool(true)
481 );
482 });
483 }
484
485 #[test]
486 fn test_spec_set_and_keyword_ref() {
487 run_with_big_stack(|| {
488 let (_, mut env) = make_env();
489 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
490 run("(s/def ::color #{:red :green})", &mut env).unwrap();
491 assert_eq!(
492 run("(s/valid? ::color :red)", &mut env).unwrap(),
493 Value::Bool(true)
494 );
495 assert_eq!(
496 run("(s/valid? ::color :blue)", &mut env).unwrap(),
497 Value::Bool(false)
498 );
499
500 run("(s/def ::x even?)", &mut env).unwrap();
502 run("(s/def ::y ::x)", &mut env).unwrap();
503 assert_eq!(
504 run("(s/valid? ::y 4)", &mut env).unwrap(),
505 Value::Bool(true)
506 );
507 assert_eq!(
508 run("(s/valid? ::y 3)", &mut env).unwrap(),
509 Value::Bool(false)
510 );
511 });
512 }
513
514 #[test]
515 fn test_spec_forward_reference() {
516 run_with_big_stack(|| {
517 let (_, mut env) = make_env();
518 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
519 run("(s/def ::a ::b)", &mut env).unwrap();
521 run("(s/def ::b string?)", &mut env).unwrap();
522 assert_eq!(
523 run(r#"(s/conform ::a "hi")"#, &mut env).unwrap(),
524 Value::string("hi")
525 );
526 assert_eq!(
527 run("(s/invalid? (s/conform ::a 5))", &mut env).unwrap(),
528 Value::Bool(true)
529 );
530 });
531 }
532
533 #[test]
534 fn test_spec_and_or() {
535 run_with_big_stack(|| {
536 let (_, mut env) = make_env();
537 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
538 assert_eq!(
539 run("(s/valid? (s/and int? even?) 4)", &mut env).unwrap(),
540 Value::Bool(true)
541 );
542 assert_eq!(
543 run("(s/valid? (s/and int? even?) 3)", &mut env).unwrap(),
544 Value::Bool(false)
545 );
546 let v = run("(s/conform (s/or :i int? :s string?) 5)", &mut env).unwrap();
547 match v {
548 Value::Vector(vec) => {
549 let items = vec.get().iter().cloned().collect::<Vec<_>>();
550 assert_eq!(items.len(), 2);
551 assert_eq!(items[0], Value::keyword(Keyword::simple("i")));
552 assert_eq!(items[1], Value::Long(5));
553 }
554 other => panic!("expected [:i 5], got {other:?}"),
555 }
556 });
557 }
558
559 #[test]
560 fn test_spec_spec_and_registry_introspection() {
561 run_with_big_stack(|| {
562 let (_, mut env) = make_env();
563 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
564 assert_eq!(run("(s/spec? even?)", &mut env).unwrap(), Value::Nil);
567 let is_spec = run("(s/spec? (s/and int? even?))", &mut env).unwrap();
568 assert!(
569 !matches!(is_spec, Value::Nil),
570 "expected a truthy spec object"
571 );
572
573 run("(s/def ::x even?)", &mut env).unwrap();
574 assert!(!matches!(
575 run("(s/get-spec ::x)", &mut env).unwrap(),
576 Value::Nil
577 ));
578 assert!(matches!(
579 run("(s/get-spec ::does-not-exist)", &mut env).unwrap(),
580 Value::Nil
581 ));
582
583 run("(s/def ::pos-even (s/and int? even? pos?))", &mut env).unwrap();
584 let form_v = run("(s/form ::pos-even)", &mut env).unwrap();
585 assert_eq!(format!("{form_v}"), "(and int? even? pos?)");
586 let describe_v = run("(s/describe ::pos-even)", &mut env).unwrap();
587 assert_eq!(format!("{describe_v}"), "(and int? even? pos?)");
588 });
589 }
590
591 #[test]
592 fn test_spec_conform_unregistered_keyword_throws() {
593 run_with_big_stack(|| {
594 let (_, mut env) = make_env();
595 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
596 let err = run("(s/conform ::does-not-exist 1)", &mut env).unwrap_err();
597 let msg = format!("{err}");
598 assert!(
599 msg.contains("Unable to resolve spec"),
600 "expected 'Unable to resolve spec' in error, got: {msg}"
601 );
602 });
603 }
604
605 #[track_caller]
609 fn assert_bool(src: &str, expected: bool, env: &mut Env) {
610 assert_eq!(
611 run(src, env).unwrap(),
612 Value::Bool(expected),
613 "expression: {src}"
614 );
615 }
616
617 #[test]
618 fn test_spec_keys_req_opt() {
619 run_with_big_stack(|| {
620 let (_, mut env) = make_env();
621 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
622 run("(s/def ::a int?) (s/def ::b string?)", &mut env).unwrap();
623 run("(s/def ::m (s/keys :req [::a] :opt [::b]))", &mut env).unwrap();
624 assert_bool("(s/valid? ::m {::a 1})", true, &mut env);
625 assert_bool("(s/valid? ::m {::a 1 ::b \"x\"})", true, &mut env);
626 assert_bool("(s/valid? ::m {::b \"x\"})", false, &mut env); assert_bool("(s/valid? ::m {::a \"no\"})", false, &mut env); assert_bool("(s/valid? ::m {::a 1 ::b 2})", false, &mut env); assert_bool("(s/valid? ::m 42)", false, &mut env); assert_bool(
632 "(s/valid? (s/keys :req [::a]) {::a 1 ::b :not-a-string})",
633 false,
634 &mut env,
635 );
636 run("(s/def ::c boolean?)", &mut env).unwrap();
638 run(
639 "(s/def ::conn (s/keys :req [(or ::a (and ::b ::c))]))",
640 &mut env,
641 )
642 .unwrap();
643 assert_bool("(s/valid? ::conn {::a 1})", true, &mut env);
644 assert_bool("(s/valid? ::conn {::b \"x\" ::c true})", true, &mut env);
645 assert_bool("(s/valid? ::conn {::b \"x\"})", false, &mut env);
646 assert_bool("(s/valid? ::conn {})", false, &mut env);
647 });
648 }
649
650 #[test]
651 fn test_spec_keys_un_variants() {
652 run_with_big_stack(|| {
653 let (_, mut env) = make_env();
654 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
655 run("(s/def ::a int?) (s/def ::b string?)", &mut env).unwrap();
656 run(
657 "(s/def ::mu (s/keys :req-un [::a] :opt-un [::b]))",
658 &mut env,
659 )
660 .unwrap();
661 assert_bool("(s/valid? ::mu {:a 1})", true, &mut env);
662 assert_bool("(s/valid? ::mu {:a 1 :b \"x\"})", true, &mut env);
663 assert_bool("(s/valid? ::mu {:b \"x\"})", false, &mut env); assert_bool("(s/valid? ::mu {:a \"no\"})", false, &mut env); assert_bool("(s/valid? ::mu {:a 1 :b 2})", false, &mut env); assert_bool("(= {:a 1} (s/conform ::mu {:a 1}))", true, &mut env);
667 });
668 }
669
670 #[test]
671 fn test_spec_keys_record() {
672 run_with_big_stack(|| {
673 let (_, mut env) = make_env();
674 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
675 run("(defrecord Point [x y])", &mut env).unwrap();
676 run("(s/def ::x number?) (s/def ::y number?)", &mut env).unwrap();
677 run("(s/def ::point (s/keys :req-un [::x ::y]))", &mut env).unwrap();
678 assert_bool("(s/valid? ::point (->Point 1 2))", true, &mut env);
679 assert_bool("(s/valid? ::point (->Point \"a\" 2))", false, &mut env);
680 assert_bool(
682 "(record? (s/conform ::point (->Point 1 2)))",
683 true,
684 &mut env,
685 );
686 });
687 }
688
689 #[test]
690 fn test_spec_explain_data_shape() {
691 run_with_big_stack(|| {
692 let (_, mut env) = make_env();
693 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
694 run("(s/def ::a int?) (s/def ::b string?)", &mut env).unwrap();
695 run("(s/def ::m (s/keys :req [::a] :opt [::b]))", &mut env).unwrap();
696
697 run("(def ed1 (s/explain-data ::m {::b \"x\"}))", &mut env).unwrap();
699 assert_bool(
700 "(pos? (count (:clojure.spec.alpha/problems ed1)))",
701 true,
702 &mut env,
703 );
704 let pred = run(
705 "(pr-str (:pred (first (:clojure.spec.alpha/problems ed1))))",
706 &mut env,
707 )
708 .unwrap();
709 assert_eq!(pred, Value::string("(contains? % :user/a)"));
710 assert_bool(
711 "(= [] (:path (first (:clojure.spec.alpha/problems ed1))))",
712 true,
713 &mut env,
714 );
715
716 run("(def ed2 (s/explain-data ::m {::a \"no\"}))", &mut env).unwrap();
719 run(
720 "(def p2 (first (:clojure.spec.alpha/problems ed2)))",
721 &mut env,
722 )
723 .unwrap();
724 assert_bool("(= [::a] (:path p2))", true, &mut env);
725 assert_bool("(= [::a] (:in p2))", true, &mut env);
726 assert_bool("(= [::m ::a] (:via p2))", true, &mut env);
727 assert_bool("(= \"no\" (:val p2))", true, &mut env);
728
729 assert_bool("(nil? (s/explain-data ::m {::a 4}))", true, &mut env);
731
732 assert_bool(
734 "(= {::a \"no\"} (:clojure.spec.alpha/value ed2))",
735 true,
736 &mut env,
737 );
738 });
739 }
740
741 #[test]
742 fn test_spec_merge() {
743 run_with_big_stack(|| {
744 let (_, mut env) = make_env();
745 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
746 run("(s/def ::a int?) (s/def ::b string?)", &mut env).unwrap();
747 run("(s/def ::ma (s/keys :req [::a]))", &mut env).unwrap();
748 run("(s/def ::mb (s/keys :req [::b]))", &mut env).unwrap();
749 run("(s/def ::mab (s/merge ::ma ::mb))", &mut env).unwrap();
750 assert_bool("(s/valid? ::mab {::a 1 ::b \"x\"})", true, &mut env);
751 assert_bool("(s/valid? ::mab {::a 1})", false, &mut env);
752 assert_bool(
753 "(= {::a 1 ::b \"x\"} (s/conform ::mab {::a 1 ::b \"x\"}))",
754 true,
755 &mut env,
756 );
757 assert_bool(
758 "(pos? (count (:clojure.spec.alpha/problems
759 (s/explain-data ::mab {::a 1}))))",
760 true,
761 &mut env,
762 );
763 });
764 }
765
766 #[test]
767 fn test_spec_explain_str_and_printer() {
768 run_with_big_stack(|| {
769 let (_, mut env) = make_env();
770 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
771 run("(s/def ::a int?)", &mut env).unwrap();
772 run("(s/def ::m (s/keys :req [::a]))", &mut env).unwrap();
773 let estr = match run("(s/explain-str ::m {::a \"no\"})", &mut env).unwrap() {
774 Value::Str(s) => s.get().clone(),
775 other => panic!("expected string from explain-str, got {other:?}"),
776 };
777 assert!(
778 estr.contains("failed") && estr.contains("int?"),
779 "unexpected explain-str output: {estr}"
780 );
781 let ok = run("(s/explain-str ::m {::a 1})", &mut env).unwrap();
782 assert_eq!(ok, Value::string("Success!\n"));
783 });
784 }
785
786 #[test]
787 fn test_spec_keys_unform_roundtrip() {
788 run_with_big_stack(|| {
789 let (_, mut env) = make_env();
790 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
791 run("(s/def ::id (s/or :i int? :s string?))", &mut env).unwrap();
792 run("(s/def ::rm (s/keys :req-un [::id]))", &mut env).unwrap();
793 assert_bool("(= {:id [:i 5]} (s/conform ::rm {:id 5}))", true, &mut env);
795 assert_bool(
796 "(= {:id 5} (s/unform ::rm (s/conform ::rm {:id 5})))",
797 true,
798 &mut env,
799 );
800 });
801 }
802
803 #[test]
806 fn test_spec_regex_cat_and_nesting() {
807 run_with_big_stack(|| {
808 let (_, mut env) = make_env();
809 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
810 assert_bool(
811 "(= {:a 1 :b \"x\"} (s/conform (s/cat :a int? :b string?) [1 \"x\"]))",
812 true,
813 &mut env,
814 );
815 assert_bool(
817 "(= {:a 1 :b [\"x\" \"y\"]}
818 (s/conform (s/cat :a int? :b (s/* string?)) [1 \"x\" \"y\"]))",
819 true,
820 &mut env,
821 );
822 assert_bool(
824 "(s/invalid? (s/conform (s/cat :a int? :b string?) [1 2]))",
825 true,
826 &mut env,
827 );
828 assert_bool(
829 "(s/invalid? (s/conform (s/cat :a int? :b string?) [1]))",
830 true,
831 &mut env,
832 );
833 assert_bool(
834 "(s/invalid? (s/conform (s/cat :a int?) [1 2]))",
835 true,
836 &mut env,
837 );
838 assert_bool("(s/invalid? (s/conform (s/cat :a int?) 5))", true, &mut env);
840 });
841 }
842
843 #[test]
844 fn test_spec_regex_alt_star_plus_maybe() {
845 run_with_big_stack(|| {
846 let (_, mut env) = make_env();
847 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
848 assert_bool(
850 "(= [:i 5] (s/conform (s/alt :i int? :s string?) [5]))",
851 true,
852 &mut env,
853 );
854 assert_bool(
855 "(s/invalid? (s/conform (s/alt :i int? :s string?) [:kw]))",
856 true,
857 &mut env,
858 );
859 assert_bool("(= [] (s/conform (s/* int?) []))", true, &mut env);
861 assert_bool("(s/valid? (s/* int?) [])", true, &mut env);
862 assert_bool("(= [1 2 3] (s/conform (s/* int?) [1 2 3]))", true, &mut env);
863 assert_bool(
864 "(s/invalid? (s/conform (s/* int?) [1 :a 3]))",
865 true,
866 &mut env,
867 );
868 assert_bool("(= [1] (s/conform (s/+ int?) [1]))", true, &mut env);
870 assert_bool("(s/invalid? (s/conform (s/+ int?) []))", true, &mut env);
871 assert_bool("(= 5 (s/conform (s/? int?) [5]))", true, &mut env);
873 assert_bool("(nil? (s/conform (s/? int?) []))", true, &mut env);
874 assert_bool("(s/valid? (s/? int?) [])", true, &mut env);
875 assert_bool("(s/invalid? (s/conform (s/? int?) [1 2]))", true, &mut env);
876 });
877 }
878
879 #[test]
880 fn test_spec_regex_amp_and_regex_inside_and() {
881 run_with_big_stack(|| {
882 let (_, mut env) = make_env();
883 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
884 run("(def even-count? (fn [xs] (even? (count xs))))", &mut env).unwrap();
885 assert_bool(
887 "(= [1 2] (s/conform (s/& (s/* int?) even-count?) [1 2]))",
888 true,
889 &mut env,
890 );
891 assert_bool(
892 "(s/invalid? (s/conform (s/& (s/* int?) even-count?) [1 2 3]))",
893 true,
894 &mut env,
895 );
896 run("(def all-even? (fn [xs] (every? even? xs)))", &mut env).unwrap();
899 assert_bool(
900 "(= [2 4] (s/conform (s/and (s/* int?) all-even?) [2 4]))",
901 true,
902 &mut env,
903 );
904 assert_bool(
905 "(s/valid? (s/and (s/* int?) all-even?) [1 2])",
906 false,
907 &mut env,
908 );
909 });
910 }
911
912 #[test]
913 fn test_spec_regex_keyword_ref_children() {
914 run_with_big_stack(|| {
915 let (_, mut env) = make_env();
916 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
917 run("(s/def ::a int?)", &mut env).unwrap();
919 assert_bool(
920 "(= {:x 1 :y \"x\"} (s/conform (s/cat :x ::a :y string?) [1 \"x\"]))",
921 true,
922 &mut env,
923 );
924 run("(s/def ::r (s/* int?))", &mut env).unwrap();
927 assert_bool(
928 "(= {:nums [1 2] :tail \"x\"}
929 (s/conform (s/cat :nums ::r :tail string?) [1 2 \"x\"]))",
930 true,
931 &mut env,
932 );
933 assert_bool(
935 "(= {:nums [1 2] :tail \"x\"}
936 (s/conform (s/cat :nums (s/spec (s/* int?)) :tail string?)
937 [[1 2] \"x\"]))",
938 true,
939 &mut env,
940 );
941 assert_bool("(= [1 2 3] (s/conform ::r [1 2 3]))", true, &mut env);
943 });
944 }
945
946 #[test]
947 fn test_spec_regex_explain_reasons() {
948 run_with_big_stack(|| {
949 let (_, mut env) = make_env();
950 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
951 run(
953 "(def p-insuff (first (:clojure.spec.alpha/problems
954 (s/explain-data (s/cat :a int? :b string?) [1]))))",
955 &mut env,
956 )
957 .unwrap();
958 assert_bool(
959 "(= \"Insufficient input\" (:reason p-insuff))",
960 true,
961 &mut env,
962 );
963 assert_bool("(= [:b] (:path p-insuff))", true, &mut env);
964 run(
966 "(def p-extra (first (:clojure.spec.alpha/problems
967 (s/explain-data (s/cat :a int?) [1 2]))))",
968 &mut env,
969 )
970 .unwrap();
971 assert_bool("(= \"Extra input\" (:reason p-extra))", true, &mut env);
972 assert_bool("(= [1] (:in p-extra))", true, &mut env);
973 run(
975 "(def p-elem (first (:clojure.spec.alpha/problems
976 (s/explain-data (s/cat :a int? :b string?) [1 :bad]))))",
977 &mut env,
978 )
979 .unwrap();
980 assert_bool("(= [:b] (:path p-elem))", true, &mut env);
981 assert_bool("(= [1] (:in p-elem))", true, &mut env);
982 assert_bool("(= :bad (:val p-elem))", true, &mut env);
983 });
984 }
985
986 #[test]
987 fn test_spec_regex_unform_roundtrips() {
988 run_with_big_stack(|| {
989 let (_, mut env) = make_env();
990 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
991 run("(def even-count? (fn [xs] (even? (count xs))))", &mut env).unwrap();
992 for (spec, input) in [
993 ("(s/cat :a int? :b string?)", "[1 \"x\"]"),
994 ("(s/cat :a int? :b (s/* string?))", "[1 \"x\" \"y\"]"),
995 ("(s/alt :i int? :s string?)", "[5]"),
996 ("(s/* int?)", "[1 2 3]"),
997 ("(s/* int?)", "[]"),
998 ("(s/+ int?)", "[1 2]"),
999 ("(s/? int?)", "[5]"),
1000 ("(s/? int?)", "[]"),
1001 ("(s/& (s/* int?) even-count?)", "[1 2]"),
1002 ] {
1003 assert_bool(
1004 &format!(
1005 "(let [re {spec}] (= {input} (vec (s/unform re (s/conform re {input})))))"
1006 ),
1007 true,
1008 &mut env,
1009 );
1010 }
1011 });
1012 }
1013
1014 #[test]
1015 fn test_spec_plain_map_is_not_a_spec() {
1016 run_with_big_stack(|| {
1017 let (_, mut env) = make_env();
1018 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1019 let err = run("(s/conform {:a 1} 5)", &mut env).unwrap_err();
1020 let msg = format!("{err}");
1021 assert!(
1022 msg.contains("not a valid spec"),
1023 "expected 'not a valid spec' in error, got: {msg}"
1024 );
1025 });
1026 }
1027
1028 #[test]
1031 fn test_spec_coll_of_options() {
1032 run_with_big_stack(|| {
1033 let (_, mut env) = make_env();
1034 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1035 assert_bool(
1037 "(= [1 2 3] (s/conform (s/coll-of int?) [1 2 3]))",
1038 true,
1039 &mut env,
1040 );
1041 assert_bool(
1042 "(s/invalid? (s/conform (s/coll-of int?) [1 \"x\"]))",
1043 true,
1044 &mut env,
1045 );
1046 assert_bool(
1048 "(s/invalid? (s/conform (s/coll-of int? :kind vector?) '(1 2 3)))",
1049 true,
1050 &mut env,
1051 );
1052 assert_bool(
1054 "(s/invalid? (s/conform (s/coll-of int? :min-count 3) [1 2]))",
1055 true,
1056 &mut env,
1057 );
1058 assert_bool(
1059 "(s/invalid? (s/conform (s/coll-of int? :max-count 2) [1 2 3]))",
1060 true,
1061 &mut env,
1062 );
1063 assert_bool(
1065 "(= [1 2 3] (s/conform (s/coll-of int? :distinct true) [1 2 3]))",
1066 true,
1067 &mut env,
1068 );
1069 assert_bool(
1070 "(s/invalid? (s/conform (s/coll-of int? :distinct true) [1 1 2]))",
1071 true,
1072 &mut env,
1073 );
1074 assert_bool(
1076 "(= #{1 2 3} (s/conform (s/coll-of int? :into #{}) [1 2 3]))",
1077 true,
1078 &mut env,
1079 );
1080 assert_bool(
1083 "(= '(1 2 3) (s/conform (s/coll-of int?) '(1 2 3)))",
1084 true,
1085 &mut env,
1086 );
1087 assert_bool(
1088 "(= '(3 2 1) (s/conform (s/coll-of int? :into '()) '(1 2 3)))",
1089 true,
1090 &mut env,
1091 );
1092 });
1093 }
1094
1095 #[test]
1096 fn test_spec_map_of_key_handling() {
1097 run_with_big_stack(|| {
1098 let (_, mut env) = make_env();
1099 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1100 assert_bool(
1102 "(= {:a 1 :b 2} (s/conform (s/map-of keyword? int?) {:a 1 :b 2}))",
1103 true,
1104 &mut env,
1105 );
1106 assert_bool(
1107 "(s/invalid? (s/conform (s/map-of keyword? int?) {:a \"x\"}))",
1108 true,
1109 &mut env,
1110 );
1111 assert_bool(
1112 "(s/invalid? (s/conform (s/map-of keyword? int?) {\"not-kw\" 1}))",
1113 true,
1114 &mut env,
1115 );
1116 assert_bool(
1118 r#"(= {"a" 1} (s/conform (s/map-of (s/conformer name) int? :conform-keys true) {:a 1}))"#,
1119 true,
1120 &mut env,
1121 );
1122 });
1123 }
1124
1125 #[test]
1126 fn test_spec_every_vs_coll_of_conform_difference() {
1127 run_with_big_stack(|| {
1128 let (_, mut env) = make_env();
1129 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1130 run("(def elem (s/or :i int? :s string?))", &mut env).unwrap();
1131 assert_bool(
1133 "(= [[:i 1] [:s \"x\"]] (s/conform (s/coll-of elem) [1 \"x\"]))",
1134 true,
1135 &mut env,
1136 );
1137 assert_bool(
1139 "(= [1 \"x\"] (s/conform (s/every elem) [1 \"x\"]))",
1140 true,
1141 &mut env,
1142 );
1143 assert_bool(
1144 "(s/invalid? (s/conform (s/every elem) [1 :bad]))",
1145 true,
1146 &mut env,
1147 );
1148 assert_bool(
1150 "(= {:a 1} (s/conform (s/every-kv keyword? int?) {:a 1}))",
1151 true,
1152 &mut env,
1153 );
1154 assert_bool(
1155 "(s/invalid? (s/conform (s/every-kv keyword? int?) {:a \"x\"}))",
1156 true,
1157 &mut env,
1158 );
1159 });
1160 }
1161
1162 #[test]
1163 fn test_spec_tuple_conform_and_explain() {
1164 run_with_big_stack(|| {
1165 let (_, mut env) = make_env();
1166 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1167 assert_bool(
1168 "(= [1 \"x\"] (s/conform (s/tuple int? string?) [1 \"x\"]))",
1169 true,
1170 &mut env,
1171 );
1172 assert_bool(
1173 "(s/invalid? (s/conform (s/tuple int? string?) [1]))",
1174 true,
1175 &mut env,
1176 );
1177 assert_bool(
1178 "(s/invalid? (s/conform (s/tuple int? string?) 5))",
1179 true,
1180 &mut env,
1181 );
1182 run(
1183 "(def tp (first (:clojure.spec.alpha/problems (s/explain-data (s/tuple int? string?) [1 :bad]))))",
1184 &mut env,
1185 )
1186 .unwrap();
1187 assert_bool("(= [1] (:path tp))", true, &mut env);
1188 assert_bool("(= [1] (:in tp))", true, &mut env);
1189 assert_bool("(= :bad (:val tp))", true, &mut env);
1190 assert_bool(
1192 "(= [1 \"x\"] (s/unform (s/tuple int? string?) (s/conform (s/tuple int? string?) [1 \"x\"])))",
1193 true,
1194 &mut env,
1195 );
1196 });
1197 }
1198
1199 #[test]
1200 fn test_spec_nilable_both_branches_and_explain_shape() {
1201 run_with_big_stack(|| {
1202 let (_, mut env) = make_env();
1203 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1204 assert_bool("(= nil (s/conform (s/nilable int?) nil))", true, &mut env);
1205 assert_bool("(= 5 (s/conform (s/nilable int?) 5))", true, &mut env);
1206 assert_bool(
1207 "(s/invalid? (s/conform (s/nilable int?) \"x\"))",
1208 true,
1209 &mut env,
1210 );
1211 run(
1212 "(def np (:clojure.spec.alpha/problems (s/explain-data (s/nilable int?) \"x\")))",
1213 &mut env,
1214 )
1215 .unwrap();
1216 assert_bool("(= 2 (count np))", true, &mut env);
1217 assert_bool(
1218 "(some (fn [p] (= [:clojure.spec.alpha/nil] (:path p))) np)",
1219 true,
1220 &mut env,
1221 );
1222 assert_bool(
1223 "(some (fn [p] (= [:clojure.spec.alpha/pred] (:path p))) np)",
1224 true,
1225 &mut env,
1226 );
1227 });
1228 }
1229
1230 #[test]
1231 fn test_spec_multi_spec_conform_and_no_method() {
1232 run_with_big_stack(|| {
1233 let (_, mut env) = make_env();
1234 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1235 run("(defmulti event-type :type)", &mut env).unwrap();
1236 run("(s/def :evt/type keyword?)", &mut env).unwrap();
1237 run("(s/def :evt/a (s/keys :req-un [:evt/type]))", &mut env).unwrap();
1238 run("(defmethod event-type :a [_] :evt/a)", &mut env).unwrap();
1239 run(
1240 "(s/def :evt/event (s/multi-spec event-type :type))",
1241 &mut env,
1242 )
1243 .unwrap();
1244 assert_bool(
1245 "(= {:type :a} (s/conform :evt/event {:type :a}))",
1246 true,
1247 &mut env,
1248 );
1249 assert_bool(
1250 "(s/invalid? (s/conform :evt/event {:type :unknown}))",
1251 true,
1252 &mut env,
1253 );
1254 run(
1255 "(def mp (first (:clojure.spec.alpha/problems (s/explain-data :evt/event {:type :unknown}))))",
1256 &mut env,
1257 )
1258 .unwrap();
1259 assert_bool("(= \"no method\" (:reason mp))", true, &mut env);
1260 });
1261 }
1262
1263 #[test]
1264 fn test_spec_conformer_transforms_and_threads_through_and() {
1265 run_with_big_stack(|| {
1266 let (_, mut env) = make_env();
1267 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1268 assert_bool(
1269 "(= 10 (s/conform (s/conformer #(* 2 %)) 5))",
1270 true,
1271 &mut env,
1272 );
1273 assert_bool(
1275 "(= 10 (s/conform (s/and int? (s/conformer #(* 2 %))) 5))",
1276 true,
1277 &mut env,
1278 );
1279 assert_bool(
1280 "(= 5 (s/unform (s/conformer #(* 2 %) #(/ % 2)) 10))",
1281 true,
1282 &mut env,
1283 );
1284 assert_bool(
1286 "(= 10 (s/unform (s/conformer #(* 2 %)) 10))",
1287 true,
1288 &mut env,
1289 );
1290 });
1291 }
1292
1293 #[test]
1294 fn test_spec_int_in_and_double_in_bounds() {
1295 run_with_big_stack(|| {
1296 let (_, mut env) = make_env();
1297 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1298 assert_bool("(s/valid? (s/int-in 0 10) 5)", true, &mut env);
1299 assert_bool("(s/valid? (s/int-in 0 10) 10)", false, &mut env); assert_bool("(s/valid? (s/int-in 0 10) 5.0)", false, &mut env); assert_bool(
1302 "(s/valid? (s/double-in :min 0.0 :max 10.0) 5.0)",
1303 true,
1304 &mut env,
1305 );
1306 assert_bool(
1307 "(s/valid? (s/double-in :min 0.0 :max 10.0) 20.0)",
1308 false,
1309 &mut env,
1310 );
1311 run("(def nan (/ 0.0 0.0))", &mut env).unwrap();
1315 run("(def pos-inf (/ 1.0 0.0))", &mut env).unwrap();
1316 assert_bool("(s/valid? (s/double-in :NaN? false) nan)", false, &mut env);
1317 assert_bool("(s/valid? (s/double-in) nan)", true, &mut env);
1318 assert_bool(
1319 "(s/valid? (s/double-in :infinite? false) pos-inf)",
1320 false,
1321 &mut env,
1322 );
1323 assert_bool("(s/valid? (s/double-in) pos-inf)", true, &mut env);
1324 assert_bool(
1326 "(= [1 \"x\"] (s/conform (s/nonconforming (s/cat :a int? :b string?)) [1 \"x\"]))",
1327 true,
1328 &mut env,
1329 );
1330 let err = run("(s/inst-in 0 1)", &mut env).unwrap_err();
1332 let msg = format!("{err}");
1333 assert!(
1334 msg.contains("not implemented"),
1335 "expected 'not implemented' in error, got: {msg}"
1336 );
1337 });
1338 }
1339
1340 #[test]
1343 fn test_spec_fdef_registers_qualified_symbol_and_get_spec() {
1344 run_with_big_stack(|| {
1345 let (_, mut env) = make_env();
1346 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1347 run("(defn my-add [a b] (+ a b))", &mut env).unwrap();
1348 assert_bool(
1349 "(= 'user/my-add (s/fdef my-add :args (s/cat :a int? :b int?) :ret int?))",
1350 true,
1351 &mut env,
1352 );
1353 assert_bool("(s/fspec? (s/get-spec 'user/my-add))", true, &mut env);
1354 assert_bool("(some? (:args (s/get-spec 'user/my-add)))", true, &mut env);
1355 assert_bool(
1358 "(= (s/get-spec 'user/my-add) (s/get-spec `my-add))",
1359 true,
1360 &mut env,
1361 );
1362 });
1363 }
1364
1365 #[test]
1366 fn test_spec_instrument_valid_and_invalid_calls() {
1367 run_with_big_stack(|| {
1368 let (_, mut env) = make_env();
1369 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1370 run("(require '[clojure.spec.test.alpha :as stest])", &mut env).unwrap();
1371 run("(defn my-add [a b] (+ a b))", &mut env).unwrap();
1372 run(
1373 "(s/fdef my-add :args (s/cat :a int? :b int?) :ret int?)",
1374 &mut env,
1375 )
1376 .unwrap();
1377 run("(stest/instrument 'user/my-add)", &mut env).unwrap();
1378 assert_eq!(run("(my-add 2 3)", &mut env).unwrap(), Value::Long(5));
1380 run(
1382 r#"(def caught
1383 (try (my-add 2 "x") :did-not-throw
1384 (catch Exception e {:msg (ex-message e) :data (ex-data e)})))"#,
1385 &mut env,
1386 )
1387 .unwrap();
1388 assert_bool(
1389 r#"(= "Call to user/my-add did not conform to spec." (:msg caught))"#,
1390 true,
1391 &mut env,
1392 );
1393 assert_bool(
1394 "(= :instrument (:clojure.spec.alpha/failure (:data caught)))",
1395 true,
1396 &mut env,
1397 );
1398 assert_bool(
1399 "(contains? (:data caught) :clojure.spec.test.alpha/caller)",
1400 true,
1401 &mut env,
1402 );
1403 });
1404 }
1405
1406 #[test]
1407 fn test_spec_unstrument_restores_raw_fn() {
1408 run_with_big_stack(|| {
1409 let (_, mut env) = make_env();
1410 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1411 run("(require '[clojure.spec.test.alpha :as stest])", &mut env).unwrap();
1412 run("(defn my-pair [a b] [a b])", &mut env).unwrap();
1413 run("(s/fdef my-pair :args (s/cat :a int? :b int?))", &mut env).unwrap();
1414 run("(stest/instrument 'user/my-pair)", &mut env).unwrap();
1415 assert!(run("(my-pair 1 \"x\")", &mut env).is_err());
1416 run("(stest/unstrument 'user/my-pair)", &mut env).unwrap();
1417 assert_bool("(= [1 \"x\"] (my-pair 1 \"x\"))", true, &mut env);
1420 });
1421 }
1422
1423 #[test]
1424 fn test_spec_instrument_affects_call_sites_evaluated_before_instrument() {
1425 run_with_big_stack(|| {
1426 let (_, mut env) = make_env();
1427 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1428 run("(require '[clojure.spec.test.alpha :as stest])", &mut env).unwrap();
1429 run("(defn my-mul [a b] (* a b))", &mut env).unwrap();
1430 run("(s/fdef my-mul :args (s/cat :a int? :b int?))", &mut env).unwrap();
1431 assert_eq!(run("(my-mul 2 3)", &mut env).unwrap(), Value::Long(6));
1434 run("(stest/instrument 'user/my-mul)", &mut env).unwrap();
1435 assert!(run("(my-mul 2 \"x\")", &mut env).is_err());
1438 });
1439 }
1440
1441 #[test]
1442 fn test_spec_instrumentable_syms_and_idempotent_double_instrument() {
1443 run_with_big_stack(|| {
1444 let (_, mut env) = make_env();
1445 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1446 run("(require '[clojure.spec.test.alpha :as stest])", &mut env).unwrap();
1447 run("(defn my-pair2 [a b] [a b])", &mut env).unwrap();
1448 run("(s/fdef my-pair2 :args (s/cat :a int? :b int?))", &mut env).unwrap();
1449 assert_bool(
1450 "(boolean (some #{'user/my-pair2} (stest/instrumentable-syms)))",
1451 true,
1452 &mut env,
1453 );
1454 run("(stest/instrument 'user/my-pair2)", &mut env).unwrap();
1455 run("(stest/instrument 'user/my-pair2)", &mut env).unwrap(); assert!(run("(my-pair2 1 \"x\")", &mut env).is_err());
1457 run("(stest/unstrument 'user/my-pair2)", &mut env).unwrap();
1461 assert_bool("(= [1 \"x\"] (my-pair2 1 \"x\"))", true, &mut env);
1462 });
1463 }
1464
1465 #[test]
1466 fn test_spec_with_instrument_disabled_bypasses_checks() {
1467 run_with_big_stack(|| {
1468 let (_, mut env) = make_env();
1469 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1470 run("(require '[clojure.spec.test.alpha :as stest])", &mut env).unwrap();
1471 run("(defn my-pair3 [a b] [a b])", &mut env).unwrap();
1472 run("(s/fdef my-pair3 :args (s/cat :a int? :b int?))", &mut env).unwrap();
1473 run("(stest/instrument 'user/my-pair3)", &mut env).unwrap();
1474 assert!(run("(my-pair3 1 \"x\")", &mut env).is_err());
1475 assert_bool(
1476 "(stest/with-instrument-disabled (= [1 \"x\"] (my-pair3 1 \"x\")))",
1477 true,
1478 &mut env,
1479 );
1480 assert!(run("(my-pair3 1 \"x\")", &mut env).is_err());
1482 });
1483 }
1484
1485 #[test]
1486 fn test_spec_assert_happy_sad_and_check_asserts_toggle() {
1487 run_with_big_stack(|| {
1488 let (_, mut env) = make_env();
1489 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1490 assert_eq!(run("(s/assert even? 4)", &mut env).unwrap(), Value::Long(4));
1491 assert!(run("(s/assert even? 5)", &mut env).is_err());
1492 run("(s/check-asserts false)", &mut env).unwrap();
1493 assert_eq!(run("(s/assert even? 5)", &mut env).unwrap(), Value::Long(5));
1497 run("(s/check-asserts true)", &mut env).unwrap();
1498 assert!(run("(s/assert even? 5)", &mut env).is_err());
1499 });
1500 }
1501
1502 #[test]
1503 fn test_spec_gen_and_check_throw_not_implemented_and_gen_ns_loads() {
1504 run_with_big_stack(|| {
1505 let (_, mut env) = make_env();
1506 run("(require '[clojure.spec.alpha :as s])", &mut env).unwrap();
1507 run("(require '[clojure.spec.test.alpha :as stest])", &mut env).unwrap();
1508 run("(require '[clojure.spec.gen.alpha :as gen])", &mut env).unwrap();
1509 for src in [
1510 "(s/gen even?)",
1511 "(s/exercise even?)",
1512 "(s/exercise-fn 'user/foo)",
1513 "(stest/check)",
1514 "(stest/check 'user/foo)",
1515 "(stest/check-fn (fn [x] x) even?)",
1516 "(gen/generate :whatever)",
1517 "(gen/sample :whatever)",
1518 "(gen/elements [1 2 3])",
1519 ] {
1520 assert!(run(src, &mut env).is_err(), "expected {src} to throw");
1521 }
1522 });
1523 }
1524}