use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn run(engine: &str, src: &str, entry: &str, args: &[&str]) -> String {
let mut cmd = ilo();
cmd.arg(src).arg(engine).arg(entry);
for a in args {
cmd.arg(a);
}
let out = cmd.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(src: &str) -> String {
let out = ilo()
.args([src, "--vm", "f"])
.output()
.expect("failed to run ilo");
assert!(!out.status.success(), "expected failure for `{src}`");
String::from_utf8_lossy(&out.stderr).to_string()
}
const SIGIL: &str = "f j:t>R n t;r=jpar! j;r.baseSeverity";
fn check_sigil(engine: &str) {
assert_eq!(
run(engine, SIGIL, "f", &[r#"{"baseSeverity":"HIGH"}"#]),
"HIGH",
"engine={engine}"
);
}
#[test]
fn camel_field_sigil_tree() {
check_sigil("--vm");
}
#[test]
fn camel_field_sigil_vm() {
check_sigil("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn camel_field_sigil_cranelift() {
check_sigil("--jit");
}
const NON_SIGIL: &str = "f j:t>R n t;r=jpar! j;r.gitURL";
fn check_non_sigil(engine: &str) {
assert_eq!(
run(engine, NON_SIGIL, "f", &[r#"{"gitURL":"x"}"#]),
"x",
"engine={engine}"
);
}
#[test]
fn camel_field_non_sigil_tree() {
check_non_sigil("--vm");
}
#[test]
fn camel_field_non_sigil_vm() {
check_non_sigil("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn camel_field_non_sigil_cranelift() {
check_non_sigil("--jit");
}
const CHAINED: &str = "f j:t>R n t;r=jpar! j;r.baseSeverity.label";
fn check_chained(engine: &str) {
assert_eq!(
run(engine, CHAINED, "f", &[r#"{"baseSeverity":{"label":"x"}}"#]),
"x",
"engine={engine}"
);
}
#[test]
fn camel_field_chained_tree() {
check_chained("--vm");
}
#[test]
fn camel_field_chained_vm() {
check_chained("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn camel_field_chained_cranelift() {
check_chained("--jit");
}
const DIGIT: &str = "f j:t>R n t;r=jpar! j;r.field2Name";
fn check_digit(engine: &str) {
assert_eq!(
run(engine, DIGIT, "f", &[r#"{"field2Name":42}"#]),
"42",
"engine={engine}"
);
}
#[test]
fn camel_field_digit_tree() {
check_digit("--vm");
}
#[test]
fn camel_field_digit_vm() {
check_digit("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn camel_field_digit_cranelift() {
check_digit("--jit");
}
#[test]
fn camel_field_safe_access_tree() {
let out = run(
"--vm",
"f j:t>R n t;r=jpar! j;r.?baseSeverity",
"f",
&[r#"{"baseSeverity":"HIGH"}"#],
);
assert_eq!(out, "HIGH");
}
const MIXED: &str = "f j:t>R n t;r=jpar! j;r.gitURL_count";
fn check_mixed(engine: &str) {
assert_eq!(
run(engine, MIXED, "f", &[r#"{"gitURL_count":7}"#]),
"7",
"engine={engine}"
);
}
#[test]
fn camel_field_mixed_snake_tree() {
check_mixed("--vm");
}
#[test]
fn camel_field_mixed_snake_vm() {
check_mixed("--vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn camel_field_mixed_snake_cranelift() {
check_mixed("--jit");
}
#[test]
fn camel_binding_still_errors_sigil() {
let err = run_err("f>n;fooSet=5;fooSet");
assert!(err.contains("ILO-L003"), "stderr: {err}");
assert!(err.contains("fooSet"), "stderr: {err}");
}
#[test]
fn camel_binding_still_errors_non_sigil() {
let err = run_err("f>n;fooBar=5;fooBar");
assert!(err.contains("ILO-L003"), "stderr: {err}");
assert!(err.contains("fooBar"), "stderr: {err}");
}
#[test]
fn dot_then_plain_ident_unchanged() {
let out = run(
"--vm",
"f j:t>R n t;r=jpar! j;r.foo",
"f",
&[r#"{"foo":3}"#],
);
assert_eq!(out, "3");
}
#[test]
fn dot_then_camel_space_ident_keeps_tokens_separate() {
let err = run_err("f j:t>R n t;r=jpar! j;r.fooBar baz");
assert!(!err.is_empty(), "expected an error, got empty stderr");
assert!(!err.contains("baseSeverity"), "stderr: {err}");
}