Integration tests for excluded files

Kirill Bulatov created

Change summary

crates/collab/src/tests/following_tests.rs | 137 ++++++++++++++++++++++++
crates/project/src/search.rs               |   2 
2 files changed, 138 insertions(+), 1 deletion(-)

Detailed changes

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

@@ -4,8 +4,10 @@ use collab_ui::notifications::project_shared_notification::ProjectSharedNotifica
 use editor::{Editor, ExcerptRange, MultiBuffer};
 use gpui::{executor::Deterministic, geometry::vector::vec2f, TestAppContext, ViewHandle};
 use live_kit_client::MacOSDisplay;
+use project::project_settings::ProjectSettings;
 use rpc::proto::PeerId;
 use serde_json::json;
+use settings::SettingsStore;
 use std::{borrow::Cow, sync::Arc};
 use workspace::{
     dock::{test::TestPanel, DockPosition},
@@ -1602,6 +1604,141 @@ async fn test_following_across_workspaces(
     });
 }
 
+#[gpui::test]
+async fn test_following_into_excluded_file(
+    deterministic: Arc<Deterministic>,
+    mut cx_a: &mut TestAppContext,
+    mut 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;
+    for cx in [&mut cx_a, &mut cx_b] {
+        cx.update(|cx| {
+            cx.update_global::<SettingsStore, _, _>(|store, cx| {
+                store.update_user_settings::<ProjectSettings>(cx, |project_settings| {
+                    project_settings.file_scan_exclusions = Some(vec!["**/.git".to_string()]);
+                });
+            });
+        });
+    }
+    server
+        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
+        .await;
+    let active_call_a = cx_a.read(ActiveCall::global);
+    let active_call_b = cx_b.read(ActiveCall::global);
+
+    cx_a.update(editor::init);
+    cx_b.update(editor::init);
+
+    client_a
+        .fs()
+        .insert_tree(
+            "/a",
+            json!({
+                ".git": {
+                    "COMMIT_EDITMSG": "write your commit message here",
+                },
+                "1.txt": "one\none\none",
+                "2.txt": "two\ntwo\ntwo",
+                "3.txt": "three\nthree\nthree",
+            }),
+        )
+        .await;
+    let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
+    active_call_a
+        .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
+        .await
+        .unwrap();
+
+    let project_id = active_call_a
+        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
+        .await
+        .unwrap();
+    let project_b = client_b.build_remote_project(project_id, cx_b).await;
+    active_call_b
+        .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
+        .await
+        .unwrap();
+
+    let window_a = client_a.build_workspace(&project_a, cx_a);
+    let workspace_a = window_a.root(cx_a);
+    let peer_id_a = client_a.peer_id().unwrap();
+    let window_b = client_b.build_workspace(&project_b, cx_b);
+    let workspace_b = window_b.root(cx_b);
+
+    // Client A opens editors for a regular file and an excluded file.
+    let editor_for_regular = workspace_a
+        .update(cx_a, |workspace, cx| {
+            workspace.open_path((worktree_id, "1.txt"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+    let editor_for_excluded_a = workspace_a
+        .update(cx_a, |workspace, cx| {
+            workspace.open_path((worktree_id, ".git/COMMIT_EDITMSG"), None, true, cx)
+        })
+        .await
+        .unwrap()
+        .downcast::<Editor>()
+        .unwrap();
+
+    // Client A updates their selections in those editors
+    editor_for_regular.update(cx_a, |editor, cx| {
+        editor.handle_input("a", cx);
+        editor.handle_input("b", cx);
+        editor.handle_input("c", cx);
+        editor.select_left(&Default::default(), cx);
+        assert_eq!(editor.selections.ranges(cx), vec![3..2]);
+    });
+    editor_for_excluded_a.update(cx_a, |editor, cx| {
+        editor.select_all(&Default::default(), cx);
+        editor.handle_input("new commit message", cx);
+        editor.select_left(&Default::default(), cx);
+        assert_eq!(editor.selections.ranges(cx), vec![18..17]);
+    });
+
+    // When client B starts following client A, currently visible file is replicated
+    workspace_b
+        .update(cx_b, |workspace, cx| {
+            workspace.follow(peer_id_a, cx).unwrap()
+        })
+        .await
+        .unwrap();
+
+    let editor_for_excluded_b = workspace_b.read_with(cx_b, |workspace, cx| {
+        workspace
+            .active_item(cx)
+            .unwrap()
+            .downcast::<Editor>()
+            .unwrap()
+    });
+    assert_eq!(
+        cx_b.read(|cx| editor_for_excluded_b.project_path(cx)),
+        Some((worktree_id, ".git/COMMIT_EDITMSG").into())
+    );
+    assert_eq!(
+        editor_for_excluded_b.read_with(cx_b, |editor, cx| editor.selections.ranges(cx)),
+        vec![18..17]
+    );
+
+    // Changes from B to the excluded file are replicated in A's editor
+    editor_for_excluded_b.update(cx_b, |editor, cx| {
+        editor.handle_input("\nCo-Authored-By: B <b@b.b>", cx);
+    });
+    deterministic.run_until_parked();
+    editor_for_excluded_a.update(cx_a, |editor, cx| {
+        assert_eq!(
+            editor.text(cx),
+            "new commit messag\nCo-Authored-By: B <b@b.b>"
+        );
+    });
+}
+
 fn visible_push_notifications(
     cx: &mut TestAppContext,
 ) -> Vec<gpui::ViewHandle<ProjectSharedNotification>> {

crates/project/src/search.rs 🔗

@@ -372,7 +372,7 @@ impl SearchQuery {
         match file_path {
             Some(file_path) => {
                 let mut path = file_path.to_path_buf();
-                let mut matches = false;
+                let mut matches;
                 loop {
                     matches = !self
                         .files_to_exclude()