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