use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run_ok(engine: &str, src: &str, entry: &str) -> String {
let out = ilo()
.args([src, engine, entry])
.output()
.expect("failed to run ilo");
assert!(
out.status.success(),
"ilo {engine} failed for `{src}`: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
fn run_err(engine: &str, src: &str, entry: &str) -> String {
let out = ilo()
.args([src, engine, entry])
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"ilo {engine} unexpectedly succeeded for `{src}`: stdout={}",
String::from_utf8_lossy(&out.stdout)
);
String::from_utf8_lossy(&out.stderr).to_string()
}
fn engines() -> &'static [&'static str] {
&["--vm"]
}
#[test]
fn wr_json_overload_record_writes_json() {
for (i, engine) in engines().iter().enumerate() {
let path = format!("/tmp/ilo_wr_json_overload_rec_{i}.json");
let _ = std::fs::remove_file(&path);
let src = format!(r#"f>R t t;wr "{path}" (mset (mset mmap "x" 1) "y" 2) "json""#);
let _ = run_ok(engine, &src, "f");
let body = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("engine={engine}: missing output file: {e}"));
let parsed: serde_json::Value =
serde_json::from_str(&body).expect("output must be valid JSON");
assert_eq!(parsed["x"].as_f64(), Some(1.0));
assert_eq!(parsed["y"].as_f64(), Some(2.0));
let _ = std::fs::remove_file(&path);
}
}
#[test]
fn wr_json_overload_list_writes_json() {
for (i, engine) in engines().iter().enumerate() {
let path = format!("/tmp/ilo_wr_json_overload_list_{i}.json");
let _ = std::fs::remove_file(&path);
let src = format!(r#"f>R t t;wr "{path}" [1,2,3] "json""#);
let _ = run_ok(engine, &src, "f");
let body = std::fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("engine={engine}: missing output file: {e}"));
let parsed: serde_json::Value =
serde_json::from_str(&body).expect("output must be valid JSON");
let nums: Vec<f64> = parsed
.as_array()
.expect("expected JSON array")
.iter()
.map(|v| v.as_f64().expect("array element should be numeric"))
.collect();
assert_eq!(nums, vec![1.0, 2.0, 3.0]);
let _ = std::fs::remove_file(&path);
}
}
#[test]
fn wr_two_arg_text_still_works() {
for (i, engine) in engines().iter().enumerate() {
let path = format!("/tmp/ilo_wr_json_overload_txt_{i}.txt");
let _ = std::fs::remove_file(&path);
let src = format!(r#"f>R t t;wr "{path}" "hello""#);
let _ = run_ok(engine, &src, "f");
let body = std::fs::read_to_string(&path).expect("missing output file");
assert_eq!(body, "hello");
let _ = std::fs::remove_file(&path);
}
}
#[test]
fn wr_unsupported_format_literal_is_verifier_error() {
let src = r#"f>R t t;wr "/tmp/x" [1,2,3] "not_json""#;
let err = run_err("--vm", src, "f");
assert!(
err.contains("not_json"),
"error should mention the bad format literal, got: {err}"
);
assert!(
err.contains("json"),
"error should point at the supported \"json\" format, got: {err}"
);
}
#[test]
fn wr_format_arg_must_be_text() {
let src = r#"f>R t t;wr "/tmp/x" [1,2,3] 42"#;
let err = run_err("--vm", src, "f");
assert!(
err.contains("format") || err.contains("expects t"),
"should reject numeric format arg, got: {err}"
);
}
#[test]
fn wr_two_arg_nontext_data_hints_three_arg_form() {
let src = r#"f>R t t;wr "/tmp/x" [1,2,3]"#;
let err = run_err("--vm", src, "f");
assert!(
err.contains("arg 2") && err.contains("t"),
"expected text-content error on 2-arg wr, got: {err}"
);
assert!(
err.contains("3-arg") || err.contains("\"json\""),
"expected hint pointing to the json overload, got: {err}"
);
}
#[test]
fn wr_json_roundtrip_via_rdl_jpar() {
for (i, engine) in engines().iter().enumerate() {
let path = format!("/tmp/ilo_wr_json_overload_rt_{i}.json");
let _ = std::fs::remove_file(&path);
let src = format!(r#"f>R t t;wr "{path}" (mset (mset mmap "x" 1) "y" 2) "json""#);
let _ = run_ok(engine, &src, "f");
let body = std::fs::read_to_string(&path).expect("missing output file");
let read_src = format!(r#"g>t;p=rdl "{path}";?p{{t v:hd v;_:"err"}}"#);
let first_line = run_ok(engine, &read_src, "g");
assert!(
!first_line.is_empty(),
"engine={engine}: rdl roundtrip produced empty output"
);
let parsed: serde_json::Value = serde_json::from_str(&body).expect("valid JSON");
assert_eq!(parsed["x"].as_f64(), Some(1.0));
assert_eq!(parsed["y"].as_f64(), Some(2.0));
let _ = std::fs::remove_file(&path);
}
}