Add random delays in FakeFs

Max Brunsfeld and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/project/src/fs.rs          | 11 ++++++++++-
crates/project/src/project.rs     |  8 ++++----
crates/project/src/worktree.rs    |  2 +-
crates/server/src/rpc.rs          | 26 +++++++++++++-------------
crates/workspace/src/workspace.rs |  2 +-
crates/zed/src/test.rs            |  2 +-
6 files changed, 30 insertions(+), 21 deletions(-)

Detailed changes

crates/project/src/fs.rs 🔗

@@ -181,11 +181,12 @@ impl FakeFsState {
 pub struct FakeFs {
     // Use an unfair lock to ensure tests are deterministic.
     state: futures::lock::Mutex<FakeFsState>,
+    executor: std::sync::Arc<gpui::executor::Background>,
 }
 
 #[cfg(any(test, feature = "test-support"))]
 impl FakeFs {
-    pub fn new() -> Self {
+    pub fn new(executor: std::sync::Arc<gpui::executor::Background>) -> Self {
         let (events_tx, _) = postage::broadcast::channel(2048);
         let mut entries = std::collections::BTreeMap::new();
         entries.insert(
@@ -201,6 +202,7 @@ impl FakeFs {
             },
         );
         Self {
+            executor,
             state: futures::lock::Mutex::new(FakeFsState {
                 entries,
                 next_inode: 1,
@@ -330,6 +332,7 @@ impl FakeFs {
 #[async_trait::async_trait]
 impl Fs for FakeFs {
     async fn load(&self, path: &Path) -> Result<String> {
+        self.executor.simulate_random_delay().await;
         let state = self.state.lock().await;
         let text = state
             .entries
@@ -340,6 +343,7 @@ impl Fs for FakeFs {
     }
 
     async fn save(&self, path: &Path, text: &Rope) -> Result<()> {
+        self.executor.simulate_random_delay().await;
         let mut state = self.state.lock().await;
         state.validate_path(path)?;
         if let Some(entry) = state.entries.get_mut(path) {
@@ -370,10 +374,12 @@ impl Fs for FakeFs {
     }
 
     async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
+        self.executor.simulate_random_delay().await;
         Ok(path.to_path_buf())
     }
 
     async fn is_file(&self, path: &Path) -> bool {
+        self.executor.simulate_random_delay().await;
         let state = self.state.lock().await;
         state
             .entries
@@ -382,6 +388,7 @@ impl Fs for FakeFs {
     }
 
     async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
+        self.executor.simulate_random_delay().await;
         let state = self.state.lock().await;
         Ok(state.entries.get(path).map(|entry| entry.metadata.clone()))
     }
@@ -391,6 +398,7 @@ impl Fs for FakeFs {
         abs_path: &Path,
     ) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> {
         use futures::{future, stream};
+        self.executor.simulate_random_delay().await;
         let state = self.state.lock().await;
         let abs_path = abs_path.to_path_buf();
         Ok(Box::pin(stream::iter(state.entries.clone()).filter_map(
@@ -410,6 +418,7 @@ impl Fs for FakeFs {
         _: Duration,
     ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>> {
         let state = self.state.lock().await;
+        self.executor.simulate_random_delay().await;
         let rx = state.events_tx.subscribe();
         let path = path.to_path_buf();
         Box::pin(futures::StreamExt::filter(rx, move |events| {

crates/project/src/project.rs 🔗

@@ -2439,7 +2439,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_save_file(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx.background()));
         fs.insert_tree(
             "/dir",
             json!({
@@ -2477,7 +2477,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx.background()));
         fs.insert_tree(
             "/dir",
             json!({
@@ -2664,7 +2664,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_buffer_deduping(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx.background()));
         fs.insert_tree(
             "/the-dir",
             json!({
@@ -2953,7 +2953,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx.background()));
         fs.insert_tree(
             "/the-dir",
             json!({

crates/project/src/worktree.rs 🔗

@@ -2432,7 +2432,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_traversal(cx: gpui::TestAppContext) {
-        let fs = FakeFs::new();
+        let fs = FakeFs::new(cx.background());
         fs.insert_tree(
             "/root",
             json!({

crates/server/src/rpc.rs 🔗

@@ -1201,7 +1201,7 @@ mod tests {
     async fn test_share_project(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         let (window_b, _) = cx_b.add_window(|_| EmptyView);
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
         cx_a.foreground().forbid_parking();
 
         // Connect to a server as 2 clients.
@@ -1339,7 +1339,7 @@ mod tests {
     #[gpui::test]
     async fn test_unshare_project(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
         cx_a.foreground().forbid_parking();
 
         // Connect to a server as 2 clients.
@@ -1440,7 +1440,7 @@ mod tests {
         mut cx_c: TestAppContext,
     ) {
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
         cx_a.foreground().forbid_parking();
 
         // Connect to a server as 3 clients.
@@ -1623,7 +1623,7 @@ mod tests {
     async fn test_buffer_conflict_after_save(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground()).await;
@@ -1716,7 +1716,7 @@ mod tests {
     async fn test_buffer_reloading(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground()).await;
@@ -1801,7 +1801,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground()).await;
@@ -1881,7 +1881,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground()).await;
@@ -1956,7 +1956,7 @@ mod tests {
     async fn test_peer_disconnection(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground()).await;
@@ -2030,7 +2030,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_server) =
@@ -2251,7 +2251,7 @@ mod tests {
     async fn test_formatting_buffer(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_server) =
@@ -2355,7 +2355,7 @@ mod tests {
     async fn test_definition(mut cx_a: TestAppContext, mut cx_b: TestAppContext) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
         fs.insert_tree(
             "/root-1",
             json!({
@@ -2516,7 +2516,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
         fs.insert_tree(
             "/root",
             json!({
@@ -3042,7 +3042,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new());
+        let fs = Arc::new(FakeFs::new(cx_a.background()));
 
         // Connect to a server as 3 clients.
         let mut server = TestServer::start(cx_a.foreground()).await;

crates/workspace/src/workspace.rs 🔗

@@ -490,7 +490,7 @@ pub struct WorkspaceParams {
 impl WorkspaceParams {
     #[cfg(any(test, feature = "test-support"))]
     pub fn test(cx: &mut MutableAppContext) -> Self {
-        let fs = Arc::new(project::FakeFs::new());
+        let fs = Arc::new(project::FakeFs::new(cx.background().clone()));
         let languages = Arc::new(LanguageRegistry::new());
         let http_client = client::test::FakeHttpClient::new(|_| async move {
             Ok(client::http::ServerResponse::new(404))

crates/zed/src/test.rs 🔗

@@ -40,7 +40,7 @@ pub fn test_app_state(cx: &mut MutableAppContext) -> Arc<AppState> {
         channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), client.clone(), cx)),
         client,
         user_store,
-        fs: Arc::new(FakeFs::new()),
+        fs: Arc::new(FakeFs::new(cx.background().clone())),
         path_openers: Arc::from(path_openers),
         build_window_options: &build_window_options,
         build_workspace: &build_workspace,