use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
struct Run {
stdout: String,
stderr: String,
}
fn run_jit(src: &str) -> Run {
static SEQ: AtomicU64 = AtomicU64::new(0);
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos();
let seq = SEQ.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
"cljrs_jit_robustness_{}_{nanos}_{seq}.cljrs",
std::process::id()
));
std::fs::write(&path, src).expect("write script");
let output = Command::new(env!("CARGO_BIN_EXE_cljrs"))
.args(["--jit-threshold", "50", "run"])
.arg(&path)
.env("CLJRS_EAGER_LOWER", "1")
.output()
.expect("spawn cljrs");
let _ = std::fs::remove_file(&path);
assert!(
output.status.success(),
"cljrs exited with {:?}\nstderr:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
Run {
stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
}
}
#[test]
fn closure_bearing_fn_runs_correctly_without_panicking() {
let src = r#"
(defn make-adder [n] (fn [x] (+ x n)))
(defn vsum [a b & xs] [a b (count xs)])
(dotimes [i 30000]
((make-adder 5) 1)
(vsum 1 2 3 4)
(when (= i 29999)
(println "adder=" ((make-adder 5) 1))
(println "vsum=" (vsum 1 2 3 4))))
"#;
let run = run_jit(src);
assert!(
run.stdout.contains("adder= 6"),
"closure result wrong; stdout:\n{}",
run.stdout
);
assert!(
run.stdout.contains("vsum= [1 2 2]"),
"variadic result wrong; stdout:\n{}",
run.stdout
);
assert!(
!run.stderr.contains("panicked"),
"JIT worker panicked instead of declining gracefully; stderr:\n{}",
run.stderr
);
}
#[test]
fn string_join_char_elements_from_jit() {
let src = r#"
(require '[clojure.string :as s])
(defn join-chars [chars sep]
(s/join sep chars))
(dotimes [_ 100]
(join-chars [\8 \0] "")
(join-chars [\8 \0] "-"))
(println (join-chars [\8 \0] ""))
(println (join-chars [\8 \0] "-"))
"#;
let run = run_jit(src);
assert!(
run.stdout.contains("80"),
"expected \"80\" in stdout, got:\n{}",
run.stdout
);
assert!(
run.stdout.contains("8-0"),
"expected \"8-0\" in stdout, got:\n{}",
run.stdout
);
assert!(
!run.stdout.contains("\\8"),
"join produced reader syntax (\\8) instead of char value; stdout:\n{}",
run.stdout
);
}
#[test]
fn contains_q_vector_non_integer_key_returns_false_under_jit() {
let src = r#"
(defn check [v k] (contains? v k))
(dotimes [_ 100]
(check [1 2 3] :a)
(check [1 2 3] "x")
(check [1 2 3] 0))
(println (check [1 2 3] :a))
(println (check [1 2 3] "x"))
(println (check [1 2 3] 0))
"#;
let run = run_jit(src);
assert!(
run.stdout.contains("false\nfalse\ntrue"),
"unexpected output; stdout:\n{}",
run.stdout
);
}