use std::path::PathBuf;
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn temp_path(tag: &str) -> PathBuf {
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let pid = std::process::id();
std::env::temp_dir().join(format!("ilo_rdjl_{tag}_{pid}_{n}.jsonl"))
}
fn write_fixture(path: &PathBuf, contents: &str) {
std::fs::write(path, contents).expect("write fixture");
}
fn run(engine: &str, src: &str, entry: &str, extra: &[&str]) -> String {
let mut cmd = ilo();
cmd.arg(src).arg(engine).arg(entry);
for a in extra {
cmd.arg(a);
}
let out = cmd.output().expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} failed for `{src}` (entry={entry}): stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn engines() -> Vec<&'static str> {
let mut v = vec!["--vm"];
if cfg!(feature = "cranelift") {
v.push("--jit");
}
v
}
const COUNT_SRC: &str = "count p:t>n;es=rdjl p;len es";
#[test]
fn rdjl_three_well_formed_lines() {
let path = temp_path("happy");
write_fixture(&path, "{\"amount\":10}\n{\"amount\":20}\n{\"amount\":12}\n");
for engine in engines() {
let got = run(engine, COUNT_SRC, "count", &[path.to_str().unwrap()]);
assert_eq!(got, "3", "engine={engine}");
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn rdjl_empty_file_yields_empty_list() {
let path = temp_path("empty");
write_fixture(&path, "");
for engine in engines() {
let got = run(engine, COUNT_SRC, "count", &[path.to_str().unwrap()]);
assert_eq!(got, "0", "engine={engine}");
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn rdjl_skips_blank_lines() {
let path = temp_path("blanks");
write_fixture(&path, "{\"x\":1}\n\n{\"x\":2}\n\n\n{\"x\":3}\n");
for engine in engines() {
let got = run(engine, COUNT_SRC, "count", &[path.to_str().unwrap()]);
assert_eq!(got, "3", "engine={engine}");
}
let _ = std::fs::remove_file(&path);
}
#[test]
fn rdjl_mixed_lines_each_wrapped() {
let path = temp_path("mixed");
write_fixture(
&path,
"{\"a\":1}\nnot json\n{\"a\":2}\n{also bad\n{\"a\":3}\n",
);
for engine in engines() {
let got = run(engine, COUNT_SRC, "count", &[path.to_str().unwrap()]);
assert_eq!(got, "5", "engine={engine}");
}
let _ = std::fs::remove_file(&path);
}
const FIRST_OK_SRC: &str = "head-amt p:t>n;es=rdjl p;ev=hd es;?ev{~v:v.amount;^er:999}";
#[test]
fn rdjl_first_line_unwraps_to_record_field() {
let path = temp_path("first");
write_fixture(&path, "{\"amount\":7}\n{\"amount\":8}\n");
let got = run("--vm", FIRST_OK_SRC, "head-amt", &[path.to_str().unwrap()]);
assert_eq!(got, "7");
let _ = std::fs::remove_file(&path);
}
const HEAD_ERR_SRC: &str = "head-tag p:t>n;es=rdjl p;ev=hd es;?ev{~v:1;^er:0}";
#[test]
fn rdjl_malformed_first_line_is_err() {
let path = temp_path("err");
write_fixture(&path, "not json\n{\"ok\":true}\n");
let got = run("--vm", HEAD_ERR_SRC, "head-tag", &[path.to_str().unwrap()]);
assert_eq!(got, "0");
let _ = std::fs::remove_file(&path);
}