WIP

Nathan Sobo created

Change summary

crates/server/Cargo.toml |  2 
crates/server/src/rpc.rs | 73 +++++++++++++++++++++++++++++------------
2 files changed, 52 insertions(+), 23 deletions(-)

Detailed changes

crates/server/Cargo.toml 🔗

@@ -14,7 +14,6 @@ required-features = ["seed-support"]
 
 [dependencies]
 collections = { path = "../collections" }
-settings = { path = "../settings" }
 rpc = { path = "../rpc" }
 anyhow = "1.0.40"
 async-io = "1.3"
@@ -65,6 +64,7 @@ editor = { path = "../editor", features = ["test-support"] }
 language = { path = "../language", features = ["test-support"] }
 lsp = { path = "../lsp", features = ["test-support"] }
 project = { path = "../project", features = ["test-support"] }
+settings = { path = "../settings", features = ["test-support"] }
 workspace = { path = "../workspace", features = ["test-support"] }
 ctor = "0.1"
 env_logger = "0.8"

crates/server/src/rpc.rs 🔗

@@ -1080,7 +1080,7 @@ mod tests {
     use ::rpc::Peer;
     use client::{
         self, test::FakeHttpClient, Channel, ChannelDetails, ChannelList, Client, Credentials,
-        EstablishConnectionError, UserStore,
+        EstablishConnectionError, UserStore, RECEIVE_TIMEOUT,
     };
     use collections::BTreeMap;
     use editor::{
@@ -4983,6 +4983,7 @@ mod tests {
         let operations = Rc::new(Cell::new(0));
         let mut server = TestServer::start(cx.foreground(), cx.background()).await;
         let mut clients = Vec::new();
+        let mut user_ids = Vec::new();
         let files = Arc::new(Mutex::new(Vec::new()));
 
         let mut next_entity_id = 100000;
@@ -5150,13 +5151,14 @@ mod tests {
         });
         host_language_registry.add(Arc::new(language));
 
+        user_ids.push(host.current_user_id(&host_cx));
         clients.push(cx.foreground().spawn(host.simulate_host(
             host_project,
             files,
             operations.clone(),
             max_operations,
             rng.clone(),
-            host_cx,
+            &mut host_cx,
         )));
 
         while operations.get() < max_operations {
@@ -5191,6 +5193,7 @@ mod tests {
                 )
                 .await
                 .unwrap();
+                user_ids.push(guest.current_user_id(&guest_cx));
                 clients.push(cx.foreground().spawn(guest.simulate_guest(
                     guest_id,
                     guest_project,
@@ -5201,6 +5204,30 @@ mod tests {
                 )));
 
                 log::info!("Guest {} added", guest_id);
+            } else if rng.lock().gen_bool(0.05) {
+                server.disconnect_client(user_ids[0]);
+                cx.foreground().advance_clock(RECEIVE_TIMEOUT);
+                let mut clients = futures::future::join_all(clients).await;
+                cx.foreground().run_until_parked();
+
+                let store = server.store.read();
+                let (host, host_cx) = clients.remove(0);
+                host.project
+                    .as_ref()
+                    .unwrap()
+                    .read_with(&host_cx, |project, _| assert!(!project.is_shared()));
+                for (guest, guest_cx) in clients {
+                    assert!(store
+                        .contacts_for_user(guest.current_user_id(&guest_cx))
+                        .is_empty());
+                    guest
+                        .project
+                        .as_ref()
+                        .unwrap()
+                        .read_with(&guest_cx, |project, _| assert!(project.is_read_only()));
+                }
+
+                return;
             }
         }
 
@@ -5471,6 +5498,14 @@ mod tests {
         }
     }
 
+    impl Deref for TestServer {
+        type Target = Server;
+
+        fn deref(&self) -> &Self::Target {
+            &self.server
+        }
+    }
+
     impl Drop for TestServer {
         fn drop(&mut self) {
             self.peer.reset();
@@ -5584,15 +5619,15 @@ mod tests {
         }
 
         async fn simulate_host(
-            mut self,
+            &mut self,
             project: ModelHandle<Project>,
             files: Arc<Mutex<Vec<PathBuf>>>,
             operations: Rc<Cell<usize>>,
             max_operations: usize,
             rng: Arc<Mutex<StdRng>>,
-            mut cx: TestAppContext,
-        ) -> (Self, TestAppContext) {
-            let fs = project.read_with(&cx, |project, _| project.fs().clone());
+            cx: &mut TestAppContext,
+        ) -> anyhow::Result<()> {
+            let fs = project.read_with(cx, |project, _| project.fs().clone());
             while operations.get() < max_operations {
                 operations.set(operations.get() + 1);
 
@@ -5609,29 +5644,25 @@ mod tests {
                         }
 
                         log::info!("Host: find/create local worktree {:?}", path);
-                        let find_or_create_worktree = project.update(&mut cx, |project, cx| {
+                        let find_or_create_worktree = project.update(cx, |project, cx| {
                             project.find_or_create_local_worktree(path, true, cx)
                         });
-                        let find_or_create_worktree = async move {
-                            find_or_create_worktree.await.unwrap();
-                        };
                         if rng.lock().gen() {
                             cx.background().spawn(find_or_create_worktree).detach();
                         } else {
-                            find_or_create_worktree.await;
+                            find_or_create_worktree.await?;
                         }
                     }
                     10..=80 if !files.lock().is_empty() => {
                         let buffer = if self.buffers.is_empty() || rng.lock().gen() {
                             let file = files.lock().choose(&mut *rng.lock()).unwrap().clone();
                             let (worktree, path) = project
-                                .update(&mut cx, |project, cx| {
+                                .update(cx, |project, cx| {
                                     project.find_or_create_local_worktree(file.clone(), true, cx)
                                 })
-                                .await
-                                .unwrap();
+                                .await?;
                             let project_path =
-                                worktree.read_with(&cx, |worktree, _| (worktree.id(), path));
+                                worktree.read_with(cx, |worktree, _| (worktree.id(), path));
                             log::info!(
                                 "Host: opening path {:?}, worktree {}, relative_path {:?}",
                                 file,
@@ -5639,9 +5670,7 @@ mod tests {
                                 project_path.1
                             );
                             let buffer = project
-                                .update(&mut cx, |project, cx| {
-                                    project.open_buffer(project_path, cx)
-                                })
+                                .update(cx, |project, cx| project.open_buffer(project_path, cx))
                                 .await
                                 .unwrap();
                             self.buffers.insert(buffer.clone());
@@ -5664,7 +5693,7 @@ mod tests {
                                 drop(buffer);
                             });
                         } else {
-                            buffer.update(&mut cx, |buffer, cx| {
+                            buffer.update(cx, |buffer, cx| {
                                 log::info!(
                                     "Host: updating buffer {:?} ({})",
                                     buffer.file().unwrap().full_path(cx),
@@ -5701,10 +5730,10 @@ mod tests {
                 cx.background().simulate_random_delay().await;
             }
 
-            log::info!("Host done");
-
             self.project = Some(project);
-            (self, cx)
+
+            log::info!("Host done");
+            Ok(())
         }
 
         pub async fn simulate_guest(