Move autosave tests down into `Workspace`

Antonio Scandurra created

Change summary

crates/workspace/src/workspace.rs | 92 ++++++++++++++++++++++++++++++++
crates/zed/src/zed.rs             | 75 --------------------------
2 files changed, 90 insertions(+), 77 deletions(-)

Detailed changes

crates/workspace/src/workspace.rs 🔗

@@ -2688,7 +2688,7 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui::{ModelHandle, TestAppContext, ViewContext};
+    use gpui::{executor::Deterministic, ModelHandle, TestAppContext, ViewContext};
     use project::{FakeFs, Project, ProjectEntryId};
     use serde_json::json;
 
@@ -3026,6 +3026,86 @@ mod tests {
         });
     }
 
+    #[gpui::test]
+    async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+        deterministic.forbid_parking();
+
+        Settings::test_async(cx);
+        let fs = FakeFs::new(cx.background());
+
+        let project = Project::test(fs, [], cx).await;
+        let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
+
+        let item = cx.add_view(window_id, |_| {
+            let mut item = TestItem::new();
+            item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
+            item.is_dirty = true;
+            item
+        });
+        let item_id = item.id();
+        workspace.update(cx, |workspace, cx| {
+            workspace.add_item(Box::new(item.clone()), cx);
+        });
+
+        // Autosave on window change.
+        item.update(cx, |_, cx| {
+            cx.update_global(|settings: &mut Settings, _| {
+                settings.autosave = Autosave::OnWindowChange;
+            });
+        });
+
+        // Deactivating the window saves the file.
+        cx.simulate_window_activation(None);
+        deterministic.run_until_parked();
+        item.read_with(cx, |item, _| assert_eq!(item.save_count, 1));
+
+        // Autosave on focus change.
+        item.update(cx, |_, cx| {
+            cx.focus_self();
+            cx.update_global(|settings: &mut Settings, _| {
+                settings.autosave = Autosave::OnFocusChange;
+            });
+        });
+
+        // Blurring the item saves the file.
+        item.update(cx, |_, cx| cx.blur());
+        deterministic.run_until_parked();
+        item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
+
+        // Autosave after delay.
+        item.update(cx, |_, cx| {
+            cx.update_global(|settings: &mut Settings, _| {
+                settings.autosave = Autosave::AfterDelay { milliseconds: 500 };
+            });
+            cx.emit(TestItemEvent::Edit);
+        });
+
+        // Delay hasn't fully expired, so the file is still dirty and unsaved.
+        deterministic.advance_clock(Duration::from_millis(250));
+        item.read_with(cx, |item, _| assert_eq!(item.save_count, 2));
+
+        // After delay expires, the file is saved.
+        deterministic.advance_clock(Duration::from_millis(250));
+        item.read_with(cx, |item, _| assert_eq!(item.save_count, 3));
+
+        // Autosave on focus change, ensuring closing the tab counts as such.
+        item.update(cx, |_, cx| {
+            cx.update_global(|settings: &mut Settings, _| {
+                settings.autosave = Autosave::OnFocusChange;
+            });
+        });
+
+        workspace
+            .update(cx, |workspace, cx| {
+                let pane = workspace.active_pane().clone();
+                Pane::close_items(workspace, pane, cx, move |id| id == item_id)
+            })
+            .await
+            .unwrap();
+        assert!(!cx.has_pending_prompt(window_id));
+        item.read_with(cx, |item, _| assert_eq!(item.save_count, 4));
+    }
+
     #[derive(Clone)]
     struct TestItem {
         save_count: usize,
@@ -3038,6 +3118,10 @@ mod tests {
         is_singleton: bool,
     }
 
+    enum TestItemEvent {
+        Edit,
+    }
+
     impl TestItem {
         fn new() -> Self {
             Self {
@@ -3054,7 +3138,7 @@ mod tests {
     }
 
     impl Entity for TestItem {
-        type Event = ();
+        type Event = TestItemEvent;
     }
 
     impl View for TestItem {
@@ -3136,5 +3220,9 @@ mod tests {
         fn should_update_tab_on_event(_: &Self::Event) -> bool {
             true
         }
+
+        fn is_edit_event(event: &Self::Event) -> bool {
+            matches!(event, TestItemEvent::Edit)
+        }
     }
 }

crates/zed/src/zed.rs 🔗

@@ -396,11 +396,9 @@ mod tests {
     };
     use project::{Project, ProjectPath};
     use serde_json::json;
-    use settings::Autosave;
     use std::{
         collections::HashSet,
         path::{Path, PathBuf},
-        time::Duration,
     };
     use theme::{Theme, ThemeRegistry, DEFAULT_THEME_NAME};
     use workspace::{
@@ -979,79 +977,6 @@ mod tests {
         })
     }
 
-    #[gpui::test]
-    async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
-        let app_state = init(cx);
-        let fs = app_state.fs.clone();
-        fs.as_fake()
-            .insert_tree("/root", json!({ "a.txt": "" }))
-            .await;
-
-        let project = Project::test(fs.clone(), ["/root".as_ref()], cx).await;
-        let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
-        cx.update(|cx| {
-            workspace.update(cx, |view, cx| {
-                view.open_paths(vec![PathBuf::from("/root/a.txt")], true, cx)
-            })
-        })
-        .await;
-        let editor = cx.read(|cx| {
-            let pane = workspace.read(cx).active_pane().read(cx);
-            let item = pane.active_item().unwrap();
-            item.downcast::<Editor>().unwrap()
-        });
-
-        // Autosave on window change.
-        editor.update(cx, |editor, cx| {
-            cx.update_global(|settings: &mut Settings, _| {
-                settings.autosave = Autosave::OnWindowChange;
-            });
-            editor.insert("X", cx);
-            assert!(editor.is_dirty(cx))
-        });
-
-        // Deactivating the window saves the file.
-        cx.simulate_window_activation(None);
-        deterministic.run_until_parked();
-        assert_eq!(fs.load(Path::new("/root/a.txt")).await.unwrap(), "X");
-        editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
-
-        // Autosave on focus change.
-        editor.update(cx, |editor, cx| {
-            cx.focus_self();
-            cx.update_global(|settings: &mut Settings, _| {
-                settings.autosave = Autosave::OnFocusChange;
-            });
-            editor.insert("X", cx);
-            assert!(editor.is_dirty(cx))
-        });
-
-        // Blurring the editor saves the file.
-        editor.update(cx, |_, cx| cx.blur());
-        deterministic.run_until_parked();
-        assert_eq!(fs.load(Path::new("/root/a.txt")).await.unwrap(), "XX");
-        editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
-
-        // Autosave after delay.
-        editor.update(cx, |editor, cx| {
-            cx.update_global(|settings: &mut Settings, _| {
-                settings.autosave = Autosave::AfterDelay { milliseconds: 500 };
-            });
-            editor.insert("X", cx);
-            assert!(editor.is_dirty(cx))
-        });
-
-        // Delay hasn't fully expired, so the file is still dirty and unsaved.
-        deterministic.advance_clock(Duration::from_millis(250));
-        assert_eq!(fs.load(Path::new("/root/a.txt")).await.unwrap(), "XX");
-        editor.read_with(cx, |editor, cx| assert!(editor.is_dirty(cx)));
-
-        // After delay expires, the file is saved.
-        deterministic.advance_clock(Duration::from_millis(250));
-        assert_eq!(fs.load(Path::new("/root/a.txt")).await.unwrap(), "XXX");
-        editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
-    }
-
     #[gpui::test]
     async fn test_setting_language_when_saving_as_single_file_worktree(cx: &mut TestAppContext) {
         let app_state = init(cx);