11use binaryninja:: binary_view:: { AnalysisState , BinaryViewBase , BinaryViewExt } ;
2+ use binaryninja:: function:: Function ;
23use binaryninja:: headless:: Session ;
34use binaryninja:: main_thread:: execute_on_main_thread_and_wait;
45use binaryninja:: symbol:: { SymbolBuilder , SymbolType } ;
@@ -32,15 +33,13 @@ fn test_binary_saving() {
3233 // HACK: To prevent us from deadlocking in save_to_path we wait for all main thread actions to finish.
3334 execute_on_main_thread_and_wait ( || { } ) ;
3435
36+ let temp_dir = tempfile:: tempdir ( ) . expect ( "Failed to create temporary directory" ) ;
37+ let temp_path = temp_dir. path ( ) . join ( "atox.obj.new" ) ;
3538 // Save the modified file
36- assert ! ( view. save_to_path( out_dir . join ( "atox.obj.new" ) ) ) ;
39+ assert ! ( view. save_to_path( & temp_path ) ) ;
3740 // Verify that the file exists and is modified.
38- let new_view =
39- binaryninja:: load ( out_dir. join ( "atox.obj.new" ) ) . expect ( "Failed to load new view" ) ;
40- assert_eq ! (
41- new_view. read_vec( contents_addr, 4 ) ,
42- [ 0xff , 0xff , 0xff , 0xff ]
43- ) ;
41+ let new_view = binaryninja:: load ( temp_path) . expect ( "Failed to load new view" ) ;
42+ assert_eq ! ( new_view. read_vec( contents_addr, 4 ) , [ 0xff , 0xff , 0xff , 0xff ] ) ;
4443}
4544
4645#[ test]
@@ -58,12 +57,53 @@ fn test_binary_saving_database() {
5857 // Verify that we modified the binary
5958 assert_eq ! ( entry_function. symbol( ) . raw_name( ) . as_str( ) , "test" ) ;
6059 // Save the modified database.
61- assert ! ( view. file( ) . create_database( out_dir. join( "atox.obj.bndb" ) ) ) ;
60+ let temp_dir = tempfile:: tempdir ( ) . expect ( "Failed to create temporary directory" ) ;
61+ let temp_path = temp_dir. path ( ) . join ( "atox.obj.bndb" ) ;
62+ assert ! ( view. file( ) . create_database( & temp_path) ) ;
6263 // Verify that the file exists and is modified.
63- let new_view =
64- binaryninja:: load ( out_dir. join ( "atox.obj.bndb" ) ) . expect ( "Failed to load new view" ) ;
64+ let new_view = binaryninja:: load ( temp_path) . expect ( "Failed to load new view" ) ;
6565 let new_entry_function = new_view
6666 . entry_point_function ( )
6767 . expect ( "Failed to get entry point function" ) ;
6868 assert_eq ! ( new_entry_function. symbol( ) . raw_name( ) . as_str( ) , "test" ) ;
6969}
70+
71+ // This is what we store to check if a function matches the expected function.
72+ // See `test_deterministic_functions` for details.
73+ #[ derive( Debug , PartialEq ) ]
74+ pub struct FunctionSnapshot {
75+ name : String ,
76+ platform : Ref < Platform > ,
77+ symbol : Ref < Symbol > ,
78+ }
79+
80+ impl From < & Function > for FunctionSnapshot {
81+ fn from ( func : & Function ) -> Self {
82+ Self {
83+ name : func. symbol ( ) . raw_name ( ) . to_string ( ) ,
84+ platform : func. platform ( ) . to_owned ( ) ,
85+ symbol : func. symbol ( ) . to_owned ( ) ,
86+ }
87+ }
88+ }
89+
90+ #[ rstest]
91+ fn test_deterministic_functions ( session : & Session ) {
92+ // Test to make sure that analysis always collects the same information on functions.
93+ let out_dir = env ! ( "OUT_DIR" ) . parse :: < PathBuf > ( ) . unwrap ( ) ;
94+ for entry in std:: fs:: read_dir ( out_dir) . expect ( "Failed to read OUT_DIR" ) {
95+ let entry = entry. expect ( "Failed to read directory entry" ) ;
96+ let path = entry. path ( ) ;
97+ if path. is_file ( ) {
98+ let view = session. load ( & path) . expect ( "Failed to load view" ) ;
99+ assert_eq ! ( view. analysis_progress( ) . state, AnalysisState :: IdleState ) ;
100+ let functions: BTreeMap < u64 , FunctionSnapshot > = view
101+ . functions ( )
102+ . iter ( )
103+ . map ( |f| ( f. start ( ) , FunctionSnapshot :: from ( f. as_ref ( ) ) ) )
104+ . collect ( ) ;
105+ let snapshot_name = path. file_stem ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
106+ insta:: assert_debug_snapshot!( snapshot_name, functions) ;
107+ }
108+ }
109+ }
0 commit comments