From 82569a031fa95a907a3d8299729073855091cfa5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 13 Jul 2021 13:24:48 -0600 Subject: [PATCH] Fix race condition in integration test Co-Authored-By: Max Brunsfeld --- server/src/tests.rs | 40 +++++++++++++++++++++++++++++----------- zed/src/worktree.rs | 2 -- zed/src/worktree/fs.rs | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/server/src/tests.rs b/server/src/tests.rs index 19481a4608f8dc8d418392279d8038c23b9c38c2..7eab65e23b0c9e6b703108dc2c9321b2f80ce836 100644 --- a/server/src/tests.rs +++ b/server/src/tests.rs @@ -12,7 +12,7 @@ use sqlx::{ postgres::PgPoolOptions, Executor as _, Postgres, }; -use std::{fs, path::Path, sync::Arc}; +use std::{path::Path, sync::Arc}; use zed::{ editor::Editor, language::LanguageRegistry, @@ -135,15 +135,22 @@ async fn test_propagate_saves_and_fs_changes_in_shared_worktree( let client_b = server.create_client(&mut cx_b, "user_b").await; let client_c = server.create_client(&mut cx_c, "user_c").await; + let fs = Arc::new(FakeFs::new()); + // Share a worktree as client A. - let dir = temp_tree(json!({ - "file1": "", - "file2": "" - })); + fs.insert_tree( + "/a", + json!({ + "file1": "", + "file2": "" + }), + ) + .await; + let worktree_a = Worktree::open_local( - dir.path(), + Path::new("/a"), lang_registry.clone(), - Arc::new(RealFs), + fs.clone(), &mut cx_a.to_async(), ) .await @@ -195,7 +202,13 @@ async fn test_propagate_saves_and_fs_changes_in_shared_worktree( .update(&mut cx_a, |tree, cx| tree.open_buffer("file1", cx)) .await .unwrap(); - buffer_a.update(&mut cx_a, |buf, cx| buf.edit([0..0], "i-am-a", cx)); + + buffer_a + .condition(&mut cx_a, |buf, _| buf.text() == "i-am-c, i-am-b, ") + .await; + buffer_a.update(&mut cx_a, |buf, cx| { + buf.edit([buf.len()..buf.len()], "i-am-a", cx) + }); // Wait for edits to propagate buffer_a @@ -213,7 +226,7 @@ async fn test_propagate_saves_and_fs_changes_in_shared_worktree( buffer_a.update(&mut cx_a, |buf, cx| buf.edit([0..0], "hi-a, ", cx)); save_b.await.unwrap(); assert_eq!( - fs::read_to_string(dir.path().join("file1")).unwrap(), + fs.load(Path::new("/a/file1")).await.unwrap(), "hi-a, i-am-c, i-am-b, i-am-a" ); buffer_a.read_with(&cx_a, |buf, _| assert!(!buf.is_dirty())); @@ -221,8 +234,13 @@ async fn test_propagate_saves_and_fs_changes_in_shared_worktree( buffer_c.condition(&cx_c, |buf, _| !buf.is_dirty()).await; // Make changes on host's file system, see those changes on the guests. - fs::rename(dir.path().join("file2"), dir.path().join("file3")).unwrap(); - fs::write(dir.path().join("file4"), "4").unwrap(); + fs.rename(Path::new("/a/file2"), Path::new("/a/file3")) + .await + .unwrap(); + fs.insert_file(Path::new("/a/file4"), "4".into()) + .await + .unwrap(); + worktree_b .condition(&cx_b, |tree, _| tree.file_count() == 3) .await; diff --git a/zed/src/worktree.rs b/zed/src/worktree.rs index 25f2a1d42215100c376b4e11a469f332ef356650..9f6e6edb01b6c6c6bf0f42ed610a510ab6ec7d2e 100644 --- a/zed/src/worktree.rs +++ b/zed/src/worktree.rs @@ -2519,8 +2519,6 @@ mod remote { rpc: &rpc::Client, cx: &mut AsyncAppContext, ) -> anyhow::Result<()> { - eprintln!("got buffer_saved {:?}", envelope.payload); - rpc.state .read() .await diff --git a/zed/src/worktree/fs.rs b/zed/src/worktree/fs.rs index 5f108b784a74bcd445b36d9347f38db9701c1dfa..d6a86c6d2e706c849c5d48e5e77712d9c3a94820 100644 --- a/zed/src/worktree/fs.rs +++ b/zed/src/worktree/fs.rs @@ -2,7 +2,7 @@ use super::{char_bag::CharBag, char_bag_for_path, Entry, EntryKind, Rope}; use anyhow::{anyhow, Context, Result}; use atomic::Ordering::SeqCst; use fsevent::EventStream; -use futures::{Stream, StreamExt}; +use futures::{future::BoxFuture, Stream, StreamExt}; use postage::prelude::Sink as _; use smol::io::{AsyncReadExt, AsyncWriteExt}; use std::{ @@ -290,6 +290,40 @@ impl FakeFs { Ok(()) } + pub fn insert_tree<'a>( + &'a self, + path: impl 'a + AsRef + Send, + tree: serde_json::Value, + ) -> BoxFuture<'a, ()> { + use futures::FutureExt as _; + use serde_json::Value::*; + + async move { + let path = path.as_ref(); + + match tree { + Object(map) => { + self.insert_dir(path).await.unwrap(); + for (name, contents) in map { + let mut path = PathBuf::from(path); + path.push(name); + self.insert_tree(&path, contents).await; + } + } + Null => { + self.insert_dir(&path).await.unwrap(); + } + String(contents) => { + self.insert_file(&path, contents).await.unwrap(); + } + _ => { + panic!("JSON object must contain only objects, strings, or null"); + } + } + } + .boxed() + } + pub async fn remove(&self, path: &Path) -> Result<()> { let mut state = self.state.lock().await; state.validate_path(path)?;