Add unit test for autosave

Antonio Scandurra created

Change summary

crates/editor/src/editor.rs | 78 +++++++++++++++++++++++++++++++++++++--
1 file changed, 74 insertions(+), 4 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -5634,7 +5634,10 @@ impl Editor {
 
     fn autosave(&mut self, cx: &mut ViewContext<Self>) -> Task<Result<()>> {
         if let Some(project) = self.project.clone() {
-            if self.buffer.read(cx).is_dirty(cx) && !self.buffer.read(cx).has_conflict(cx) {
+            if self.buffer.read(cx).is_dirty(cx)
+                && !self.buffer.read(cx).has_conflict(cx)
+                && workspace::Item::can_save(self, cx)
+            {
                 return workspace::Item::save(self, project, cx);
             }
         }
@@ -6276,22 +6279,23 @@ mod tests {
     use super::*;
     use futures::StreamExt;
     use gpui::{
+        executor::Deterministic,
         geometry::rect::RectF,
         platform::{WindowBounds, WindowOptions},
     };
     use indoc::indoc;
     use language::{FakeLspAdapter, LanguageConfig};
     use lsp::FakeLanguageServer;
-    use project::FakeFs;
+    use project::{FakeFs, Fs};
     use settings::LanguageSettings;
-    use std::{cell::RefCell, rc::Rc, time::Instant};
+    use std::{cell::RefCell, path::Path, rc::Rc, time::Instant};
     use text::Point;
     use unindent::Unindent;
     use util::{
         assert_set_eq,
         test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
     };
-    use workspace::{FollowableItem, ItemHandle};
+    use workspace::{FollowableItem, Item, ItemHandle};
 
     #[gpui::test]
     fn test_edit_events(cx: &mut MutableAppContext) {
@@ -9555,6 +9559,72 @@ mod tests {
         save.await.unwrap();
     }
 
+    #[gpui::test]
+    async fn test_autosave(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
+        deterministic.forbid_parking();
+
+        let fs = FakeFs::new(cx.background().clone());
+        fs.insert_file("/file.rs", Default::default()).await;
+
+        let project = Project::test(fs.clone(), ["/file.rs".as_ref()], cx).await;
+        let buffer = project
+            .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
+            .await
+            .unwrap();
+
+        let (_, editor) = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+
+        // 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("/file.rs")).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("/file.rs")).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("/file.rs")).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("/file.rs")).await.unwrap(), "XXX");
+        editor.read_with(cx, |editor, cx| assert!(!editor.is_dirty(cx)));
+    }
+
     #[gpui::test]
     async fn test_completion(cx: &mut gpui::TestAppContext) {
         let mut language = Language::new(