use callgraph_verify::{analyze, diff_calls, has_direct_branch_target, Pe};
#[test]
fn real_binary_sweeps_agree_and_diff_clean() {
let exe = match std::env::var("CALLGRAPH_REAL_EXE") {
Ok(p) if !p.trim().is_empty() => p,
_ => {
eprintln!("skipping: set CALLGRAPH_REAL_EXE=<path-to-32bit-PE> to sweep a real PE");
return;
}
};
let pe = Pe::load(&exe).unwrap_or_else(|e| panic!("load {exe}: {e}"));
assert!(
!pe.is_le(),
"DOS LE coverage uses the repository's Open Watcom C fixture"
);
let refs = pe.referenced_addrs();
let (lo, hi) = pe.image_range();
let mut starts: Vec<(u64, u64)> = pe
.sections()
.iter()
.filter(|s| s.is_executable())
.map(|s| {
let start = pe.image_base + s.virtual_address as u64;
(start, start + s.virtual_size.max(s.raw_size) as u64)
})
.collect();
starts.sort();
assert!(!starts.is_empty(), "{exe} has no executable section");
let mut fns = 0u32;
let mut checked_callers = 0u32;
let mut calls = 0u32;
let mut disagreements = Vec::new();
let mut unclean = Vec::new();
for (start, end) in starts {
let mut va = start;
while va < end {
if let Ok(cg) = analyze(&pe, va) {
fns += 1;
let real: Vec<u64> = cg.call_targets().into_iter().collect();
match diff_calls(&pe, va, &real, true) {
Ok(d) if d.hallucinated.is_empty() && d.missing.is_empty() => {}
other => unclean.push((va, format!("{other:?}"))),
}
if refs.contains(&(va as u32)) {
checked_callers += 1;
for &t in cg.direct_calls.keys() {
if !(lo..hi).contains(&t) {
continue; }
calls += 1;
if !has_direct_branch_target(&pe, t) {
disagreements.push((va, t));
}
}
}
va = va.saturating_add(cg.max_end.saturating_sub(va).max(1));
} else {
va += 1;
}
}
}
eprintln!("{exe}: swept {fns} entries ({checked_callers} referenced), {calls} direct calls");
assert!(fns > 0, "no analyzable functions in {exe}");
assert!(
disagreements.is_empty(),
"{} of {calls} CALL targets disagree with the byte scan: {disagreements:?}",
disagreements.len()
);
assert!(
unclean.is_empty(),
"{} of {fns} functions not diff-clean: {unclean:?}",
unclean.len()
);
}