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