summaryrefslogtreecommitdiff
path: root/rust/tests/collaboration.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/tests/collaboration.rs')
-rw-r--r--rust/tests/collaboration.rs209
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");
+ });
+}