Add test to verify closing window via the mouse

Antonio Scandurra created

Change summary

crates/gpui/src/app.rs           | 66 ++++++++++++++++++---------------
crates/gpui/src/platform/test.rs |  2 
crates/zed/src/zed.rs            | 34 +++++++++++++++++
3 files changed, 69 insertions(+), 33 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -22,7 +22,7 @@ use smallvec::SmallVec;
 use smol::prelude::*;
 use std::{
     any::{type_name, Any, TypeId},
-    cell::RefCell,
+    cell::{RefCell, RefMut},
     collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque},
     fmt::{self, Debug},
     hash::{Hash, Hasher},
@@ -521,16 +521,8 @@ impl TestAppContext {
     pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) {
         use postage::prelude::Sink as _;
 
-        let mut state = self.cx.borrow_mut();
-        let (_, window) = state
-            .presenters_and_platform_windows
-            .get_mut(&window_id)
-            .unwrap();
-        let test_window = window
-            .as_any_mut()
-            .downcast_mut::<platform::test::Window>()
-            .unwrap();
-        let mut done_tx = test_window
+        let mut done_tx = self
+            .window_mut(window_id)
             .pending_prompts
             .borrow_mut()
             .pop_front()
@@ -539,30 +531,28 @@ impl TestAppContext {
     }
 
     pub fn has_pending_prompt(&self, window_id: usize) -> bool {
-        let mut state = self.cx.borrow_mut();
-        let (_, window) = state
-            .presenters_and_platform_windows
-            .get_mut(&window_id)
-            .unwrap();
-        let test_window = window
-            .as_any_mut()
-            .downcast_mut::<platform::test::Window>()
-            .unwrap();
-        let prompts = test_window.pending_prompts.borrow_mut();
+        let window = self.window_mut(window_id);
+        let prompts = window.pending_prompts.borrow_mut();
         !prompts.is_empty()
     }
 
     pub fn current_window_title(&self, window_id: usize) -> Option<String> {
-        let mut state = self.cx.borrow_mut();
-        let (_, window) = state
-            .presenters_and_platform_windows
-            .get_mut(&window_id)
-            .unwrap();
-        let test_window = window
-            .as_any_mut()
-            .downcast_mut::<platform::test::Window>()
-            .unwrap();
-        test_window.title.clone()
+        self.window_mut(window_id).title.clone()
+    }
+
+    pub fn simulate_window_close(&self, window_id: usize) -> bool {
+        let handler = self.window_mut(window_id).should_close_handler.take();
+        if let Some(mut handler) = handler {
+            let should_close = handler();
+            self.window_mut(window_id).should_close_handler = Some(handler);
+            should_close
+        } else {
+            false
+        }
+    }
+
+    pub fn is_window_edited(&self, window_id: usize) -> bool {
+        self.window_mut(window_id).edited
     }
 
     pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
@@ -576,6 +566,20 @@ impl TestAppContext {
             .lock()
             .assert_dropped(handle.id())
     }
+
+    fn window_mut(&self, window_id: usize) -> RefMut<platform::test::Window> {
+        RefMut::map(self.cx.borrow_mut(), |state| {
+            let (_, window) = state
+                .presenters_and_platform_windows
+                .get_mut(&window_id)
+                .unwrap();
+            let test_window = window
+                .as_any_mut()
+                .downcast_mut::<platform::test::Window>()
+                .unwrap();
+            test_window
+        })
+    }
 }
 
 impl AsyncAppContext {

crates/gpui/src/platform/test.rs 🔗

@@ -37,7 +37,7 @@ pub struct Window {
     event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
     resize_handlers: Vec<Box<dyn FnMut()>>,
     close_handlers: Vec<Box<dyn FnOnce()>>,
-    should_close_handler: Option<Box<dyn FnMut() -> bool>>,
+    pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
     pub(crate) title: Option<String>,
     pub(crate) edited: bool,
     pub(crate) pending_prompts: RefCell<VecDeque<oneshot::Sender<usize>>>,

crates/zed/src/zed.rs 🔗

@@ -371,7 +371,9 @@ mod tests {
     use super::*;
     use assets::Assets;
     use editor::{Autoscroll, DisplayPoint, Editor};
-    use gpui::{AssetSource, MutableAppContext, TestAppContext, ViewHandle};
+    use gpui::{
+        executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle,
+    };
     use project::ProjectPath;
     use serde_json::json;
     use std::{
@@ -439,6 +441,36 @@ mod tests {
         assert_eq!(cx.window_ids().len(), 2);
     }
 
+    #[gpui::test]
+    async fn test_closing_window_via_mouse(executor: Arc<Deterministic>, cx: &mut TestAppContext) {
+        let app_state = init(cx);
+        app_state
+            .fs
+            .as_fake()
+            .insert_tree("/root", json!({"a": "hey"}))
+            .await;
+
+        cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx))
+            .await;
+        assert_eq!(cx.window_ids().len(), 1);
+
+        let workspace = cx.root_view::<Workspace>(cx.window_ids()[0]).unwrap();
+        let editor = workspace.read_with(cx, |workspace, cx| {
+            workspace
+                .active_item(cx)
+                .unwrap()
+                .downcast::<Editor>()
+                .unwrap()
+        });
+        editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
+
+        assert!(!cx.simulate_window_close(workspace.window_id()));
+        executor.run_until_parked();
+        cx.simulate_prompt_answer(workspace.window_id(), 1);
+        executor.run_until_parked();
+        assert_eq!(cx.window_ids().len(), 0);
+    }
+
     #[gpui::test]
     async fn test_new_empty_workspace(cx: &mut TestAppContext) {
         let app_state = init(cx);