Move window-related methods from TestAppContext to AnyWindowHandle

Nathan Sobo created

Change summary

crates/collab/src/tests/integration_tests.rs |   4 
crates/editor/src/editor_tests.rs            |   9 +
crates/gpui/src/app/test_app_context.rs      | 132 ++++++++++-----------
crates/project_panel/src/project_panel.rs    |  26 ++--
crates/workspace/src/workspace.rs            |  46 ++----
crates/zed/src/zed.rs                        |  29 ++--
6 files changed, 118 insertions(+), 128 deletions(-)

Detailed changes

crates/collab/src/tests/integration_tests.rs πŸ”—

@@ -1510,7 +1510,7 @@ async fn test_host_disconnect(
         .unwrap();
     assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
     editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
-    assert!(cx_b.is_window_edited(workspace_b.window_id()));
+    assert!(window_b.is_edited(cx_b));
 
     // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
     server.forbid_connections();
@@ -1525,7 +1525,7 @@ async fn test_host_disconnect(
     window_b.read_with(cx_b, |cx| {
         assert_eq!(cx.focused_view_id(), None);
     });
-    assert!(!cx_b.is_window_edited(workspace_b.window_id()));
+    assert!(!window_b.is_edited(cx_b));
 
     // Ensure client B is not prompted to save edits when closing window after disconnecting.
     let can_close = workspace_b

crates/editor/src/editor_tests.rs πŸ”—

@@ -1290,7 +1290,8 @@ async fn test_move_start_of_paragraph_end_of_paragraph(cx: &mut gpui::TestAppCon
     let mut cx = EditorTestContext::new(cx).await;
 
     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-    cx.simulate_window_resize(cx.window.id(), vec2f(100., 4. * line_height));
+    let window = cx.window;
+    window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
 
     cx.set_state(
         &r#"Λ‡one
@@ -1401,7 +1402,8 @@ async fn test_scroll_page_up_page_down(cx: &mut gpui::TestAppContext) {
     init_test(cx, |_| {});
     let mut cx = EditorTestContext::new(cx).await;
     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-    cx.simulate_window_resize(cx.window.id(), vec2f(1000., 4. * line_height + 0.5));
+    let window = cx.window;
+    window.simulate_resize(vec2f(1000., 4. * line_height + 0.5), &mut cx);
 
     cx.set_state(
         &r#"Λ‡one
@@ -1439,7 +1441,8 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
     let mut cx = EditorTestContext::new(cx).await;
 
     let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
-    cx.simulate_window_resize(cx.window.id(), vec2f(100., 4. * line_height));
+    let window = cx.window;
+    window.simulate_resize(vec2f(100., 4. * line_height), &mut cx);
 
     cx.set_state(
         &r#"

crates/gpui/src/app/test_app_context.rs πŸ”—

@@ -202,10 +202,6 @@ impl TestAppContext {
         self.cx.borrow().windows().collect()
     }
 
-    // pub fn window_ids(&self) -> Vec<usize> {
-    //     self.cx.borrow().windows.keys().copied().collect()
-    // }
-
     pub fn remove_all_windows(&mut self) {
         self.update(|cx| cx.windows.clear());
     }
@@ -273,57 +269,6 @@ impl TestAppContext {
         self.foreground_platform.as_ref().did_prompt_for_new_path()
     }
 
-    pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) {
-        use postage::prelude::Sink as _;
-
-        let mut done_tx = self
-            .platform_window_mut(window_id)
-            .pending_prompts
-            .borrow_mut()
-            .pop_front()
-            .expect("prompt was not called");
-        done_tx.try_send(answer).ok();
-    }
-
-    pub fn has_pending_prompt(&self, window_id: usize) -> bool {
-        let window = self.platform_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> {
-        self.platform_window_mut(window_id).title.clone()
-    }
-
-    pub fn simulate_window_close(&self, window_id: usize) -> bool {
-        let handler = self
-            .platform_window_mut(window_id)
-            .should_close_handler
-            .take();
-        if let Some(mut handler) = handler {
-            let should_close = handler();
-            self.platform_window_mut(window_id).should_close_handler = Some(handler);
-            should_close
-        } else {
-            false
-        }
-    }
-
-    pub fn simulate_window_resize(&self, window_id: usize, size: Vector2F) {
-        let mut window = self.platform_window_mut(window_id);
-        window.size = size;
-        let mut handlers = mem::take(&mut window.resize_handlers);
-        drop(window);
-        for handler in &mut handlers {
-            handler();
-        }
-        self.platform_window_mut(window_id).resize_handlers = handlers;
-    }
-
-    pub fn is_window_edited(&self, window_id: usize) -> bool {
-        self.platform_window_mut(window_id).edited
-    }
-
     pub fn leak_detector(&self) -> Arc<Mutex<LeakDetector>> {
         self.cx.borrow().leak_detector()
     }
@@ -344,18 +289,6 @@ impl TestAppContext {
         self.assert_dropped(weak);
     }
 
-    fn platform_window_mut(&self, window_id: usize) -> std::cell::RefMut<platform::test::Window> {
-        std::cell::RefMut::map(self.cx.borrow_mut(), |state| {
-            let window = state.windows.get_mut(&window_id).unwrap();
-            let test_window = window
-                .platform_window
-                .as_any_mut()
-                .downcast_mut::<platform::test::Window>()
-                .unwrap();
-            test_window
-        })
-    }
-
     pub fn set_condition_duration(&mut self, duration: Option<Duration>) {
         self.condition_duration = duration;
     }
@@ -545,6 +478,71 @@ impl<T: Entity> ModelHandle<T> {
     }
 }
 
+impl AnyWindowHandle {
+    pub fn has_pending_prompt(&self, cx: &mut TestAppContext) -> bool {
+        let window = self.platform_window_mut(cx);
+        let prompts = window.pending_prompts.borrow_mut();
+        !prompts.is_empty()
+    }
+
+    pub fn current_title(&self, cx: &mut TestAppContext) -> Option<String> {
+        self.platform_window_mut(cx).title.clone()
+    }
+
+    pub fn simulate_close(&self, cx: &mut TestAppContext) -> bool {
+        let handler = self.platform_window_mut(cx).should_close_handler.take();
+        if let Some(mut handler) = handler {
+            let should_close = handler();
+            self.platform_window_mut(cx).should_close_handler = Some(handler);
+            should_close
+        } else {
+            false
+        }
+    }
+
+    pub fn simulate_resize(&self, size: Vector2F, cx: &mut TestAppContext) {
+        let mut window = self.platform_window_mut(cx);
+        window.size = size;
+        let mut handlers = mem::take(&mut window.resize_handlers);
+        drop(window);
+        for handler in &mut handlers {
+            handler();
+        }
+        self.platform_window_mut(cx).resize_handlers = handlers;
+    }
+
+    pub fn is_edited(&self, cx: &mut TestAppContext) -> bool {
+        self.platform_window_mut(cx).edited
+    }
+
+    pub fn simulate_prompt_answer(&self, answer: usize, cx: &mut TestAppContext) {
+        use postage::prelude::Sink as _;
+
+        let mut done_tx = self
+            .platform_window_mut(cx)
+            .pending_prompts
+            .borrow_mut()
+            .pop_front()
+            .expect("prompt was not called");
+        done_tx.try_send(answer).ok();
+    }
+
+    fn platform_window_mut<'a>(
+        &self,
+        cx: &'a mut TestAppContext,
+    ) -> std::cell::RefMut<'a, platform::test::Window> {
+        std::cell::RefMut::map(cx.cx.borrow_mut(), |state| {
+            let window = state.windows.get_mut(&self.window_id).unwrap();
+            let test_window = window
+                .platform_window
+                .as_any_mut()
+                .downcast_mut::<platform::test::Window>()
+                .unwrap();
+            test_window
+        })
+    }
+}
+
 impl<T: View> ViewHandle<T> {
     pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
         use postage::prelude::{Sink as _, Stream as _};

crates/project_panel/src/project_panel.rs πŸ”—

@@ -1726,7 +1726,7 @@ impl ClipboardEntry {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use gpui::{TestAppContext, ViewHandle};
+    use gpui::{AnyWindowHandle, TestAppContext, ViewHandle};
     use pretty_assertions::assert_eq;
     use project::FakeFs;
     use serde_json::json;
@@ -2421,7 +2421,7 @@ mod tests {
         );
         ensure_single_file_is_opened(window_id, &workspace, "test/first.rs", cx);
 
-        submit_deletion(window_id, &panel, cx);
+        submit_deletion(window.into(), &panel, cx);
         assert_eq!(
             visible_entries_as_strings(&panel, 0..10, cx),
             &[
@@ -2432,7 +2432,7 @@ mod tests {
             ],
             "Project panel should have no deleted file, no other file is selected in it"
         );
-        ensure_no_open_items_and_panes(window_id, &workspace, cx);
+        ensure_no_open_items_and_panes(window.into(), &workspace, cx);
 
         select_path(&panel, "src/test/second.rs", cx);
         panel.update(cx, |panel, cx| panel.open_file(&Open, cx));
@@ -2464,13 +2464,13 @@ mod tests {
                 .expect("Open item should be an editor");
             open_editor.update(cx, |editor, cx| editor.set_text("Another text!", cx));
         });
-        submit_deletion(window_id, &panel, cx);
+        submit_deletion(window.into(), &panel, cx);
         assert_eq!(
             visible_entries_as_strings(&panel, 0..10, cx),
             &["v src", "    v test", "          third.rs"],
             "Project panel should have no deleted file, with one last file remaining"
         );
-        ensure_no_open_items_and_panes(window_id, &workspace, cx);
+        ensure_no_open_items_and_panes(window.into(), &workspace, cx);
     }
 
     #[gpui::test]
@@ -2910,12 +2910,12 @@ mod tests {
     }
 
     fn submit_deletion(
-        window_id: usize,
+        window: AnyWindowHandle,
         panel: &ViewHandle<ProjectPanel>,
         cx: &mut TestAppContext,
     ) {
         assert!(
-            !cx.has_pending_prompt(window_id),
+            !window.has_pending_prompt(cx),
             "Should have no prompts before the deletion"
         );
         panel.update(cx, |panel, cx| {
@@ -2925,27 +2925,27 @@ mod tests {
                 .detach_and_log_err(cx);
         });
         assert!(
-            cx.has_pending_prompt(window_id),
+            window.has_pending_prompt(cx),
             "Should have a prompt after the deletion"
         );
-        cx.simulate_prompt_answer(window_id, 0);
+        window.simulate_prompt_answer(0, cx);
         assert!(
-            !cx.has_pending_prompt(window_id),
+            !window.has_pending_prompt(cx),
             "Should have no prompts after prompt was replied to"
         );
         cx.foreground().run_until_parked();
     }
 
     fn ensure_no_open_items_and_panes(
-        window_id: usize,
+        window: AnyWindowHandle,
         workspace: &ViewHandle<Workspace>,
         cx: &mut TestAppContext,
     ) {
         assert!(
-            !cx.has_pending_prompt(window_id),
+            !window.has_pending_prompt(cx),
             "Should have no prompts after deletion operation closes the file"
         );
-        cx.read_window(window_id, |cx| {
+        window.read_with(cx, |cx| {
             let open_project_paths = workspace
                 .read(cx)
                 .panes()

crates/workspace/src/workspace.rs πŸ”—

@@ -4197,17 +4197,11 @@ mod tests {
                     .map(|e| e.id)
             );
         });
-        assert_eq!(
-            cx.current_window_title(window.id()).as_deref(),
-            Some("one.txt β€” root1")
-        );
+        assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
 
         // Add a second item to a non-empty pane
         workspace.update(cx, |workspace, cx| workspace.add_item(Box::new(item2), cx));
-        assert_eq!(
-            cx.current_window_title(window.id()).as_deref(),
-            Some("two.txt β€” root1")
-        );
+        assert_eq!(window.current_title(cx).as_deref(), Some("two.txt β€” root1"));
         project.read_with(cx, |project, cx| {
             assert_eq!(
                 project.active_entry(),
@@ -4223,10 +4217,7 @@ mod tests {
         })
         .await
         .unwrap();
-        assert_eq!(
-            cx.current_window_title(window.id()).as_deref(),
-            Some("one.txt β€” root1")
-        );
+        assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root1"));
         project.read_with(cx, |project, cx| {
             assert_eq!(
                 project.active_entry(),
@@ -4244,16 +4235,13 @@ mod tests {
             .await
             .unwrap();
         assert_eq!(
-            cx.current_window_title(window.id()).as_deref(),
+            window.current_title(cx).as_deref(),
             Some("one.txt β€” root1, root2")
         );
 
         // Remove a project folder
         project.update(cx, |project, cx| project.remove_worktree(worktree_id, cx));
-        assert_eq!(
-            cx.current_window_title(window.id()).as_deref(),
-            Some("one.txt β€” root2")
-        );
+        assert_eq!(window.current_title(cx).as_deref(), Some("one.txt β€” root2"));
     }
 
     #[gpui::test]
@@ -4287,9 +4275,9 @@ mod tests {
         });
         let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
         cx.foreground().run_until_parked();
-        cx.simulate_prompt_answer(window.id(), 2 /* cancel */);
+        window.simulate_prompt_answer(2, cx); // cancel
         cx.foreground().run_until_parked();
-        assert!(!cx.has_pending_prompt(window.id()));
+        assert!(!window.has_pending_prompt(cx));
         assert!(!task.await.unwrap());
     }
 
@@ -4348,10 +4336,10 @@ mod tests {
             assert_eq!(pane.items_len(), 4);
             assert_eq!(pane.active_item().unwrap().id(), item1.id());
         });
-        assert!(cx.has_pending_prompt(window.id()));
+        assert!(window.has_pending_prompt(cx));
 
         // Confirm saving item 1.
-        cx.simulate_prompt_answer(window.id(), 0);
+        window.simulate_prompt_answer(0, cx);
         cx.foreground().run_until_parked();
 
         // Item 1 is saved. There's a prompt to save item 3.
@@ -4362,10 +4350,10 @@ mod tests {
             assert_eq!(pane.items_len(), 3);
             assert_eq!(pane.active_item().unwrap().id(), item3.id());
         });
-        assert!(cx.has_pending_prompt(window.id()));
+        assert!(window.has_pending_prompt(cx));
 
         // Cancel saving item 3.
-        cx.simulate_prompt_answer(window.id(), 1);
+        window.simulate_prompt_answer(1, cx);
         cx.foreground().run_until_parked();
 
         // Item 3 is reloaded. There's a prompt to save item 4.
@@ -4376,10 +4364,10 @@ mod tests {
             assert_eq!(pane.items_len(), 2);
             assert_eq!(pane.active_item().unwrap().id(), item4.id());
         });
-        assert!(cx.has_pending_prompt(window.id()));
+        assert!(window.has_pending_prompt(cx));
 
         // Confirm saving item 4.
-        cx.simulate_prompt_answer(window.id(), 0);
+        window.simulate_prompt_answer(0, cx);
         cx.foreground().run_until_parked();
 
         // There's a prompt for a path for item 4.
@@ -4482,7 +4470,7 @@ mod tests {
                 &[ProjectEntryId::from_proto(0)]
             );
         });
-        cx.simulate_prompt_answer(window.id(), 0);
+        window.simulate_prompt_answer(0, cx);
 
         cx.foreground().run_until_parked();
         left_pane.read_with(cx, |pane, cx| {
@@ -4491,7 +4479,7 @@ mod tests {
                 &[ProjectEntryId::from_proto(2)]
             );
         });
-        cx.simulate_prompt_answer(window.id(), 0);
+        window.simulate_prompt_answer(0, cx);
 
         cx.foreground().run_until_parked();
         close.await.unwrap();
@@ -4593,7 +4581,7 @@ mod tests {
         pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id))
             .await
             .unwrap();
-        assert!(!cx.has_pending_prompt(window.id()));
+        assert!(!window.has_pending_prompt(cx));
         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
 
         // Add the item again, ensuring autosave is prevented if the underlying file has been deleted.
@@ -4614,7 +4602,7 @@ mod tests {
         let _close_items =
             pane.update(cx, |pane, cx| pane.close_items(cx, move |id| id == item_id));
         deterministic.run_until_parked();
-        assert!(cx.has_pending_prompt(window.id()));
+        assert!(window.has_pending_prompt(cx));
         item.read_with(cx, |item, _| assert_eq!(item.save_count, 5));
     }
 

crates/zed/src/zed.rs πŸ”—

@@ -841,7 +841,8 @@ mod tests {
         assert_eq!(cx.windows().len(), 1);
 
         // When opening the workspace, the window is not in a edited state.
-        let workspace = cx.windows()[0].downcast::<Workspace>().unwrap().root(cx);
+        let window = cx.windows()[0].downcast::<Workspace>().unwrap();
+        let workspace = window.root(cx);
         let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
         let editor = workspace.read_with(cx, |workspace, cx| {
             workspace
@@ -850,19 +851,19 @@ mod tests {
                 .downcast::<Editor>()
                 .unwrap()
         });
-        assert!(!cx.is_window_edited(workspace.window_id()));
+        assert!(!window.is_edited(cx));
 
         // Editing a buffer marks the window as edited.
         editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
-        assert!(cx.is_window_edited(workspace.window_id()));
+        assert!(window.is_edited(cx));
 
         // Undoing the edit restores the window's edited state.
         editor.update(cx, |editor, cx| editor.undo(&Default::default(), cx));
-        assert!(!cx.is_window_edited(workspace.window_id()));
+        assert!(!window.is_edited(cx));
 
         // Redoing the edit marks the window as edited again.
         editor.update(cx, |editor, cx| editor.redo(&Default::default(), cx));
-        assert!(cx.is_window_edited(workspace.window_id()));
+        assert!(window.is_edited(cx));
 
         // Closing the item restores the window's edited state.
         let close = pane.update(cx, |pane, cx| {
@@ -870,9 +871,10 @@ mod tests {
             pane.close_active_item(&Default::default(), cx).unwrap()
         });
         executor.run_until_parked();
-        cx.simulate_prompt_answer(workspace.window_id(), 1);
+
+        window.simulate_prompt_answer(1, cx);
         close.await.unwrap();
-        assert!(!cx.is_window_edited(workspace.window_id()));
+        assert!(!window.is_edited(cx));
 
         // Opening the buffer again doesn't impact the window's edited state.
         cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx))
@@ -885,20 +887,20 @@ mod tests {
                 .downcast::<Editor>()
                 .unwrap()
         });
-        assert!(!cx.is_window_edited(workspace.window_id()));
+        assert!(!window.is_edited(cx));
 
         // Editing the buffer marks the window as edited.
         editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
-        assert!(cx.is_window_edited(workspace.window_id()));
+        assert!(window.is_edited(cx));
 
         // Ensure closing the window via the mouse gets preempted due to the
         // buffer having unsaved changes.
-        assert!(!cx.simulate_window_close(workspace.window_id()));
+        assert!(!window.simulate_close(cx));
         executor.run_until_parked();
         assert_eq!(cx.windows().len(), 1);
 
         // The window is successfully closed after the user dismisses the prompt.
-        cx.simulate_prompt_answer(workspace.window_id(), 1);
+        window.simulate_prompt_answer(1, cx);
         executor.run_until_parked();
         assert_eq!(cx.windows().len(), 0);
     }
@@ -1273,7 +1275,6 @@ mod tests {
         let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
         let window = cx.add_window(|cx| Workspace::test_new(project, cx));
         let workspace = window.root(cx);
-        let window_id = window.id();
 
         // Open a file within an existing worktree.
         workspace
@@ -1299,7 +1300,7 @@ mod tests {
         cx.read(|cx| assert!(editor.is_dirty(cx)));
 
         let save_task = workspace.update(cx, |workspace, cx| workspace.save_active_item(false, cx));
-        cx.simulate_prompt_answer(window_id, 0);
+        window.simulate_prompt_answer(0, cx);
         save_task.await.unwrap();
         editor.read_with(cx, |editor, cx| {
             assert!(!editor.is_dirty(cx));
@@ -1506,7 +1507,7 @@ mod tests {
 
         cx.dispatch_action(window_id, workspace::CloseActiveItem);
         cx.foreground().run_until_parked();
-        cx.simulate_prompt_answer(window_id, 1);
+        window.simulate_prompt_answer(1, cx);
         cx.foreground().run_until_parked();
 
         workspace.read_with(cx, |workspace, cx| {