Add smoke test for collaboration

Mikayla Maki created

Change summary

crates/collab/src/tests/integration_tests.rs | 83 +++++++++++++++++++++
crates/fs/src/fs.rs                          | 27 +++++-
crates/fs/src/repository.rs                  |  4 
3 files changed, 104 insertions(+), 10 deletions(-)

Detailed changes

crates/collab/src/tests/integration_tests.rs 🔗

@@ -13,8 +13,8 @@ use editor::{
 use fs::{FakeFs, Fs as _, LineEnding, RemoveOptions};
 use futures::StreamExt as _;
 use gpui::{
-    executor::Deterministic, geometry::vector::vec2f, test::EmptyView, ModelHandle, TestAppContext,
-    ViewHandle,
+    executor::Deterministic, geometry::vector::vec2f, test::EmptyView, AppContext, ModelHandle,
+    TestAppContext, ViewHandle,
 };
 use indoc::indoc;
 use language::{
@@ -2604,6 +2604,85 @@ async fn test_git_diff_base_change(
     });
 }
 
+#[gpui::test]
+async fn test_git_branch_name(
+    deterministic: Arc<Deterministic>,
+    cx_a: &mut TestAppContext,
+    cx_b: &mut TestAppContext,
+) {
+    deterministic.forbid_parking();
+    let mut server = TestServer::start(&deterministic).await;
+    let client_a = server.create_client(cx_a, "user_a").await;
+    let client_b = server.create_client(cx_b, "user_b").await;
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+
+    client_a
+        .fs
+        .insert_tree(
+            "/dir",
+            json!({
+            ".git": {},
+            }),
+        )
+        .await;
+
+    let (project_local, _worktree_id) = client_a.build_local_project("/dir", cx_a).await;
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| {
+            call.share_project(project_local.clone(), cx)
+        })
+        .await
+        .unwrap();
+
+    let project_remote = client_b.build_remote_project(project_id, cx_b).await;
+    client_a
+        .fs
+        .as_fake()
+        .set_branch_name(Path::new("/dir/.git"), Some("branch-1"))
+        .await;
+
+    // Wait for it to catch up to the new branch
+    deterministic.run_until_parked();
+
+    #[track_caller]
+    fn assert_branch(branch_name: Option<impl Into<String>>, project: &Project, cx: &AppContext) {
+        let branch_name = branch_name.map(Into::into);
+        let worktrees = project.visible_worktrees(cx).collect::<Vec<_>>();
+        assert_eq!(worktrees.len(), 1);
+        let worktree = worktrees[0].clone();
+        let root_entry = worktree.read(cx).snapshot().root_git_entry().unwrap();
+        assert_eq!(root_entry.branch(), branch_name.map(Into::into));
+    }
+
+    // Smoke test branch reading
+    project_local.read_with(cx_a, |project, cx| {
+        assert_branch(Some("branch-1"), project, cx)
+    });
+    project_remote.read_with(cx_b, |project, cx| {
+        assert_branch(Some("branch-1"), project, cx)
+    });
+
+    client_a
+        .fs
+        .as_fake()
+        .set_branch_name(Path::new("/dir/.git"), Some("branch-2"))
+        .await;
+
+    // Wait for buffer_local_a to receive it
+    deterministic.run_until_parked();
+
+    // Smoke test branch reading
+    project_local.read_with(cx_a, |project, cx| {
+        assert_branch(Some("branch-2"), project, cx)
+    });
+    project_remote.read_with(cx_b, |project, cx| {
+        assert_branch(Some("branch-2"), project, cx)
+    });
+}
+
 #[gpui::test(iterations = 10)]
 async fn test_fs_operations(
     deterministic: Arc<Deterministic>,

crates/fs/src/fs.rs 🔗

@@ -619,7 +619,10 @@ impl FakeFs {
         .boxed()
     }
 
-    pub async fn set_index_for_repo(&self, dot_git: &Path, head_state: &[(&Path, String)]) {
+    pub fn with_git_state<F>(&self, dot_git: &Path, f: F)
+    where
+        F: FnOnce(&mut FakeGitRepositoryState),
+    {
         let mut state = self.state.lock();
         let entry = state.read_path(dot_git).unwrap();
         let mut entry = entry.lock();
@@ -628,12 +631,7 @@ impl FakeFs {
             let repo_state = git_repo_state.get_or_insert_with(Default::default);
             let mut repo_state = repo_state.lock();
 
-            repo_state.index_contents.clear();
-            repo_state.index_contents.extend(
-                head_state
-                    .iter()
-                    .map(|(path, content)| (path.to_path_buf(), content.clone())),
-            );
+            f(&mut repo_state);
 
             state.emit_event([dot_git]);
         } else {
@@ -641,6 +639,21 @@ impl FakeFs {
         }
     }
 
+    pub async fn set_branch_name(&self, dot_git: &Path, branch: Option<impl Into<String>>) {
+        self.with_git_state(dot_git, |state| state.branch_name = branch.map(Into::into))
+    }
+
+    pub async fn set_index_for_repo(&self, dot_git: &Path, head_state: &[(&Path, String)]) {
+        self.with_git_state(dot_git, |state| {
+            state.index_contents.clear();
+            state.index_contents.extend(
+                head_state
+                    .iter()
+                    .map(|(path, content)| (path.to_path_buf(), content.clone())),
+            );
+        });
+    }
+
     pub fn paths(&self) -> Vec<PathBuf> {
         let mut result = Vec::new();
         let mut queue = collections::VecDeque::new();

crates/fs/src/repository.rs 🔗

@@ -71,6 +71,7 @@ pub struct FakeGitRepository {
 #[derive(Debug, Clone, Default)]
 pub struct FakeGitRepositoryState {
     pub index_contents: HashMap<PathBuf, String>,
+    pub branch_name: Option<String>,
 }
 
 impl FakeGitRepository {
@@ -89,7 +90,8 @@ impl GitRepository for FakeGitRepository {
     }
 
     fn branch_name(&self) -> Option<String> {
-        None
+        let state = self.state.lock();
+        state.branch_name.clone()
     }
 }