use oinkie::prelude::*;
use std::path::{Path, PathBuf};
const LEVELS: [(&str, Ir); 3] = [
("llil", Ir::BinaryNinjaLlil),
("mlil", Ir::BinaryNinjaMlil),
("hlil", Ir::BinaryNinjaHlil),
];
fn fixture(level: &str, name: &str) -> PathBuf {
Path::new("testdata/lifted")
.join(format!("bnil_{level}"))
.join(name)
}
#[test]
fn test_each_level_reads_back_as_the_representation_it_names() {
for (level, ir) in LEVELS {
let p = AnyProgram::load(&fixture(level, "hello_clang.json"))
.unwrap_or_else(|e| panic!("{level}: {e}"));
assert_eq!(p.ir(), ir, "{level} loaded as the wrong representation");
assert_eq!(p.name(), "hello_clang");
}
}
#[test]
fn test_every_level_finds_the_call_that_is_there() {
for (level, _) in LEVELS {
let p = AnyProgram::load(&fixture(level, "hello_clang.json")).unwrap();
let b = Extractor::new(BirthmarkType::try_from("fc-set").unwrap())
.extract_any(&p)
.unwrap_or_else(|e| panic!("{level}: {e}"));
let calls: Vec<String> = b
.iter()
.flat_map(|e| e.ops().map(|s| s.to_string()))
.collect();
assert!(
calls.iter().any(|c| c == "_printf"),
"{level} did not find the call to _printf: {calls:?}"
);
}
}
#[test]
fn test_two_levels_of_the_same_program_refuse_to_be_compared() {
let llil = AnyProgram::load(&fixture("llil", "hello_clang.json")).unwrap();
let hlil = AnyProgram::load(&fixture("hlil", "hello_clang.json")).unwrap();
let err = match Comparator::from(&Algorithm::Jaccard).compare_any(
&llil,
&hlil,
&Aggregator::default(),
) {
Err(e) => e.to_string(),
Ok(_) => panic!("LLIL and HLIL were compared"),
};
assert!(
err.contains("binary-ninja-llil") && err.contains("binary-ninja-hlil"),
"the refusal does not name both representations: {err}"
);
}
#[test]
fn test_a_level_compares_with_itself() {
for (level, _) in LEVELS {
let a = AnyProgram::load(&fixture(level, "hello_clang.json")).unwrap();
let b = AnyProgram::load(&fixture(level, "hello_gcc.json")).unwrap();
let c = Comparator::from(&Algorithm::Jaccard)
.compare_any(&a, &b, &Aggregator::default())
.unwrap_or_else(|e| panic!("{level}: {e}"));
let s = c.similarity();
assert!(
(0.0..=1.0).contains(&s),
"{level} scored outside [0, 1]: {s}"
);
}
}