use std::path::PathBuf;
use std::process::Command;
fn fixture(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}
fn run_xpile(args: &[&str]) -> std::process::Output {
let bin = PathBuf::from(env!("CARGO_BIN_EXE_xpile"));
Command::new(bin)
.args(args)
.output()
.expect("spawn xpile binary")
}
fn xpile_transpile_to_rust(fixture_name: &str) -> String {
let py = fixture(fixture_name);
let out = run_xpile(&["transpile", py.to_str().unwrap()]);
assert!(
out.status.success(),
"xpile failed on {fixture_name}: stderr={}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8(out.stdout).expect("stdout is UTF-8")
}
fn rust_target_dir(name: &str) -> PathBuf {
let dir = std::env::temp_dir().join("xpile-runtime-strata").join(name);
std::fs::create_dir_all(&dir).expect("create temp dir");
dir
}
fn build_and_run(name: &str, merged: &str) -> Result<(), String> {
if Command::new("rustc").arg("--version").output().is_err() {
return Ok(());
}
let dir = rust_target_dir(name);
let file = dir.join(format!("{name}.rs"));
std::fs::write(&file, merged).expect("write merged rust");
let bin = dir.join(name);
let compile = Command::new("rustc")
.arg("--edition=2021")
.arg("-O")
.arg("-o")
.arg(&bin)
.arg(&file)
.output()
.expect("spawn rustc");
if !compile.status.success() {
return Err(format!(
"rustc failed:\n=== source ===\n{merged}\n=== stderr ===\n{}",
String::from_utf8_lossy(&compile.stderr)
));
}
let run = Command::new(&bin).output().expect("spawn binary");
if !run.status.success() {
return Err(format!(
"binary {name} exited non-zero (assertion tripped):\n=== source ===\n{merged}\n=== stdout ===\n{}\n=== stderr ===\n{}",
String::from_utf8_lossy(&run.stdout),
String::from_utf8_lossy(&run.stderr),
));
}
Ok(())
}
#[test]
fn py_int_arith_runtime_stratum_add_matches_python_semantics() {
let transpiled = xpile_transpile_to_rust("add.py");
let driver = r#"
fn main() {
// Linear congruential generator — same Numerical Recipes
// constants Rust's old `rand` test code used. Period 2^64; for
// 4096 samples we won't approach it. Seed fixed for reproducibility.
//
// We shift each LCG output right by 2 (sign-extending) before
// casting to i64 so the pair (a, b) has |a|+|b| ≤ i64::MAX/2.
// That makes overflow impossible — every pair is on the happy
// path and the sweep tests the full 4096 cases. The overflow arm
// is exercised by the companion test.
let mut state: u64 = 0xdead_beef_cafe_f00d;
for i in 0..4096u64 {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let a: i64 = (state as i64) >> 2;
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let b: i64 = (state as i64) >> 2;
let expected = a.checked_add(b)
.expect("sweep generator should not produce overflowing pairs");
let got = add(a, b);
assert_eq!(got, expected, "iter {i}: add({a}, {b}) gave {got}, expected {expected}");
}
println!("ok happy path: 4096/4096");
}
"#;
let merged = format!("{transpiled}\n\n{driver}\n");
build_and_run("py_int_arith_runtime_happy", &merged)
.expect("runtime stratum: add(a, b) must match checked_add semantics across the sweep");
}
#[test]
fn py_int_arith_runtime_stratum_abs_val_matches_sign_branch() {
let transpiled = xpile_transpile_to_rust("abs_val.py");
let driver = r#"
fn main() {
let mut state: u64 = 0xdead_beef_cafe_f00d;
for i in 0..4096u64 {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let x: i64 = (state as i64) >> 1;
let expected: i64 = if x < 0 { -x } else { x };
let got = abs_val(x);
assert_eq!(got, expected, "iter {i}: abs_val({x}) gave {got}, expected {expected}");
}
println!("ok abs_val sweep: 4096/4096");
}
"#;
let merged = format!("{transpiled}\n\n{driver}\n");
build_and_run("py_int_arith_runtime_abs_val", &merged)
.expect("runtime stratum: abs_val(x) must match Python sign-branching semantics");
}
#[test]
fn py_int_arith_runtime_stratum_fib_matches_iterative_reference() {
let transpiled = xpile_transpile_to_rust("fib.py");
let driver = r#"
fn fib_iter(n: i64) -> i64 {
if n <= 1 { return n; }
let (mut a, mut b) = (0i64, 1i64);
for _ in 1..n { let t = b; b = a + b; a = t; }
b
}
fn main() {
for n in 0..24i64 {
let expected = fib_iter(n);
let got = fib(n);
assert_eq!(got, expected, "fib({n}) gave {got}, expected {expected}");
}
println!("ok fib recursion: 24/24");
}
"#;
let merged = format!("{transpiled}\n\n{driver}\n");
build_and_run("py_int_arith_runtime_fib", &merged)
.expect("runtime stratum: fib(n) must match iterative reference for n in 0..24");
}
#[test]
fn py_int_arith_runtime_stratum_gcd_matches_euclidean_reference() {
let transpiled = xpile_transpile_to_rust("gcd.py");
let driver = r#"
fn gcd_iter(mut a: i64, mut b: i64) -> i64 {
while b != 0 {
let t = b;
b = a % b;
a = t;
}
a
}
fn main() {
let mut state: u64 = 0xdead_beef_cafe_f00d;
for i in 0..1024u64 {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let a: i64 = ((state >> 2) as i64).abs().max(1).min(i64::MAX / 4);
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let b: i64 = ((state >> 2) as i64).abs().max(1).min(i64::MAX / 4);
let expected = gcd_iter(a, b);
let got = gcd(a, b);
assert_eq!(got, expected, "iter {i}: gcd({a}, {b}) gave {got}, expected {expected}");
}
println!("ok gcd sweep: 1024/1024");
}
"#;
let merged = format!("{transpiled}\n\n{driver}\n");
build_and_run("py_int_arith_runtime_gcd", &merged)
.expect("runtime stratum: gcd(a, b) must match iterative Euclidean reference");
}
#[test]
fn py_int_arith_runtime_stratum_overflow_panics() {
let transpiled = xpile_transpile_to_rust("add.py");
let driver = r#"
fn main() {
let result = std::panic::catch_unwind(|| {
add(i64::MAX, 1)
});
match result {
Err(_) => {
// Expected: checked_add overflowed and `.expect(...)` panicked.
println!("ok overflow panicked");
std::process::exit(0);
}
Ok(v) => {
eprintln!("contract violated: add(i64::MAX, 1) returned {v}, expected panic");
std::process::exit(1);
}
}
}
"#;
let merged = format!("{transpiled}\n\n{driver}\n");
build_and_run("py_int_arith_runtime_overflow", &merged)
.expect("runtime stratum: add(i64::MAX, 1) must panic per checked_add semantics");
}
#[test]
fn py_int_arith_runtime_stratum_for_loop_desugaring_matches_reference() {
let transpiled = xpile_transpile_to_rust("for_sum.py");
let driver = r#"
fn ref_for_sum(n: i64) -> i64 {
// CPython `range(n)` is empty for n <= 0; sum over 0..n.
if n <= 0 { return 0; }
(0..n).sum()
}
fn ref_range_with_start(a: i64, b: i64) -> i64 {
// CPython `range(a, b)` is empty if a >= b.
if a >= b { return 0; }
(a..b).sum()
}
fn ref_range_with_step(stop: i64) -> i64 {
// CPython `range(0, stop, 2)` is empty if stop <= 0; otherwise
// sums 0, 2, 4, ... < stop.
if stop <= 0 { return 0; }
(0..stop).step_by(2).sum()
}
fn main() {
// Sweep 1: for_sum(n) over n in 0..200 — contiguous coverage of
// small n including the n=0 edge.
for n in 0i64..200 {
let expected = ref_for_sum(n);
let got = for_sum(n);
assert_eq!(got, expected, "for_sum({n}) gave {got}, expected {expected}");
}
// Sweep 2: range_with_start(a, b) over 100 LCG pairs clamped to
// small range. Some pairs will have a >= b (empty loop case).
let mut state: u64 = 0xdead_beef_cafe_f00d;
for i in 0..100u64 {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let a: i64 = (state as i64) % 100; // [-99..99]
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let b: i64 = (state as i64) % 100;
let expected = ref_range_with_start(a, b);
let got = range_with_start(a, b);
assert_eq!(got, expected, "iter {i}: range_with_start({a}, {b}) gave {got}, expected {expected}");
}
// Sweep 3: range_with_step(stop) over 100 LCG values clamped to
// small positive bound — including stop <= 0 boundary.
for i in 0..100u64 {
state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let stop: i64 = (state as i64) % 200; // [-199..199]
let expected = ref_range_with_step(stop);
let got = range_with_step(stop);
assert_eq!(got, expected, "iter {i}: range_with_step({stop}) gave {got}, expected {expected}");
}
println!("ok for-loop sweeps: 200 + 100 + 100");
}
"#;
let merged = format!("{transpiled}\n\n{driver}\n");
build_and_run("py_int_arith_runtime_for_loop", &merged).expect(
"runtime stratum: for-in-range desugaring must match CPython range semantics across the sweep",
);
}