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