Fix outstanding compiler errors in randomized collaboration test

Antonio Scandurra created

Change summary

crates/collab/src/integration_tests.rs | 844 ++++++---------------------
1 file changed, 187 insertions(+), 657 deletions(-)

Detailed changes

crates/collab/src/integration_tests.rs 🔗

@@ -28,7 +28,7 @@ use gpui::{
 };
 use language::{
     range_to_lsp, tree_sitter_rust, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
-    LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, Rope,
+    LanguageConfig, LanguageRegistry, OffsetRangeExt, Point, PointUtf16, Rope,
 };
 use live_kit_client::MacOSDisplay;
 use lsp::{self, FakeLanguageServer};
@@ -5779,6 +5779,8 @@ async fn test_random_collaboration(
     rng: StdRng,
 ) {
     deterministic.forbid_parking();
+    let rng = Arc::new(Mutex::new(rng));
+
     let max_peers = env::var("MAX_PEERS")
         .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
         .unwrap_or(5);
@@ -5788,14 +5790,6 @@ async fn test_random_collaboration(
         .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
         .unwrap_or(10);
 
-    let rng = Arc::new(Mutex::new(rng));
-
-    let guest_lang_registry = Arc::new(LanguageRegistry::test());
-    let host_language_registry = Arc::new(LanguageRegistry::test());
-
-    let fs = FakeFs::new(cx.background());
-    fs.insert_tree("/_collab", json!({"init": ""})).await;
-
     let mut server = TestServer::start(cx.foreground(), cx.background()).await;
     let db = server.app_state.db.clone();
 
@@ -5858,140 +5852,8 @@ async fn test_random_collaboration(
     let mut user_ids = Vec::new();
     let mut peer_ids = Vec::new();
     let mut op_start_signals = Vec::new();
-
     let mut next_entity_id = 100000;
 
-    // Set up fake language servers.
-    let mut language = Language::new(
-        LanguageConfig {
-            name: "Rust".into(),
-            path_suffixes: vec!["rs".to_string()],
-            ..Default::default()
-        },
-        None,
-    );
-    let _fake_servers = language
-        .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-            name: "the-fake-language-server",
-            capabilities: lsp::LanguageServer::full_capabilities(),
-            initializer: Some(Box::new({
-                let rng = rng.clone();
-                let fs = fs.clone();
-                let project = host_project.downgrade();
-                move |fake_server: &mut FakeLanguageServer| {
-                    fake_server.handle_request::<lsp::request::Completion, _, _>(
-                        |_, _| async move {
-                            Ok(Some(lsp::CompletionResponse::Array(vec![
-                                lsp::CompletionItem {
-                                    text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
-                                        range: lsp::Range::new(
-                                            lsp::Position::new(0, 0),
-                                            lsp::Position::new(0, 0),
-                                        ),
-                                        new_text: "the-new-text".to_string(),
-                                    })),
-                                    ..Default::default()
-                                },
-                            ])))
-                        },
-                    );
-
-                    fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
-                        |_, _| async move {
-                            Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
-                                lsp::CodeAction {
-                                    title: "the-code-action".to_string(),
-                                    ..Default::default()
-                                },
-                            )]))
-                        },
-                    );
-
-                    fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
-                        |params, _| async move {
-                            Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
-                                params.position,
-                                params.position,
-                            ))))
-                        },
-                    );
-
-                    fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
-                        let fs = fs.clone();
-                        let rng = rng.clone();
-                        move |_, _| {
-                            let fs = fs.clone();
-                            let rng = rng.clone();
-                            async move {
-                                let files = fs.files().await;
-                                let mut rng = rng.lock();
-                                let count = rng.gen_range::<usize, _>(1..3);
-                                let files = (0..count)
-                                    .map(|_| files.choose(&mut *rng).unwrap())
-                                    .collect::<Vec<_>>();
-                                log::info!("LSP: Returning definitions in files {:?}", &files);
-                                Ok(Some(lsp::GotoDefinitionResponse::Array(
-                                    files
-                                        .into_iter()
-                                        .map(|file| lsp::Location {
-                                            uri: lsp::Url::from_file_path(file).unwrap(),
-                                            range: Default::default(),
-                                        })
-                                        .collect(),
-                                )))
-                            }
-                        }
-                    });
-
-                    fake_server.handle_request::<lsp::request::DocumentHighlightRequest, _, _>({
-                        let rng = rng.clone();
-                        let project = project;
-                        move |params, mut cx| {
-                            let highlights = if let Some(project) = project.upgrade(&cx) {
-                                project.update(&mut cx, |project, cx| {
-                                    let path = params
-                                        .text_document_position_params
-                                        .text_document
-                                        .uri
-                                        .to_file_path()
-                                        .unwrap();
-                                    let (worktree, relative_path) =
-                                        project.find_local_worktree(&path, cx)?;
-                                    let project_path =
-                                        ProjectPath::from((worktree.read(cx).id(), relative_path));
-                                    let buffer =
-                                        project.get_open_buffer(&project_path, cx)?.read(cx);
-
-                                    let mut highlights = Vec::new();
-                                    let highlight_count = rng.lock().gen_range(1..=5);
-                                    let mut prev_end = 0;
-                                    for _ in 0..highlight_count {
-                                        let range =
-                                            buffer.random_byte_range(prev_end, &mut *rng.lock());
-
-                                        highlights.push(lsp::DocumentHighlight {
-                                            range: range_to_lsp(range.to_point_utf16(buffer)),
-                                            kind: Some(lsp::DocumentHighlightKind::READ),
-                                        });
-                                        prev_end = range.end;
-                                    }
-                                    Some(highlights)
-                                })
-                            } else {
-                                None
-                            };
-                            async move { Ok(highlights) }
-                        }
-                    });
-                }
-            })),
-            ..Default::default()
-        }))
-        .await;
-    let language = Arc::new(language);
-
-    let op_start_signal = futures::channel::mpsc::unbounded();
-
     let mut operations = 0;
     while operations < max_operations {
         let distribution = rng.lock().gen_range(0..100);
@@ -6014,7 +5876,6 @@ async fn test_random_collaboration(
 
                 deterministic.start_waiting();
                 let guest = server.create_client(&mut guest_cx, &guest_username).await;
-                guest.language_registry.add(language.clone());
                 let guest_user_id = guest.current_user_id(&guest_cx);
 
                 active_call
@@ -6095,7 +5956,7 @@ async fn test_random_collaboration(
 
                 operations += 1;
             }
-            _ => {
+            _ if !op_start_signals.is_empty() => {
                 while operations < max_operations && rng.lock().gen_bool(0.7) {
                     op_start_signals
                         .choose(&mut *rng.lock())
@@ -6109,32 +5970,33 @@ async fn test_random_collaboration(
                     deterministic.run_until_parked();
                 }
             }
+            _ => {}
         }
     }
 
     drop(op_start_signals);
     deterministic.start_waiting();
-    let mut clients = futures::future::join_all(clients).await;
+    let clients = futures::future::join_all(clients).await;
     deterministic.finish_waiting();
     deterministic.run_until_parked();
 
-    for (guest_client, mut guest_cx, guest_err) in &clients {
+    for (guest_client, guest_cx, guest_err) in &clients {
         if let Some(guest_err) = guest_err {
             panic!("{} error - {:?}", guest_client.username, guest_err);
         }
 
-        for guest_project in guest_client.remote_projects {
-            guest_project.read_with(&guest_cx, |guest_project, cx| {
+        for guest_project in &guest_client.remote_projects {
+            guest_project.read_with(guest_cx, |guest_project, cx| {
                 let host_project = clients.iter().find_map(|(client, cx, _)| {
                     let project = client.local_projects.iter().find(|host_project| {
-                        host_project.read_with(cx, |host_project, cx| {
+                        host_project.read_with(cx, |host_project, _| {
                             host_project.remote_id() == guest_project.remote_id()
                         })
                     })?;
-                    Some((client, project, cx))
+                    Some((project, cx))
                 });
 
-                if let Some((host_client, host_project, host_cx)) = host_project {
+                if let Some((host_project, host_cx)) = host_project {
                     let host_worktree_snapshots =
                         host_project.read_with(host_cx, |host_project, cx| {
                             host_project
@@ -6186,44 +6048,61 @@ async fn test_random_collaboration(
             });
         }
 
-        for guest_buffer in &guest_client.buffers {
-            let buffer_id = guest_buffer.read_with(&guest_cx, |buffer, _| buffer.remote_id());
-            let host_buffer = host_project.read_with(&host_cx, |project, cx| {
-                project.buffer_for_id(buffer_id, cx).unwrap_or_else(|| {
-                    panic!(
-                        "host does not have buffer for guest:{}, peer:{:?}, id:{}",
-                        guest_client.username,
-                        guest_client.peer_id(),
-                        buffer_id
-                    )
-                })
+        for (project_id, guest_buffers) in &guest_client.buffers {
+            let host_project = clients.iter().find_map(|(client, cx, _)| {
+                let project = client.local_projects.iter().find(|host_project| {
+                    host_project.read_with(cx, |host_project, _| {
+                        host_project.remote_id() == Some(*project_id)
+                    })
+                })?;
+                Some((project, cx))
             });
-            let path =
-                host_buffer.read_with(&host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
 
-            assert_eq!(
-                guest_buffer.read_with(&guest_cx, |buffer, _| buffer.deferred_ops_len()),
-                0,
-                "{}, buffer {}, path {:?} has deferred operations",
-                guest_client.username,
-                buffer_id,
-                path,
-            );
-            assert_eq!(
-                guest_buffer.read_with(&guest_cx, |buffer, _| buffer.text()),
-                host_buffer.read_with(&host_cx, |buffer, _| buffer.text()),
-                "{}, buffer {}, path {:?}, differs from the host's buffer",
-                guest_client.username,
-                buffer_id,
-                path
-            );
+            let (host_project, host_cx) = if let Some((host_project, host_cx)) = host_project {
+                (host_project, host_cx)
+            } else {
+                continue;
+            };
+
+            for guest_buffer in guest_buffers {
+                let buffer_id = guest_buffer.read_with(guest_cx, |buffer, _| buffer.remote_id());
+                let host_buffer = host_project.read_with(host_cx, |project, cx| {
+                    project.buffer_for_id(buffer_id, cx).unwrap_or_else(|| {
+                        panic!(
+                            "host does not have buffer for guest:{}, peer:{:?}, id:{}",
+                            guest_client.username,
+                            guest_client.peer_id(),
+                            buffer_id
+                        )
+                    })
+                });
+                let path = host_buffer
+                    .read_with(host_cx, |buffer, cx| buffer.file().unwrap().full_path(cx));
+
+                assert_eq!(
+                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.deferred_ops_len()),
+                    0,
+                    "{}, buffer {}, path {:?} has deferred operations",
+                    guest_client.username,
+                    buffer_id,
+                    path,
+                );
+                assert_eq!(
+                    guest_buffer.read_with(guest_cx, |buffer, _| buffer.text()),
+                    host_buffer.read_with(host_cx, |buffer, _| buffer.text()),
+                    "{}, buffer {}, path {:?}, differs from the host's buffer",
+                    guest_client.username,
+                    buffer_id,
+                    path
+                );
+            }
         }
     }
 
-    for (guest_client, guest_cx, _) in clients {
-        guest_cx.update(|cx| {
+    for (client, mut cx, _) in clients {
+        cx.update(|cx| {
             cx.clear_globals();
-            drop(guest_client);
+            drop(client);
         });
     }
 }
@@ -6520,7 +6399,7 @@ struct TestClient {
     pub project_store: ModelHandle<ProjectStore>,
     language_registry: Arc<LanguageRegistry>,
     fs: Arc<FakeFs>,
-    buffers: HashSet<ModelHandle<language::Buffer>>,
+    buffers: HashMap<u64, HashSet<ModelHandle<language::Buffer>>>,
 }
 
 impl Deref for TestClient {
@@ -6635,486 +6514,141 @@ impl TestClient {
         })
     }
 
-    async fn simulate_host(
+    pub async fn simulate(
         mut self,
-        project: ModelHandle<Project>,
+        username: String,
         op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
         rng: Arc<Mutex<StdRng>>,
         mut cx: TestAppContext,
-    ) -> (
-        Self,
-        ModelHandle<Project>,
-        TestAppContext,
-        Option<anyhow::Error>,
-    ) {
-        async fn simulate_host_internal(
+    ) -> (Self, TestAppContext, Option<anyhow::Error>) {
+        async fn simulate_internal(
             client: &mut TestClient,
-            project: ModelHandle<Project>,
+            username: &str,
             mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
             rng: Arc<Mutex<StdRng>>,
             cx: &mut TestAppContext,
         ) -> anyhow::Result<()> {
-            let fs = project.read_with(cx, |project, _| project.fs().clone());
+            // Setup language server
+            let mut language = Language::new(
+                LanguageConfig {
+                    name: "Rust".into(),
+                    path_suffixes: vec!["rs".to_string()],
+                    ..Default::default()
+                },
+                None,
+            );
+            language
+                .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                    name: "the-fake-language-server",
+                    capabilities: lsp::LanguageServer::full_capabilities(),
+                    initializer: Some(Box::new({
+                        let rng = rng.clone();
+                        let fs = client.fs.clone();
+                        move |fake_server: &mut FakeLanguageServer| {
+                            fake_server.handle_request::<lsp::request::Completion, _, _>(
+                                |_, _| async move {
+                                    Ok(Some(lsp::CompletionResponse::Array(vec![
+                                        lsp::CompletionItem {
+                                            text_edit: Some(lsp::CompletionTextEdit::Edit(
+                                                lsp::TextEdit {
+                                                    range: lsp::Range::new(
+                                                        lsp::Position::new(0, 0),
+                                                        lsp::Position::new(0, 0),
+                                                    ),
+                                                    new_text: "the-new-text".to_string(),
+                                                },
+                                            )),
+                                            ..Default::default()
+                                        },
+                                    ])))
+                                },
+                            );
 
-            while op_start_signal.next().await.is_some() {
-                let distribution = rng.lock().gen_range::<usize, _>(0..100);
-                let files = fs.as_fake().files().await;
-                match distribution {
-                    0..=19 if !files.is_empty() => {
-                        let path = files.choose(&mut *rng.lock()).unwrap();
-                        let mut path = path.as_path();
-                        while let Some(parent_path) = path.parent() {
-                            path = parent_path;
-                            if rng.lock().gen() {
-                                break;
-                            }
-                        }
+                            fake_server.handle_request::<lsp::request::CodeActionRequest, _, _>(
+                                |_, _| async move {
+                                    Ok(Some(vec![lsp::CodeActionOrCommand::CodeAction(
+                                        lsp::CodeAction {
+                                            title: "the-code-action".to_string(),
+                                            ..Default::default()
+                                        },
+                                    )]))
+                                },
+                            );
 
-                        log::info!("Host: find/create local worktree {:?}", path);
-                        let find_or_create_worktree = project.update(cx, |project, cx| {
-                            project.find_or_create_local_worktree(path, true, cx)
-                        });
-                        if rng.lock().gen() {
-                            cx.background().spawn(find_or_create_worktree).detach();
-                        } else {
-                            find_or_create_worktree.await?;
-                        }
-                    }
-                    20..=79 if !files.is_empty() => {
-                        let buffer = if client.buffers.is_empty() || rng.lock().gen() {
-                            let file = files.choose(&mut *rng.lock()).unwrap();
-                            let (worktree, path) = project
-                                .update(cx, |project, cx| {
-                                    project.find_or_create_local_worktree(file.clone(), true, cx)
-                                })
-                                .await?;
-                            let project_path =
-                                worktree.read_with(cx, |worktree, _| (worktree.id(), path));
-                            log::info!(
-                                "Host: opening path {:?}, worktree {}, relative_path {:?}",
-                                file,
-                                project_path.0,
-                                project_path.1
+                            fake_server.handle_request::<lsp::request::PrepareRenameRequest, _, _>(
+                                |params, _| async move {
+                                    Ok(Some(lsp::PrepareRenameResponse::Range(lsp::Range::new(
+                                        params.position,
+                                        params.position,
+                                    ))))
+                                },
                             );
-                            let buffer = project
-                                .update(cx, |project, cx| project.open_buffer(project_path, cx))
-                                .await
-                                .unwrap();
-                            client.buffers.insert(buffer.clone());
-                            buffer
-                        } else {
-                            client
-                                .buffers
-                                .iter()
-                                .choose(&mut *rng.lock())
-                                .unwrap()
-                                .clone()
-                        };
 
-                        if rng.lock().gen_bool(0.1) {
-                            cx.update(|cx| {
-                                log::info!(
-                                    "Host: dropping buffer {:?}",
-                                    buffer.read(cx).file().unwrap().full_path(cx)
-                                );
-                                client.buffers.remove(&buffer);
-                                drop(buffer);
-                            });
-                        } else {
-                            buffer.update(cx, |buffer, cx| {
-                                log::info!(
-                                    "Host: updating buffer {:?} ({})",
-                                    buffer.file().unwrap().full_path(cx),
-                                    buffer.remote_id()
-                                );
-
-                                if rng.lock().gen_bool(0.7) {
-                                    buffer.randomly_edit(&mut *rng.lock(), 5, cx);
-                                } else {
-                                    buffer.randomly_undo_redo(&mut *rng.lock(), cx);
+                            fake_server.handle_request::<lsp::request::GotoDefinition, _, _>({
+                                let fs = fs.clone();
+                                let rng = rng.clone();
+                                move |_, _| {
+                                    let fs = fs.clone();
+                                    let rng = rng.clone();
+                                    async move {
+                                        let files = fs.files().await;
+                                        let mut rng = rng.lock();
+                                        let count = rng.gen_range::<usize, _>(1..3);
+                                        let files = (0..count)
+                                            .map(|_| files.choose(&mut *rng).unwrap())
+                                            .collect::<Vec<_>>();
+                                        log::info!(
+                                            "LSP: Returning definitions in files {:?}",
+                                            &files
+                                        );
+                                        Ok(Some(lsp::GotoDefinitionResponse::Array(
+                                            files
+                                                .into_iter()
+                                                .map(|file| lsp::Location {
+                                                    uri: lsp::Url::from_file_path(file).unwrap(),
+                                                    range: Default::default(),
+                                                })
+                                                .collect(),
+                                        )))
+                                    }
                                 }
                             });
-                        }
-                    }
-                    _ => loop {
-                        let path_component_count = rng.lock().gen_range::<usize, _>(1..=5);
-                        let mut path = PathBuf::new();
-                        path.push("/");
-                        for _ in 0..path_component_count {
-                            let letter = rng.lock().gen_range(b'a'..=b'z');
-                            path.push(std::str::from_utf8(&[letter]).unwrap());
-                        }
-                        path.set_extension("rs");
-                        let parent_path = path.parent().unwrap();
-
-                        log::info!("Host: creating file {:?}", path,);
-
-                        if fs.create_dir(parent_path).await.is_ok()
-                            && fs.create_file(&path, Default::default()).await.is_ok()
-                        {
-                            break;
-                        } else {
-                            log::info!("Host: cannot create file");
-                        }
-                    },
-                }
-
-                cx.background().simulate_random_delay().await;
-            }
-
-            Ok(())
-        }
-
-        let result =
-            simulate_host_internal(&mut self, project.clone(), op_start_signal, rng, &mut cx).await;
-        log::info!("Host done");
-        (self, project, cx, result.err())
-    }
-
-    pub async fn simulate_guest(
-        mut self,
-        guest_username: String,
-        project: ModelHandle<Project>,
-        op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
-        rng: Arc<Mutex<StdRng>>,
-        mut cx: TestAppContext,
-    ) -> (
-        Self,
-        ModelHandle<Project>,
-        TestAppContext,
-        Option<anyhow::Error>,
-    ) {
-        async fn simulate_guest_internal(
-            client: &mut TestClient,
-            guest_username: &str,
-            project: ModelHandle<Project>,
-            mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
-            rng: Arc<Mutex<StdRng>>,
-            cx: &mut TestAppContext,
-        ) -> anyhow::Result<()> {
-            while op_start_signal.next().await.is_some() {
-                let buffer = if client.buffers.is_empty() || rng.lock().gen() {
-                    let worktree = if let Some(worktree) = project.read_with(cx, |project, cx| {
-                        project
-                            .worktrees(cx)
-                            .filter(|worktree| {
-                                let worktree = worktree.read(cx);
-                                worktree.is_visible()
-                                    && worktree.entries(false).any(|e| e.is_file())
-                            })
-                            .choose(&mut *rng.lock())
-                    }) {
-                        worktree
-                    } else {
-                        cx.background().simulate_random_delay().await;
-                        continue;
-                    };
-
-                    let (worktree_root_name, project_path) =
-                        worktree.read_with(cx, |worktree, _| {
-                            let entry = worktree
-                                .entries(false)
-                                .filter(|e| e.is_file())
-                                .choose(&mut *rng.lock())
-                                .unwrap();
-                            (
-                                worktree.root_name().to_string(),
-                                (worktree.id(), entry.path.clone()),
-                            )
-                        });
-                    log::info!(
-                        "{}: opening path {:?} in worktree {} ({})",
-                        guest_username,
-                        project_path.1,
-                        project_path.0,
-                        worktree_root_name,
-                    );
-                    let buffer = project
-                        .update(cx, |project, cx| {
-                            project.open_buffer(project_path.clone(), cx)
-                        })
-                        .await?;
-                    log::info!(
-                        "{}: opened path {:?} in worktree {} ({}) with buffer id {}",
-                        guest_username,
-                        project_path.1,
-                        project_path.0,
-                        worktree_root_name,
-                        buffer.read_with(cx, |buffer, _| buffer.remote_id())
-                    );
-                    client.buffers.insert(buffer.clone());
-                    buffer
-                } else {
-                    client
-                        .buffers
-                        .iter()
-                        .choose(&mut *rng.lock())
-                        .unwrap()
-                        .clone()
-                };
-
-                let choice = rng.lock().gen_range(0..100);
-                match choice {
-                    0..=9 => {
-                        cx.update(|cx| {
-                            log::info!(
-                                "{}: dropping buffer {:?}",
-                                guest_username,
-                                buffer.read(cx).file().unwrap().full_path(cx)
-                            );
-                            client.buffers.remove(&buffer);
-                            drop(buffer);
-                        });
-                    }
-                    10..=19 => {
-                        let completions = project.update(cx, |project, cx| {
-                            log::info!(
-                                "{}: requesting completions for buffer {} ({:?})",
-                                guest_username,
-                                buffer.read(cx).remote_id(),
-                                buffer.read(cx).file().unwrap().full_path(cx)
-                            );
-                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
-                            project.completions(&buffer, offset, cx)
-                        });
-                        let completions = cx.background().spawn(async move {
-                            completions
-                                .await
-                                .map_err(|err| anyhow!("completions request failed: {:?}", err))
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching completions request", guest_username);
-                            cx.update(|cx| completions.detach_and_log_err(cx));
-                        } else {
-                            completions.await?;
-                        }
-                    }
-                    20..=29 => {
-                        let code_actions = project.update(cx, |project, cx| {
-                            log::info!(
-                                "{}: requesting code actions for buffer {} ({:?})",
-                                guest_username,
-                                buffer.read(cx).remote_id(),
-                                buffer.read(cx).file().unwrap().full_path(cx)
-                            );
-                            let range = buffer.read(cx).random_byte_range(0, &mut *rng.lock());
-                            project.code_actions(&buffer, range, cx)
-                        });
-                        let code_actions = cx.background().spawn(async move {
-                            code_actions
-                                .await
-                                .map_err(|err| anyhow!("code actions request failed: {:?}", err))
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching code actions request", guest_username);
-                            cx.update(|cx| code_actions.detach_and_log_err(cx));
-                        } else {
-                            code_actions.await?;
-                        }
-                    }
-                    30..=39 if buffer.read_with(cx, |buffer, _| buffer.is_dirty()) => {
-                        let (requested_version, save) = buffer.update(cx, |buffer, cx| {
-                            log::info!(
-                                "{}: saving buffer {} ({:?})",
-                                guest_username,
-                                buffer.remote_id(),
-                                buffer.file().unwrap().full_path(cx)
-                            );
-                            (buffer.version(), buffer.save(cx))
-                        });
-                        let save = cx.background().spawn(async move {
-                            let (saved_version, _, _) = save
-                                .await
-                                .map_err(|err| anyhow!("save request failed: {:?}", err))?;
-                            assert!(saved_version.observed_all(&requested_version));
-                            Ok::<_, anyhow::Error>(())
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching save request", guest_username);
-                            cx.update(|cx| save.detach_and_log_err(cx));
-                        } else {
-                            save.await?;
-                        }
-                    }
-                    40..=44 => {
-                        let prepare_rename = project.update(cx, |project, cx| {
-                            log::info!(
-                                "{}: preparing rename for buffer {} ({:?})",
-                                guest_username,
-                                buffer.read(cx).remote_id(),
-                                buffer.read(cx).file().unwrap().full_path(cx)
-                            );
-                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
-                            project.prepare_rename(buffer, offset, cx)
-                        });
-                        let prepare_rename = cx.background().spawn(async move {
-                            prepare_rename
-                                .await
-                                .map_err(|err| anyhow!("prepare rename request failed: {:?}", err))
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching prepare rename request", guest_username);
-                            cx.update(|cx| prepare_rename.detach_and_log_err(cx));
-                        } else {
-                            prepare_rename.await?;
-                        }
-                    }
-                    45..=49 => {
-                        let definitions = project.update(cx, |project, cx| {
-                            log::info!(
-                                "{}: requesting definitions for buffer {} ({:?})",
-                                guest_username,
-                                buffer.read(cx).remote_id(),
-                                buffer.read(cx).file().unwrap().full_path(cx)
-                            );
-                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
-                            project.definition(&buffer, offset, cx)
-                        });
-                        let definitions = cx.background().spawn(async move {
-                            definitions
-                                .await
-                                .map_err(|err| anyhow!("definitions request failed: {:?}", err))
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching definitions request", guest_username);
-                            cx.update(|cx| definitions.detach_and_log_err(cx));
-                        } else {
-                            client.buffers.extend(
-                                definitions.await?.into_iter().map(|loc| loc.target.buffer),
-                            );
-                        }
-                    }
-                    50..=54 => {
-                        let highlights = project.update(cx, |project, cx| {
-                            log::info!(
-                                "{}: requesting highlights for buffer {} ({:?})",
-                                guest_username,
-                                buffer.read(cx).remote_id(),
-                                buffer.read(cx).file().unwrap().full_path(cx)
-                            );
-                            let offset = rng.lock().gen_range(0..=buffer.read(cx).len());
-                            project.document_highlights(&buffer, offset, cx)
-                        });
-                        let highlights = cx.background().spawn(async move {
-                            highlights
-                                .await
-                                .map_err(|err| anyhow!("highlights request failed: {:?}", err))
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching highlights request", guest_username);
-                            cx.update(|cx| highlights.detach_and_log_err(cx));
-                        } else {
-                            highlights.await?;
-                        }
-                    }
-                    55..=59 => {
-                        let search = project.update(cx, |project, cx| {
-                            let query = rng.lock().gen_range('a'..='z');
-                            log::info!("{}: project-wide search {:?}", guest_username, query);
-                            project.search(SearchQuery::text(query, false, false), cx)
-                        });
-                        let search = cx.background().spawn(async move {
-                            search
-                                .await
-                                .map_err(|err| anyhow!("search request failed: {:?}", err))
-                        });
-                        if rng.lock().gen_bool(0.3) {
-                            log::info!("{}: detaching search request", guest_username);
-                            cx.update(|cx| search.detach_and_log_err(cx));
-                        } else {
-                            client.buffers.extend(search.await?.into_keys());
-                        }
-                    }
-                    60..=69 => {
-                        let worktree = project
-                            .read_with(cx, |project, cx| {
-                                project
-                                    .worktrees(cx)
-                                    .filter(|worktree| {
-                                        let worktree = worktree.read(cx);
-                                        worktree.is_visible()
-                                            && worktree.entries(false).any(|e| e.is_file())
-                                            && worktree.root_entry().map_or(false, |e| e.is_dir())
-                                    })
-                                    .choose(&mut *rng.lock())
-                            })
-                            .unwrap();
-                        let (worktree_id, worktree_root_name) = worktree
-                            .read_with(cx, |worktree, _| {
-                                (worktree.id(), worktree.root_name().to_string())
-                            });
 
-                        let mut new_name = String::new();
-                        for _ in 0..10 {
-                            let letter = rng.lock().gen_range('a'..='z');
-                            new_name.push(letter);
+                            fake_server
+                                .handle_request::<lsp::request::DocumentHighlightRequest, _, _>({
+                                    let rng = rng.clone();
+                                    move |_, _| {
+                                        let mut highlights = Vec::new();
+                                        let highlight_count = rng.lock().gen_range(1..=5);
+                                        for _ in 0..highlight_count {
+                                            let start = PointUtf16::new(
+                                                rng.lock().gen_range(0..100),
+                                                rng.lock().gen_range(0..100),
+                                            );
+                                            let end = PointUtf16::new(
+                                                rng.lock().gen_range(0..100),
+                                                rng.lock().gen_range(0..100),
+                                            );
+                                            let range =
+                                                if start > end { end..start } else { start..end };
+                                            highlights.push(lsp::DocumentHighlight {
+                                                range: range_to_lsp(range.clone()),
+                                                kind: Some(lsp::DocumentHighlightKind::READ),
+                                            });
+                                        }
+                                        highlights.sort_unstable_by_key(|highlight| {
+                                            (highlight.range.start, highlight.range.end)
+                                        });
+                                        async move { Ok(Some(highlights)) }
+                                    }
+                                });
                         }
-                        let mut new_path = PathBuf::new();
-                        new_path.push(new_name);
-                        new_path.set_extension("rs");
-                        log::info!(
-                            "{}: creating {:?} in worktree {} ({})",
-                            guest_username,
-                            new_path,
-                            worktree_id,
-                            worktree_root_name,
-                        );
-                        project
-                            .update(cx, |project, cx| {
-                                project.create_entry((worktree_id, new_path), false, cx)
-                            })
-                            .unwrap()
-                            .await?;
-                    }
-                    _ => {
-                        buffer.update(cx, |buffer, cx| {
-                            log::info!(
-                                "{}: updating buffer {} ({:?})",
-                                guest_username,
-                                buffer.remote_id(),
-                                buffer.file().unwrap().full_path(cx)
-                            );
-                            if rng.lock().gen_bool(0.7) {
-                                buffer.randomly_edit(&mut *rng.lock(), 5, cx);
-                            } else {
-                                buffer.randomly_undo_redo(&mut *rng.lock(), cx);
-                            }
-                        });
-                    }
-                }
-                cx.background().simulate_random_delay().await;
-            }
-            Ok(())
-        }
-
-        let result = simulate_guest_internal(
-            &mut self,
-            &guest_username,
-            project.clone(),
-            op_start_signal,
-            rng,
-            &mut cx,
-        )
-        .await;
-        log::info!("{}: done", guest_username);
-
-        (self, project, cx, result.err())
-    }
+                    })),
+                    ..Default::default()
+                }))
+                .await;
+            client.language_registry.add(Arc::new(language));
 
-    pub async fn simulate(
-        mut self,
-        username: String,
-        op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
-        rng: Arc<Mutex<StdRng>>,
-        mut cx: TestAppContext,
-    ) -> (Self, TestAppContext, Option<anyhow::Error>) {
-        async fn simulate_internal(
-            client: &mut TestClient,
-            username: &str,
-            mut op_start_signal: futures::channel::mpsc::UnboundedReceiver<()>,
-            rng: Arc<Mutex<StdRng>>,
-            cx: &mut TestAppContext,
-        ) -> anyhow::Result<()> {
             while op_start_signal.next().await.is_some() {
                 let active_call = cx.read(ActiveCall::global);
                 let room = active_call.read_with(cx, |call, _| call.room().unwrap().clone());
@@ -7164,12 +6698,13 @@ impl TestClient {
                             .clone()
                     }
                 };
-                active_call
+                let project_id = active_call
                     .update(cx, |call, cx| call.share_project(project.clone(), cx))
                     .await
                     .unwrap();
 
-                let buffer = if client.buffers.is_empty() || rng.lock().gen() {
+                let buffers = client.buffers.entry(project_id).or_default();
+                let buffer = if buffers.is_empty() || rng.lock().gen() {
                     let worktree = if let Some(worktree) = project.read_with(cx, |project, cx| {
                         project
                             .worktrees(cx)
@@ -7218,15 +6753,10 @@ impl TestClient {
                         worktree_root_name,
                         buffer.read_with(cx, |buffer, _| buffer.remote_id())
                     );
-                    client.buffers.insert(buffer.clone());
+                    buffers.insert(buffer.clone());
                     buffer
                 } else {
-                    client
-                        .buffers
-                        .iter()
-                        .choose(&mut *rng.lock())
-                        .unwrap()
-                        .clone()
+                    buffers.iter().choose(&mut *rng.lock()).unwrap().clone()
                 };
 
                 let choice = rng.lock().gen_range(0..100);
@@ -7238,7 +6768,7 @@ impl TestClient {
                                 username,
                                 buffer.read(cx).file().unwrap().full_path(cx)
                             );
-                            client.buffers.remove(&buffer);
+                            buffers.remove(&buffer);
                             drop(buffer);
                         });
                     }
@@ -7355,7 +6885,7 @@ impl TestClient {
                             log::info!("{}: detaching definitions request", username);
                             cx.update(|cx| definitions.detach_and_log_err(cx));
                         } else {
-                            client.buffers.extend(
+                            buffers.extend(
                                 definitions.await?.into_iter().map(|loc| loc.target.buffer),
                             );
                         }
@@ -7398,7 +6928,7 @@ impl TestClient {
                             log::info!("{}: detaching search request", username);
                             cx.update(|cx| search.detach_and_log_err(cx));
                         } else {
-                            client.buffers.extend(search.await?.into_keys());
+                            buffers.extend(search.await?.into_keys());
                         }
                     }
                     60..=69 => {