use std::path::PathBuf;
use std::process::Command;
fn fixture(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}
#[test]
fn hybrid_cli_reconciles_resolved_boundary() {
let out = Command::new(env!("CARGO_BIN_EXE_xpile"))
.arg("hybrid")
.arg(fixture("hybrid_sum"))
.output()
.expect("run xpile hybrid");
assert!(
out.status.success(),
"expected exit 0 for a resolved hybrid dir"
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("2 module(s) dispatched"),
"dispatch line:\n{stdout}"
);
assert!(
stdout.contains("square_sum") && stdout.contains("Python") && stdout.contains("[shim_"),
"resolved boundary line:\n{stdout}"
);
}
#[test]
fn hybrid_cli_python_sibling_is_not_a_boundary() {
let out = Command::new(env!("CARGO_BIN_EXE_xpile"))
.arg("hybrid")
.arg(fixture("hybrid_pysibling"))
.output()
.expect("run xpile hybrid");
assert!(
out.status.success(),
"same-language imports are not FFI failures"
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("2 module(s) dispatched") && stdout.contains("no cross-language FFI"),
"expected zero FFI boundaries:\n{stdout}"
);
}
#[test]
fn hybrid_cli_emit_shims_writes_compilable_file() {
let pid = std::process::id();
let out_path = std::env::temp_dir().join(format!("xpile_emit_shims_cli_{pid}.rs"));
let _ = std::fs::remove_file(&out_path);
let out = Command::new(env!("CARGO_BIN_EXE_xpile"))
.arg("hybrid")
.arg(fixture("hybrid_sum"))
.arg("--emit-shims")
.arg(&out_path)
.output()
.expect("run xpile hybrid --emit-shims");
assert!(
out.status.success(),
"emit-shims should exit 0 for a resolved dir; stderr:\n{}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("emitted 1 FFI shim") && stdout.contains("square_sum"),
"emit confirmation line:\n{stdout}"
);
let emitted = std::fs::read_to_string(&out_path).expect("shim file written");
assert!(emitted.contains("// xpile-contract: C-FFI-CPYTHON-EXT"));
assert!(emitted.contains("extern \"C\" {"));
assert!(emitted.contains("fn square_sum(x: ::std::os::raw::c_int) -> ::std::os::raw::c_int;"));
assert!(emitted.contains("pub fn square_sum_shim(x: i64) -> i64 {"));
if Command::new("rustc").arg("--version").output().is_ok() {
for edition in ["2021", "2024"] {
let rlib =
std::env::temp_dir().join(format!("libxpile_emit_shims_cli_{edition}_{pid}.rlib"));
let status = Command::new("rustc")
.args([
"--crate-type",
"lib",
"--edition",
edition,
"-D",
"warnings",
])
.arg(&out_path)
.arg("-o")
.arg(&rlib)
.status()
.expect("spawn rustc");
let _ = std::fs::remove_file(&rlib);
assert!(
status.success(),
"emitted shim file must compile under edition {edition} -D warnings"
);
}
}
let _ = std::fs::remove_file(&out_path);
}
#[test]
fn hybrid_cli_fails_on_unresolved_boundary() {
let out = Command::new(env!("CARGO_BIN_EXE_xpile"))
.arg("hybrid")
.arg(fixture("hybrid_missing"))
.output()
.expect("run xpile hybrid");
assert!(
!out.status.success(),
"an unresolved FFI boundary must exit non-zero"
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("square_sum") && stderr.contains("defines"),
"unresolved diagnostic:\n{stderr}"
);
}