Skip to content

Commit 68aad5e

Browse files
committed
Add test_deterministic_functions to Rust API unit tests
This test makes sure that initial analysis is not tainting functions
1 parent 0b69ad0 commit 68aad5e

File tree

5 files changed

+3265
-12
lines changed

5 files changed

+3265
-12
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ thiserror = "2.0"
2121
[dev-dependencies]
2222
rstest = "0.24"
2323
tempfile = "3.15"
24-
serial_test = "3.2"
24+
serial_test = "3.2"
25+
insta = { version = "1.42", features = ["yaml"] }

rust/tests/binary_view.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use binaryninja::binary_view::{AnalysisState, BinaryViewBase, BinaryViewExt};
2+
use binaryninja::function::Function;
23
use binaryninja::headless::Session;
34
use binaryninja::main_thread::execute_on_main_thread_and_wait;
4-
use binaryninja::symbol::{SymbolBuilder, SymbolType};
5+
use binaryninja::platform::Platform;
6+
use binaryninja::rc::Ref;
7+
use binaryninja::symbol::{Symbol, SymbolBuilder, SymbolType};
58
use rstest::*;
9+
use std::collections::BTreeMap;
610
use std::path::PathBuf;
711

812
#[fixture]
@@ -37,15 +41,13 @@ fn test_binary_saving(_session: &Session) {
3741
// HACK: To prevent us from deadlocking in save_to_path we wait for all main thread actions to finish.
3842
execute_on_main_thread_and_wait(|| {});
3943

44+
let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
45+
let temp_path = temp_dir.path().join("atox.obj.new");
4046
// Save the modified file
41-
assert!(view.save_to_path(out_dir.join("atox.obj.new")));
47+
assert!(view.save_to_path(&temp_path));
4248
// Verify that the file exists and is modified.
43-
let new_view =
44-
binaryninja::load(out_dir.join("atox.obj.new")).expect("Failed to load new view");
45-
assert_eq!(
46-
new_view.read_vec(contents_addr, 4),
47-
[0xff, 0xff, 0xff, 0xff]
48-
);
49+
let new_view = binaryninja::load(temp_path).expect("Failed to load new view");
50+
assert_eq!(new_view.read_vec(contents_addr, 4), [0xff, 0xff, 0xff, 0xff]);
4951
}
5052

5153
#[rstest]
@@ -62,12 +64,53 @@ fn test_binary_saving_database(_session: &Session) {
6264
// Verify that we modified the binary
6365
assert_eq!(entry_function.symbol().raw_name().as_str(), "test");
6466
// Save the modified database.
65-
assert!(view.file().create_database(out_dir.join("atox.obj.bndb")));
67+
let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
68+
let temp_path = temp_dir.path().join("atox.obj.bndb");
69+
assert!(view.file().create_database(&temp_path));
6670
// Verify that the file exists and is modified.
67-
let new_view =
68-
binaryninja::load(out_dir.join("atox.obj.bndb")).expect("Failed to load new view");
71+
let new_view = binaryninja::load(temp_path).expect("Failed to load new view");
6972
let new_entry_function = new_view
7073
.entry_point_function()
7174
.expect("Failed to get entry point function");
7275
assert_eq!(new_entry_function.symbol().raw_name().as_str(), "test");
7376
}
77+
78+
// This is what we store to check if a function matches the expected function.
79+
// See `test_deterministic_functions` for details.
80+
#[derive(Debug, PartialEq)]
81+
pub struct FunctionSnapshot {
82+
name: String,
83+
platform: Ref<Platform>,
84+
symbol: Ref<Symbol>,
85+
}
86+
87+
impl From<&Function> for FunctionSnapshot {
88+
fn from(func: &Function) -> Self {
89+
Self {
90+
name: func.symbol().raw_name().to_string(),
91+
platform: func.platform().to_owned(),
92+
symbol: func.symbol().to_owned(),
93+
}
94+
}
95+
}
96+
97+
#[rstest]
98+
fn test_deterministic_functions(session: &Session) {
99+
// Test to make sure that analysis always collects the same information on functions.
100+
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
101+
for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") {
102+
let entry = entry.expect("Failed to read directory entry");
103+
let path = entry.path();
104+
if path.is_file() {
105+
let view = session.load(&path).expect("Failed to load view");
106+
assert_eq!(view.analysis_progress().state, AnalysisState::IdleState);
107+
let functions: BTreeMap<u64, FunctionSnapshot> = view
108+
.functions()
109+
.iter()
110+
.map(|f| (f.start(), FunctionSnapshot::from(f.as_ref())))
111+
.collect();
112+
let snapshot_name = path.file_stem().unwrap().to_str().unwrap();
113+
insta::assert_debug_snapshot!(snapshot_name, functions);
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)