Skip to content

Commit

Permalink
Add test_deterministic_functions to Rust API unit tests
Browse files Browse the repository at this point in the history
This test makes sure that initial analysis is not tainting functions
  • Loading branch information
emesare committed Jan 31, 2025
1 parent a4484f2 commit 385a9ca
Show file tree
Hide file tree
Showing 5 changed files with 3,264 additions and 8 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ thiserror = "2.0"
[dev-dependencies]
rstest = "0.24"
tempfile = "3.15"
serial_test = "3.2"
serial_test = "3.2"
insta = { version = "1.42", features = ["yaml"] }
60 changes: 53 additions & 7 deletions rust/tests/binary_view.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use binaryninja::binary_view::{AnalysisState, BinaryViewBase, BinaryViewExt};
use binaryninja::function::Function;
use binaryninja::headless::Session;
use binaryninja::main_thread::execute_on_main_thread_and_wait;
use binaryninja::symbol::{SymbolBuilder, SymbolType};
use binaryninja::platform::Platform;
use binaryninja::rc::Ref;
use binaryninja::symbol::{Symbol, SymbolBuilder, SymbolType};
use rstest::*;
use std::collections::BTreeMap;
use std::path::PathBuf;

#[fixture]
Expand Down Expand Up @@ -36,11 +40,12 @@ fn test_binary_saving(_session: &Session) {
// HACK: To prevent us from deadlocking in save_to_path we wait for all main thread actions to finish.
execute_on_main_thread_and_wait(|| {});

let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
let temp_path = temp_dir.path().join("atox.obj.new");
// Save the modified file
assert!(view.save_to_path(out_dir.join("atox.obj.new")));
assert!(view.save_to_path(&temp_path));
// Verify that the file exists and is modified.
let new_view =
binaryninja::load(out_dir.join("atox.obj.new")).expect("Failed to load new view");
let new_view = binaryninja::load(temp_path).expect("Failed to load new view");
assert_eq!(new_view.read_vec(0x1560, 4), [0xff, 0xff, 0xff, 0xff]);
}

Expand All @@ -58,12 +63,53 @@ fn test_binary_saving_database(_session: &Session) {
// Verify that we modified the binary
assert_eq!(entry_function.symbol().raw_name().as_str(), "test");
// Save the modified database.
assert!(view.file().create_database(out_dir.join("atox.obj.bndb")));
let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
let temp_path = temp_dir.path().join("atox.obj.bndb");
assert!(view.file().create_database(&temp_path));
// Verify that the file exists and is modified.
let new_view =
binaryninja::load(out_dir.join("atox.obj.bndb")).expect("Failed to load new view");
let new_view = binaryninja::load(temp_path).expect("Failed to load new view");
let new_entry_function = new_view
.entry_point_function()
.expect("Failed to get entry point function");
assert_eq!(new_entry_function.symbol().raw_name().as_str(), "test");
}

// This is what we store to check if a function matches the expected function.
// See `test_deterministic_functions` for details.
#[derive(Debug, PartialEq)]
pub struct FunctionSnapshot {
name: String,
platform: Ref<Platform>,
symbol: Ref<Symbol>,
}

impl From<&Function> for FunctionSnapshot {
fn from(func: &Function) -> Self {
Self {
name: func.symbol().raw_name().to_string(),
platform: func.platform().to_owned(),
symbol: func.symbol().to_owned(),
}
}
}

#[rstest]
fn test_deterministic_functions(session: &Session) {
// Test to make sure that analysis always collects the same information on functions.
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") {
let entry = entry.expect("Failed to read directory entry");
let path = entry.path();
if path.is_file() {
let view = session.load(&path).expect("Failed to load view");
assert_eq!(view.analysis_progress().state, AnalysisState::IdleState);
let functions: BTreeMap<u64, FunctionSnapshot> = view
.functions()
.iter()
.map(|f| (f.start(), FunctionSnapshot::from(f.as_ref())))
.collect();
let snapshot_name = path.file_stem().unwrap().to_str().unwrap();
insta::assert_debug_snapshot!(snapshot_name, functions);
}
}
}
Loading

0 comments on commit 385a9ca

Please sign in to comment.