use std::process::Command;
use std::sync::atomic::{AtomicU64, Ordering};
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
fn write_src(name: &str, src: &str) -> std::path::PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let mut path = std::env::temp_dir();
path.push(format!("ilo_fmtspec_{name}_{}_{n}.ilo", std::process::id()));
std::fs::write(&path, src).expect("write src");
path
}
fn run_ok(engine: &str, src: &str, entry: &str) -> String {
let path = write_src(entry, src);
let out = ilo()
.arg(&path)
.arg(engine)
.arg(entry)
.output()
.expect("failed to run ilo");
let _ = std::fs::remove_file(&path);
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 path = write_src(entry, src);
let out = ilo()
.arg(&path)
.arg(engine)
.arg(entry)
.output()
.expect("failed to run ilo");
let _ = std::fs::remove_file(&path);
assert!(
!out.status.success(),
"expected failure but ilo {engine} succeeded for `{src}`"
);
let mut s = String::from_utf8_lossy(&out.stderr).into_owned();
s.push_str(&String::from_utf8_lossy(&out.stdout));
s
}
const LITERAL_06D: &str = "f>t;fmt \"{:06d}\" 42";
fn check_literal_06d(engine: &str) {
let s = run_err(engine, LITERAL_06D, "f");
assert!(
s.contains("ILO-T013"),
"engine={engine}: expected ILO-T013, got: {s}"
);
assert!(
s.contains("{:06d}"),
"engine={engine}: expected offending spec in message, got: {s}"
);
assert!(
s.contains("fmt2") && s.contains("padl"),
"engine={engine}: expected fmt2+padl hint, got: {s}"
);
}
#[test]
fn literal_06d_tree() {
check_literal_06d("--run-tree");
}
#[test]
fn literal_06d_vm() {
check_literal_06d("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn literal_06d_cranelift() {
check_literal_06d("--run-cranelift");
}
const LITERAL_3F: &str = "f>t;fmt \"pi={:.3f}\" 3.14159";
fn check_literal_3f(engine: &str) {
let s = run_err(engine, LITERAL_3F, "f");
assert!(
s.contains("ILO-T013") && s.contains("{:.3f}"),
"engine={engine}: expected ILO-T013 mentioning {{:.3f}}, got: {s}"
);
}
#[test]
fn literal_3f_tree() {
check_literal_3f("--run-tree");
}
#[test]
fn literal_3f_vm() {
check_literal_3f("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn literal_3f_cranelift() {
check_literal_3f("--run-cranelift");
}
const COMPUTED_06D: &str = "f>t;t=cat [\"x=\" \"{:06d}\"] \"\";fmt t 42";
fn check_computed_06d(engine: &str) {
let s = run_err(engine, COMPUTED_06D, "f");
assert!(
s.contains("ILO-R009"),
"engine={engine}: expected ILO-R009 from runtime fmt, got: {s}"
);
assert!(
s.contains("{:06d}") && s.contains("fmt2"),
"engine={engine}: expected offending spec + fmt2 hint, got: {s}"
);
}
#[test]
fn computed_06d_tree() {
check_computed_06d("--run-tree");
}
#[test]
fn computed_06d_vm() {
check_computed_06d("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn computed_06d_cranelift() {
check_computed_06d("--run-cranelift");
}
const BARE_OK: &str = "f>t;fmt \"x={}\" 42";
fn check_bare_ok(engine: &str) {
assert_eq!(run_ok(engine, BARE_OK, "f"), "x=42", "engine={engine}");
}
#[test]
fn bare_ok_tree() {
check_bare_ok("--run-tree");
}
#[test]
fn bare_ok_vm() {
check_bare_ok("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn bare_ok_cranelift() {
check_bare_ok("--run-cranelift");
}
const LONE_BRACE_OK: &str = "f>t;fmt \"{a:1}\"";
fn check_lone_brace_ok(engine: &str) {
assert_eq!(
run_ok(engine, LONE_BRACE_OK, "f"),
"{a:1}",
"engine={engine}"
);
}
#[test]
fn lone_brace_ok_tree() {
check_lone_brace_ok("--run-tree");
}
#[test]
fn lone_brace_ok_vm() {
check_lone_brace_ok("--run-vm");
}
#[test]
#[cfg(feature = "cranelift")]
fn lone_brace_ok_cranelift() {
check_lone_brace_ok("--run-cranelift");
}