use std::process::Command;
use object::{Object, ObjectSection};
use sha2::{Digest, Sha256};
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture(name: &str) -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join(name)
}
fn text_sha256(wasm: &str, backend: &str, target: &str) -> (String, usize) {
let path = fixture(wasm);
let elf = format!("/tmp/frozenbytes_{backend}_{wasm}.elf");
let out = Command::new(synth())
.env_remove("SYNTH_NO_CMP_SELECT_FUSE")
.env_remove("SYNTH_NO_LOCAL_PROMOTE")
.env_remove("SYNTH_CONST_CSE")
.args([
"compile",
path.to_str().unwrap(),
"-o",
&elf,
"-b",
backend,
"--target",
target,
"--all-exports",
"--relocatable",
])
.output()
.expect("run synth");
assert!(
out.status.success(),
"synth compile failed for {wasm} ({backend}/{target}): {}",
String::from_utf8_lossy(&out.stderr)
);
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let text = obj
.section_by_name(".text")
.expect("frozen fixture must have a .text section");
let data = text.data().expect("read .text");
let mut hasher = Sha256::new();
hasher.update(data);
let digest = hasher.finalize();
let hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
(hex, data.len())
}
fn assert_frozen(cases: &[(&str, &str, usize)], backend: &str, target: &str) {
for &(wasm, golden, golden_len) in cases {
let (got, len) = text_sha256(wasm, backend, target);
assert_eq!(
len, golden_len,
"{wasm} ({backend}): .text length changed ({len} vs locked {golden_len}) \
— codegen moved. If intentional, re-freeze the golden HERE and re-run \
scripts/repro/*_differential.py on this commit (they move together)."
);
assert_eq!(
got, golden,
"{wasm} ({backend}): .text SHA-256 changed — a frozen fixture's machine \
code drifted with no deliberate re-freeze. This is the gate working: \
either a codegen change leaked in (investigate — dep bump? flag? \
selector?), or it is intentional, in which case update the golden HERE \
and re-run the scripts/repro/*_differential.py result checks on the \
SAME commit."
);
}
}
#[test]
fn frozen_fixtures_text_is_bit_identical_oracle_001() {
let cases = [
(
"control_step.wasm",
"cd929e7d91a8f7aad93f0e1cf0c93ecf3ccc6584ee94fb32e68d591134ed1410",
316usize,
),
(
"flight_seam.wasm",
"52b19365e32bcd9d5a4be74565d0fa467517eb3a07648a3e1ccd0a67556c1948",
866,
),
(
"flight_seam_flat.wasm",
"fa019f18cbc93869fff51630c6ab9cff6c4e052e22d783fe741b663ece49fa1e",
1006,
),
(
"signed_div_const.wasm",
"b277453b7829a5b2c64527131298d89fa63f5641231b1d4c7336675e8cdab9b0",
34,
),
];
assert_frozen(&cases, "arm", "cortex-m4");
}
#[test]
fn frozen_fixtures_rv32_text_is_bit_identical_oracle_001() {
let cases = [
(
"control_step.wasm",
"6e734c4c67b651067a56ff2a7593015f48ea24fe068a7a20c17f1c08a4e48e83",
504usize,
),
(
"signed_div_const.wasm",
"15fa429d5ef5474f8b65fdd9c81b3da4c70176fb077df25131e2ec3988eb999e",
88,
),
];
assert_frozen(&cases, "riscv", "rv32imac");
}