diff options
Diffstat (limited to 'rust/tests/collaboration.rs')
| -rw-r--r-- | rust/tests/collaboration.rs | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/rust/tests/collaboration.rs b/rust/tests/collaboration.rs new file mode 100644 index 00000000..48da859a --- /dev/null +++ b/rust/tests/collaboration.rs @@ -0,0 +1,209 @@ +use binaryninja::binary_view::BinaryViewExt; +use binaryninja::collaboration::{NoNameChangeset, Remote, RemoteFileType, RemoteProject}; +use binaryninja::headless::Session; +use binaryninja::symbol::{SymbolBuilder, SymbolType}; +use rstest::*; +use std::path::PathBuf; + +#[fixture] +#[once] +fn session() -> Session { + Session::new().expect("Failed to initialize session") +} + +// TODO: This cannot run in CI, as headless does not have collaboration, we should gate this. + +fn temp_project_scope<T: Fn(&RemoteProject)>(remote: &Remote, cb: T) { + if !remote.is_connected() { + remote.connect().expect("Failed to connect to remote"); + } + let project = remote + .create_project("Test Project", "Test project for test purposes") + .expect("Failed to create project"); + project.open().expect("Failed to open project"); + assert!(project.is_open(), "Project was not opened"); + // Clear out all the possible entries. This is to insure a clean slate. + let files = project.files().expect("Failed to list files in project"); + for file in &files { + project.delete_file(&file).expect("Failed to delete file"); + } + let folders = project + .folders() + .expect("Failed to list folders in project"); + for folder in &folders { + project + .delete_folder(&folder) + .expect("Failed to delete folder"); + } + // Run task + cb(&project); + // Cleanup. + project.close(); + assert!(!project.is_open(), "Project was not closed"); + remote + .delete_project(&project) + .expect("Failed to delete project"); +} + +#[rstest] +fn test_connection(_session: &Session) { + let remotes = binaryninja::collaboration::known_remotes(); + if remotes.is_empty() { + eprintln!("No known remotes, skipping test..."); + return; + } + let remote = remotes.iter().next().expect("No known remotes!"); + assert!(remote.connect().is_ok(), "Failed to connect to remote"); + remote + .disconnect() + .expect("Failed to disconnect from remote"); + assert!(!remote.is_connected(), "Connection was not disconnected"); +} + +#[rstest] +fn test_project_creation(_session: &Session) { + let remotes = binaryninja::collaboration::known_remotes(); + if remotes.is_empty() { + eprintln!("No known remotes, skipping test..."); + return; + } + let remote = remotes.iter().next().expect("No known remotes!"); + temp_project_scope(&remote, |project| { + // Create the file than verify it by opening and checking contents. + let created_file = project + .create_file( + "test_file", + b"this is my file", + "test_file", + "", + None, + RemoteFileType::UnknownFileType, + ) + .expect("Failed to create file in project"); + let created_file_id = created_file.id(); + assert_eq!(created_file.created_by(), remote.username()); + project + .delete_file(&created_file) + .expect("Failed to delete file"); + assert!( + !project + .get_file_by_id(created_file_id) + .is_ok_and(|f| f.is_some()), + "File was not deleted" + ); + + // Create a folder and verify it was created. + let created_folder = project + .create_folder("test_folder", "test_folder_desc", None) + .unwrap(); + let created_folder_id = created_folder.id(); + assert_eq!(created_folder.name().as_str(), "test_folder"); + assert_eq!(created_folder.description().as_str(), "test_folder_desc"); + + // Create a file in said folder and verify it exists in it. + let created_folder_file = project + .create_file( + "test_folder_file", + b"this is my file", + "test_folder_file", + "", + Some(&created_folder), + RemoteFileType::UnknownFileType, + ) + .expect("Failed to create file in project folder"); + let created_folder_file_id = created_folder_file.id(); + // Verify the file exists in the folder. + let check_folder_file = project + .get_file_by_id(created_folder_file_id) + .expect("Failed to get folder file by id") + .unwrap(); + assert_eq!(check_folder_file.name().as_str(), "test_folder_file"); + assert_eq!( + check_folder_file.folder().unwrap().unwrap().id(), + created_folder_id, + "Folder id does not match" + ); + project + .delete_file(&created_folder_file) + .expect("Failed to delete file"); + + // Verify the folder can be deleted. + project + .delete_folder(&created_folder) + .expect("Failed to delete folder"); + assert!( + !project + .get_folder_by_id(created_folder_id) + .is_ok_and(|f| f.is_some()), + "Folder was not deleted" + ); + }) +} + +#[rstest] +fn test_project_sync(_session: &Session) { + let remotes = binaryninja::collaboration::known_remotes(); + if remotes.is_empty() { + eprintln!("No known remotes, skipping test..."); + return; + } + let remote = remotes.iter().next().expect("No known remotes!"); + temp_project_scope(&remote, |project| { + // Open a view so that we can upload it. + let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + let view_type = view.view_type(); + // Save the view to local database so that we can upload it + assert!( + view.file().create_database(out_dir.join("atox.obj.bndb")), + "Failed to create local database" + ); + // We should have a single snapshot. + assert_eq!(view.file().database().unwrap().snapshots().len(), 1); + // Update the entry function name. + let entry_function = view + .entry_point_function() + .expect("Failed to get entry point function"); + let new_entry_func_symbol = + SymbolBuilder::new(SymbolType::Function, "test", entry_function.start()).create(); + view.define_user_symbol(&new_entry_func_symbol); + // Verify that we modified the binary + assert_eq!(entry_function.symbol().raw_name().as_str(), "test"); + // Make new snapshot. + assert!(view.file().save_auto_snapshot()); + // We should have two snapshots. + assert_eq!(view.file().database().unwrap().snapshots().len(), 2); + // Upload database and verify its remote file stuff + let remote_file = project + .upload_database(&view.file(), None, NoNameChangeset) + .expect("Failed to upload database"); + assert_eq!(remote_file.name().as_str(), "atox.obj"); + assert_eq!(remote_file.created_by(), remote.username()); + assert_eq!( + remote_file.file_type(), + RemoteFileType::BinaryViewAnalysisFileType + ); + // Delete local database and download remote one to verify changes. + view.file().close(); + drop(view); + // Verify that the remote file exists. + project + .get_file_by_id(remote_file.id()) + .expect("Failed to get remote file by id"); + // Download the remote database with our changes. + let downloaded_file = remote_file + .download_database(out_dir.join("downloaded_atox.obj.bndb")) + .expect("Failed to download database"); + let downloaded_view = downloaded_file + .view_of_type(view_type) + .expect("Failed to open downloaded view"); + // Verify the changes in the entry function. + let entry_function = downloaded_view + .entry_point_function() + .expect("Failed to get entry point function"); + assert_eq!(entry_function.symbol().raw_name().as_str(), "test"); + project + .delete_file(&remote_file) + .expect("Failed to delete file"); + }); +} |
