@@ -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 => {