remote_editing_collaboration_tests.rs

  1use crate::tests::TestServer;
  2use call::ActiveCall;
  3use fs::{FakeFs, Fs as _};
  4use gpui::{Context as _, TestAppContext};
  5use http_client::BlockedHttpClient;
  6use language::{language_settings::language_settings, LanguageRegistry};
  7use node_runtime::NodeRuntime;
  8use project::ProjectPath;
  9use remote::SshRemoteClient;
 10use remote_server::{HeadlessAppState, HeadlessProject};
 11use serde_json::json;
 12use std::{path::Path, sync::Arc};
 13
 14#[gpui::test(iterations = 10)]
 15async fn test_sharing_an_ssh_remote_project(
 16    cx_a: &mut TestAppContext,
 17    cx_b: &mut TestAppContext,
 18    server_cx: &mut TestAppContext,
 19) {
 20    let executor = cx_a.executor();
 21    let mut server = TestServer::start(executor.clone()).await;
 22    let client_a = server.create_client(cx_a, "user_a").await;
 23    let client_b = server.create_client(cx_b, "user_b").await;
 24    server
 25        .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
 26        .await;
 27
 28    // Set up project on remote FS
 29    let (port, server_ssh) = SshRemoteClient::fake_server(cx_a, server_cx);
 30    let remote_fs = FakeFs::new(server_cx.executor());
 31    remote_fs
 32        .insert_tree(
 33            "/code",
 34            json!({
 35                "project1": {
 36                    ".zed": {
 37                        "settings.json": r#"{"languages":{"Rust":{"language_servers":["override-rust-analyzer"]}}}"#
 38                    },
 39                    "README.md": "# project 1",
 40                    "src": {
 41                        "lib.rs": "fn one() -> usize { 1 }"
 42                    }
 43                },
 44                "project2": {
 45                    "README.md": "# project 2",
 46                },
 47            }),
 48        )
 49        .await;
 50
 51    // User A connects to the remote project via SSH.
 52    server_cx.update(HeadlessProject::init);
 53    let remote_http_client = Arc::new(BlockedHttpClient);
 54    let node = NodeRuntime::unavailable();
 55    let languages = Arc::new(LanguageRegistry::new(server_cx.executor()));
 56    let _headless_project = server_cx.new_model(|cx| {
 57        client::init_settings(cx);
 58        HeadlessProject::new(
 59            HeadlessAppState {
 60                session: server_ssh,
 61                fs: remote_fs.clone(),
 62                http_client: remote_http_client,
 63                node_runtime: node,
 64                languages,
 65            },
 66            cx,
 67        )
 68    });
 69
 70    let client_ssh = SshRemoteClient::fake_client(port, cx_a).await;
 71    let (project_a, worktree_id) = client_a
 72        .build_ssh_project("/code/project1", client_ssh, cx_a)
 73        .await;
 74
 75    // While the SSH worktree is being scanned, user A shares the remote project.
 76    let active_call_a = cx_a.read(ActiveCall::global);
 77    let project_id = active_call_a
 78        .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
 79        .await
 80        .unwrap();
 81
 82    // User B joins the project.
 83    let project_b = client_b.join_remote_project(project_id, cx_b).await;
 84    let worktree_b = project_b
 85        .update(cx_b, |project, cx| project.worktree_for_id(worktree_id, cx))
 86        .unwrap();
 87
 88    let worktree_a = project_a
 89        .update(cx_a, |project, cx| project.worktree_for_id(worktree_id, cx))
 90        .unwrap();
 91
 92    executor.run_until_parked();
 93
 94    worktree_a.update(cx_a, |worktree, _cx| {
 95        assert_eq!(
 96            worktree.paths().map(Arc::as_ref).collect::<Vec<_>>(),
 97            vec![
 98                Path::new(".zed"),
 99                Path::new(".zed/settings.json"),
100                Path::new("README.md"),
101                Path::new("src"),
102                Path::new("src/lib.rs"),
103            ]
104        );
105    });
106
107    worktree_b.update(cx_b, |worktree, _cx| {
108        assert_eq!(
109            worktree.paths().map(Arc::as_ref).collect::<Vec<_>>(),
110            vec![
111                Path::new(".zed"),
112                Path::new(".zed/settings.json"),
113                Path::new("README.md"),
114                Path::new("src"),
115                Path::new("src/lib.rs"),
116            ]
117        );
118    });
119
120    // User B can open buffers in the remote project.
121    let buffer_b = project_b
122        .update(cx_b, |project, cx| {
123            project.open_buffer((worktree_id, "src/lib.rs"), cx)
124        })
125        .await
126        .unwrap();
127    buffer_b.update(cx_b, |buffer, cx| {
128        assert_eq!(buffer.text(), "fn one() -> usize { 1 }");
129        let ix = buffer.text().find('1').unwrap();
130        buffer.edit([(ix..ix + 1, "100")], None, cx);
131    });
132
133    executor.run_until_parked();
134
135    cx_b.read(|cx| {
136        let file = buffer_b.read(cx).file();
137        assert_eq!(
138            language_settings(Some("Rust".into()), file, cx).language_servers,
139            ["override-rust-analyzer".to_string()]
140        )
141    });
142
143    project_b
144        .update(cx_b, |project, cx| {
145            project.save_buffer_as(
146                buffer_b.clone(),
147                ProjectPath {
148                    worktree_id: worktree_id.to_owned(),
149                    path: Arc::from(Path::new("src/renamed.rs")),
150                },
151                cx,
152            )
153        })
154        .await
155        .unwrap();
156    assert_eq!(
157        remote_fs
158            .load("/code/project1/src/renamed.rs".as_ref())
159            .await
160            .unwrap(),
161        "fn one() -> usize { 100 }"
162    );
163    cx_b.run_until_parked();
164    cx_b.update(|cx| {
165        assert_eq!(
166            buffer_b
167                .read(cx)
168                .file()
169                .unwrap()
170                .path()
171                .to_string_lossy()
172                .to_string(),
173            "src/renamed.rs".to_string()
174        );
175    });
176}