Convert some project tests to use FakeFs

Max Brunsfeld created

Also, tweak some FakeFs methods to make them slightly more convenient.

Change summary

crates/editor/src/editor.rs       |   4 
crates/project/src/fs.rs          |  23 +++---
crates/project/src/project.rs     | 118 +++++++++++++-------------------
crates/project/src/worktree.rs    |   2 
crates/server/src/rpc.rs          |  36 ++++-----
crates/workspace/src/workspace.rs |   2 
crates/zed/src/test.rs            |   2 
crates/zed/src/zed.rs             |  18 ++--
8 files changed, 89 insertions(+), 116 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -7760,8 +7760,8 @@ mod tests {
         "
         .unindent();
 
-        let fs = Arc::new(FakeFs::new(cx.background().clone()));
-        fs.insert_file("/file", text).await.unwrap();
+        let fs = FakeFs::new(cx.background().clone());
+        fs.insert_file("/file", text).await;
 
         let project = Project::test(fs, &mut cx);
 

crates/project/src/fs.rs 🔗

@@ -7,6 +7,7 @@ use std::{
     os::unix::fs::MetadataExt,
     path::{Path, PathBuf},
     pin::Pin,
+    sync::Arc,
     time::{Duration, SystemTime},
 };
 use text::Rope;
@@ -268,7 +269,7 @@ pub struct FakeFs {
 
 #[cfg(any(test, feature = "test-support"))]
 impl FakeFs {
-    pub fn new(executor: std::sync::Arc<gpui::executor::Background>) -> Self {
+    pub fn new(executor: std::sync::Arc<gpui::executor::Background>) -> Arc<Self> {
         let (events_tx, _) = postage::broadcast::channel(2048);
         let mut entries = std::collections::BTreeMap::new();
         entries.insert(
@@ -283,20 +284,20 @@ impl FakeFs {
                 content: None,
             },
         );
-        Self {
+        Arc::new(Self {
             executor,
             state: futures::lock::Mutex::new(FakeFsState {
                 entries,
                 next_inode: 1,
                 events_tx,
             }),
-        }
+        })
     }
 
-    pub async fn insert_dir(&self, path: impl AsRef<Path>) -> Result<()> {
+    pub async fn insert_dir(&self, path: impl AsRef<Path>) {
         let mut state = self.state.lock().await;
         let path = path.as_ref();
-        state.validate_path(path)?;
+        state.validate_path(path).unwrap();
 
         let inode = state.next_inode;
         state.next_inode += 1;
@@ -313,13 +314,12 @@ impl FakeFs {
             },
         );
         state.emit_event(&[path]).await;
-        Ok(())
     }
 
-    pub async fn insert_file(&self, path: impl AsRef<Path>, content: String) -> Result<()> {
+    pub async fn insert_file(&self, path: impl AsRef<Path>, content: String) {
         let mut state = self.state.lock().await;
         let path = path.as_ref();
-        state.validate_path(path)?;
+        state.validate_path(path).unwrap();
 
         let inode = state.next_inode;
         state.next_inode += 1;
@@ -336,7 +336,6 @@ impl FakeFs {
             },
         );
         state.emit_event(&[path]).await;
-        Ok(())
     }
 
     #[must_use]
@@ -353,7 +352,7 @@ impl FakeFs {
 
             match tree {
                 Object(map) => {
-                    self.insert_dir(path).await.unwrap();
+                    self.insert_dir(path).await;
                     for (name, contents) in map {
                         let mut path = PathBuf::from(path);
                         path.push(name);
@@ -361,10 +360,10 @@ impl FakeFs {
                     }
                 }
                 Null => {
-                    self.insert_dir(&path).await.unwrap();
+                    self.insert_dir(&path).await;
                 }
                 String(contents) => {
-                    self.insert_file(&path, contents).await.unwrap();
+                    self.insert_file(&path, contents).await;
                 }
                 _ => {
                     panic!("JSON object must contain only objects, strings, or null");

crates/project/src/project.rs 🔗

@@ -2980,13 +2980,11 @@ impl From<lsp::DeleteFileOptions> for fs::RemoveOptions {
 #[cfg(test)]
 mod tests {
     use super::{Event, *};
-    use client::test::FakeHttpClient;
     use fs::RealFs;
     use futures::StreamExt;
     use gpui::test::subscribe;
     use language::{
-        tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageRegistry,
-        LanguageServerConfig, Point,
+        tree_sitter_rust, AnchorRangeExt, Diagnostic, LanguageConfig, LanguageServerConfig, Point,
     };
     use lsp::Url;
     use serde_json::json;
@@ -3066,8 +3064,7 @@ mod tests {
             .clone()
             .unwrap();
 
-        let mut languages = LanguageRegistry::new();
-        languages.add(Arc::new(Language::new(
+        let language = Arc::new(Language::new(
             LanguageConfig {
                 name: "Rust".to_string(),
                 path_suffixes: vec!["rs".to_string()],
@@ -3075,30 +3072,26 @@ mod tests {
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        )));
+        ));
 
-        let dir = temp_tree(json!({
-            "a.rs": "fn a() { A }",
-            "b.rs": "const y: i32 = 1",
-        }));
-
-        let http_client = FakeHttpClient::with_404_response();
-        let client = Client::new(http_client.clone());
-        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
+        let fs = FakeFs::new(cx.background());
+        fs.insert_tree(
+            "/dir",
+            json!({
+                "a.rs": "fn a() { A }",
+                "b.rs": "const y: i32 = 1",
+            }),
+        )
+        .await;
 
-        let project = cx.update(|cx| {
-            Project::local(
-                client,
-                user_store,
-                Arc::new(languages),
-                Arc::new(RealFs),
-                cx,
-            )
+        let project = Project::test(fs, &mut cx);
+        project.update(&mut cx, |project, _| {
+            Arc::get_mut(&mut project.languages).unwrap().add(language);
         });
 
         let (tree, _) = project
             .update(&mut cx, |project, cx| {
-                project.find_or_create_local_worktree(dir.path(), false, cx)
+                project.find_or_create_local_worktree("/dir", false, cx)
             })
             .await
             .unwrap();
@@ -3110,13 +3103,7 @@ mod tests {
         // Cause worktree to start the fake language server
         let _buffer = project
             .update(&mut cx, |project, cx| {
-                project.open_buffer(
-                    ProjectPath {
-                        worktree_id,
-                        path: Path::new("b.rs").into(),
-                    },
-                    cx,
-                )
+                project.open_buffer((worktree_id, Path::new("b.rs")), cx)
             })
             .await
             .unwrap();
@@ -3136,7 +3123,7 @@ mod tests {
 
         fake_server
             .notify::<lsp::notification::PublishDiagnostics>(lsp::PublishDiagnosticsParams {
-                uri: Url::from_file_path(dir.path().join("a.rs")).unwrap(),
+                uri: Url::from_file_path("/dir/a.rs").unwrap(),
                 version: None,
                 diagnostics: vec![lsp::Diagnostic {
                     range: lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
@@ -3148,10 +3135,7 @@ mod tests {
             .await;
         assert_eq!(
             events.next().await.unwrap(),
-            Event::DiagnosticsUpdated(ProjectPath {
-                worktree_id,
-                path: Arc::from(Path::new("a.rs"))
-            })
+            Event::DiagnosticsUpdated((worktree_id, Path::new("a.rs")).into())
         );
 
         fake_server.end_progress(&progress_token).await;
@@ -3226,9 +3210,7 @@ mod tests {
     #[gpui::test]
     async fn test_definition(mut cx: gpui::TestAppContext) {
         let (language_server_config, mut fake_servers) = LanguageServerConfig::fake();
-
-        let mut languages = LanguageRegistry::new();
-        languages.add(Arc::new(Language::new(
+        let language = Arc::new(Language::new(
             LanguageConfig {
                 name: "Rust".to_string(),
                 path_suffixes: vec!["rs".to_string()],
@@ -3236,30 +3218,26 @@ mod tests {
                 ..Default::default()
             },
             Some(tree_sitter_rust::language()),
-        )));
+        ));
 
-        let dir = temp_tree(json!({
-            "a.rs": "const fn a() { A }",
-            "b.rs": "const y: i32 = crate::a()",
-        }));
-        let dir_path = dir.path().to_path_buf();
+        let fs = FakeFs::new(cx.background());
+        fs.insert_tree(
+            "/dir",
+            json!({
+                "a.rs": "const fn a() { A }",
+                "b.rs": "const y: i32 = crate::a()",
+            }),
+        )
+        .await;
 
-        let http_client = FakeHttpClient::with_404_response();
-        let client = Client::new(http_client.clone());
-        let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http_client, cx));
-        let project = cx.update(|cx| {
-            Project::local(
-                client,
-                user_store,
-                Arc::new(languages),
-                Arc::new(RealFs),
-                cx,
-            )
+        let project = Project::test(fs, &mut cx);
+        project.update(&mut cx, |project, _| {
+            Arc::get_mut(&mut project.languages).unwrap().add(language);
         });
 
         let (tree, _) = project
             .update(&mut cx, |project, cx| {
-                project.find_or_create_local_worktree(dir.path().join("b.rs"), false, cx)
+                project.find_or_create_local_worktree("/dir/b.rs", false, cx)
             })
             .await
             .unwrap();
@@ -3285,12 +3263,12 @@ mod tests {
             let params = params.text_document_position_params;
             assert_eq!(
                 params.text_document.uri.to_file_path().unwrap(),
-                dir_path.join("b.rs")
+                Path::new("/dir/b.rs"),
             );
             assert_eq!(params.position, lsp::Position::new(0, 22));
 
             Some(lsp::GotoDefinitionResponse::Scalar(lsp::Location::new(
-                lsp::Url::from_file_path(dir_path.join("a.rs")).unwrap(),
+                lsp::Url::from_file_path("/dir/a.rs").unwrap(),
                 lsp::Range::new(lsp::Position::new(0, 9), lsp::Position::new(0, 10)),
             )))
         });
@@ -3311,15 +3289,12 @@ mod tests {
                     .as_local()
                     .unwrap()
                     .abs_path(cx),
-                dir.path().join("a.rs")
+                Path::new("/dir/a.rs"),
             );
             assert_eq!(definition.target_range.to_offset(target_buffer), 9..10);
             assert_eq!(
                 list_worktrees(&project, cx),
-                [
-                    (dir.path().join("b.rs"), false),
-                    (dir.path().join("a.rs"), true)
-                ]
+                [("/dir/b.rs".as_ref(), false), ("/dir/a.rs".as_ref(), true)]
             );
 
             drop(definition);
@@ -3327,18 +3302,21 @@ mod tests {
         cx.read(|cx| {
             assert_eq!(
                 list_worktrees(&project, cx),
-                [(dir.path().join("b.rs"), false)]
+                [("/dir/b.rs".as_ref(), false)]
             );
         });
 
-        fn list_worktrees(project: &ModelHandle<Project>, cx: &AppContext) -> Vec<(PathBuf, bool)> {
+        fn list_worktrees<'a>(
+            project: &'a ModelHandle<Project>,
+            cx: &'a AppContext,
+        ) -> Vec<(&'a Path, bool)> {
             project
                 .read(cx)
                 .worktrees(cx)
                 .map(|worktree| {
                     let worktree = worktree.read(cx);
                     (
-                        worktree.as_local().unwrap().abs_path().to_path_buf(),
+                        worktree.as_local().unwrap().abs_path().as_ref(),
                         worktree.is_weak(),
                     )
                 })
@@ -3348,7 +3326,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_save_file(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new(cx.background()));
+        let fs = FakeFs::new(cx.background());
         fs.insert_tree(
             "/dir",
             json!({
@@ -3386,7 +3364,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_save_in_single_file_worktree(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new(cx.background()));
+        let fs = FakeFs::new(cx.background());
         fs.insert_tree(
             "/dir",
             json!({
@@ -3576,7 +3554,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_buffer_deduping(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new(cx.background()));
+        let fs = FakeFs::new(cx.background());
         fs.insert_tree(
             "/the-dir",
             json!({
@@ -3865,7 +3843,7 @@ mod tests {
 
     #[gpui::test]
     async fn test_grouped_diagnostics(mut cx: gpui::TestAppContext) {
-        let fs = Arc::new(FakeFs::new(cx.background()));
+        let fs = FakeFs::new(cx.background());
         fs.insert_tree(
             "/the-dir",
             json!({

crates/server/src/rpc.rs 🔗

@@ -1147,7 +1147,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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
         cx_a.foreground().forbid_parking();
 
         // Connect to a server as 2 clients.
@@ -1285,7 +1285,7 @@ mod tests {
     #[gpui::test(iterations = 10)]
     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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
         cx_a.foreground().forbid_parking();
 
         // Connect to a server as 2 clients.
@@ -1386,7 +1386,7 @@ mod tests {
         mut cx_c: TestAppContext,
     ) {
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
         cx_a.foreground().forbid_parking();
 
         // Connect to a server as 3 clients.
@@ -1514,9 +1514,7 @@ mod tests {
         fs.rename("/a/file2".as_ref(), "/a/file3".as_ref(), Default::default())
             .await
             .unwrap();
-        fs.insert_file(Path::new("/a/file4"), "4".into())
-            .await
-            .unwrap();
+        fs.insert_file(Path::new("/a/file4"), "4".into()).await;
 
         worktree_a
             .condition(&cx_a, |tree, _| {
@@ -1565,7 +1563,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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -1653,7 +1651,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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -1738,7 +1736,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -1820,7 +1818,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -1895,7 +1893,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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Connect to a server as 2 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -1969,7 +1967,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
@@ -2193,7 +2191,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Set up a fake language server.
         let (mut language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
@@ -2402,7 +2400,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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Set up a fake language server.
         let (language_server_config, mut fake_language_servers) = LanguageServerConfig::fake();
@@ -2504,7 +2502,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(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
         fs.insert_tree(
             "/root-1",
             json!({
@@ -2657,7 +2655,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
         fs.insert_tree(
             "/root",
             json!({
@@ -2766,7 +2764,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let mut lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
         let mut path_openers_b = Vec::new();
         cx_b.update(|cx| editor::init(cx, &mut path_openers_b));
 
@@ -3421,7 +3419,7 @@ mod tests {
     ) {
         cx_a.foreground().forbid_parking();
         let lang_registry = Arc::new(LanguageRegistry::new());
-        let fs = Arc::new(FakeFs::new(cx_a.background()));
+        let fs = FakeFs::new(cx_a.background());
 
         // Connect to a server as 3 clients.
         let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
@@ -3605,7 +3603,7 @@ mod tests {
                 None,
             )));
 
-        let fs = Arc::new(FakeFs::new(cx.background()));
+        let fs = FakeFs::new(cx.background());
         fs.insert_tree(
             "/_collab",
             json!({

crates/workspace/src/workspace.rs 🔗

@@ -492,7 +492,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(cx.background().clone()));
+        let fs = 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 🔗

@@ -42,7 +42,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(cx.background().clone())),
+        fs: FakeFs::new(cx.background().clone()),
         path_openers: Arc::from(path_openers),
         build_window_options: &build_window_options,
         build_workspace: &build_workspace,

crates/zed/src/zed.rs 🔗

@@ -214,7 +214,7 @@ mod tests {
         });
 
         let save_task = workspace.update(&mut cx, |workspace, cx| workspace.save_active_item(cx));
-        app_state.fs.as_fake().insert_dir("/root").await.unwrap();
+        app_state.fs.as_fake().insert_dir("/root").await;
         cx.simulate_new_path_selection(|_| Some(PathBuf::from("/root/the-new-name")));
         save_task.await.unwrap();
         editor.read_with(&cx, |editor, cx| {
@@ -348,10 +348,10 @@ mod tests {
     async fn test_open_paths(mut cx: TestAppContext) {
         let app_state = cx.update(test_app_state);
         let fs = app_state.fs.as_fake();
-        fs.insert_dir("/dir1").await.unwrap();
-        fs.insert_dir("/dir2").await.unwrap();
-        fs.insert_file("/dir1/a.txt", "".into()).await.unwrap();
-        fs.insert_file("/dir2/b.txt", "".into()).await.unwrap();
+        fs.insert_dir("/dir1").await;
+        fs.insert_dir("/dir2").await;
+        fs.insert_file("/dir1/a.txt", "".into()).await;
+        fs.insert_file("/dir2/b.txt", "".into()).await;
 
         let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
         let (_, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
@@ -456,9 +456,7 @@ mod tests {
                 editor.handle_input(&editor::Input("x".into()), cx)
             })
         });
-        fs.insert_file("/root/a.txt", "changed".to_string())
-            .await
-            .unwrap();
+        fs.insert_file("/root/a.txt", "changed".to_string()).await;
         editor
             .condition(&cx, |editor, cx| editor.has_conflict(cx))
             .await;
@@ -476,7 +474,7 @@ mod tests {
     #[gpui::test]
     async fn test_open_and_save_new_file(mut cx: TestAppContext) {
         let app_state = cx.update(test_app_state);
-        app_state.fs.as_fake().insert_dir("/root").await.unwrap();
+        app_state.fs.as_fake().insert_dir("/root").await;
         let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
         let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));
         params
@@ -576,7 +574,7 @@ mod tests {
     #[gpui::test]
     async fn test_setting_language_when_saving_as_single_file_worktree(mut cx: TestAppContext) {
         let app_state = cx.update(test_app_state);
-        app_state.fs.as_fake().insert_dir("/root").await.unwrap();
+        app_state.fs.as_fake().insert_dir("/root").await;
         let params = cx.update(|cx| WorkspaceParams::local(&app_state, cx));
         let (window_id, workspace) = cx.add_window(|cx| Workspace::new(&params, cx));