From 048bbc9da01e8b6f61bfc6810260d6128e6bd5ad Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 12 May 2021 15:19:46 +0200 Subject: [PATCH] Test prompting when saving while there's a conflict --- gpui/src/app.rs | 17 ++++++++++ gpui/src/platform/mac/window.rs | 5 +++ gpui/src/platform/mod.rs | 1 + gpui/src/platform/test.rs | 10 +++++- zed/src/workspace.rs | 55 ++++++++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 2 deletions(-) diff --git a/gpui/src/app.rs b/gpui/src/app.rs index a8d3e13fdedd3aef18a0f165d65cb647e2770ecd..decc8096be084ceb6ff3017af8eba4c5a881fbff 100644 --- a/gpui/src/app.rs +++ b/gpui/src/app.rs @@ -343,6 +343,23 @@ impl TestAppContext { pub fn did_prompt_for_new_path(&self) -> bool { self.1.as_ref().did_prompt_for_new_path() } + + pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) { + let mut state = self.0.borrow_mut(); + let (_, window) = state + .presenters_and_platform_windows + .get_mut(&window_id) + .unwrap(); + let test_window = window + .as_any_mut() + .downcast_mut::() + .unwrap(); + let callback = test_window + .last_prompt + .take() + .expect("prompt was not called"); + (callback)(answer); + } } impl UpdateModel for TestAppContext { diff --git a/gpui/src/platform/mac/window.rs b/gpui/src/platform/mac/window.rs index 2c6df244e1d69633a34f672661794005b9514a95..b1d86acb12e7b358ce747a22f0d32e5c4ff0e270 100644 --- a/gpui/src/platform/mac/window.rs +++ b/gpui/src/platform/mac/window.rs @@ -27,6 +27,7 @@ use objc::{ use pathfinder_geometry::vector::vec2f; use smol::Timer; use std::{ + any::Any, cell::{Cell, RefCell}, convert::TryInto, ffi::c_void, @@ -263,6 +264,10 @@ impl Drop for Window { } impl platform::Window for Window { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + fn on_event(&mut self, callback: Box) { self.0.as_ref().borrow_mut().event_callback = Some(callback); } diff --git a/gpui/src/platform/mod.rs b/gpui/src/platform/mod.rs index f75351744957483f1990758a6592cfed4e1a9f9d..fd62770c12c82b98bdc9ddad17bf8da44184f96d 100644 --- a/gpui/src/platform/mod.rs +++ b/gpui/src/platform/mod.rs @@ -68,6 +68,7 @@ pub trait Dispatcher: Send + Sync { } pub trait Window: WindowContext { + fn as_any_mut(&mut self) -> &mut dyn Any; fn on_event(&mut self, callback: Box); fn on_resize(&mut self, callback: Box); fn on_close(&mut self, callback: Box); diff --git a/gpui/src/platform/test.rs b/gpui/src/platform/test.rs index 98c36b110f081d0cbb7f769cf8af16aa33a70da5..d3a0c0d3f107b8205c775e65c665fa842611a658 100644 --- a/gpui/src/platform/test.rs +++ b/gpui/src/platform/test.rs @@ -24,6 +24,7 @@ pub struct Window { event_handlers: Vec>, resize_handlers: Vec>, close_handlers: Vec>, + pub(crate) last_prompt: RefCell>>, } impl Platform { @@ -123,6 +124,7 @@ impl Window { close_handlers: Vec::new(), scale_factor: 1.0, current_scene: None, + last_prompt: RefCell::new(None), } } } @@ -152,6 +154,10 @@ impl super::WindowContext for Window { } impl super::Window for Window { + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + fn on_event(&mut self, callback: Box) { self.event_handlers.push(callback); } @@ -164,7 +170,9 @@ impl super::Window for Window { self.close_handlers.push(callback); } - fn prompt(&self, _: crate::PromptLevel, _: &str, _: &[&str], _: Box) {} + fn prompt(&self, _: crate::PromptLevel, _: &str, _: &[&str], f: Box) { + self.last_prompt.replace(Some(f)); + } } pub(crate) fn platform() -> Platform { diff --git a/zed/src/workspace.rs b/zed/src/workspace.rs index cd299daf9cf9ad001262c32127b98ee7488b038b..b737b0e99d342981c61bc6b7c1ef8fecb74c6132 100644 --- a/zed/src/workspace.rs +++ b/zed/src/workspace.rs @@ -761,7 +761,7 @@ mod tests { use crate::{editor::BufferView, settings, test::temp_tree}; use gpui::App; use serde_json::json; - use std::collections::HashSet; + use std::{collections::HashSet, fs}; use tempdir::TempDir; #[test] @@ -1101,6 +1101,59 @@ mod tests { }); } + #[test] + fn test_save_conflicting_item() { + App::test_async((), |mut app| async move { + let dir = temp_tree(json!({ + "a.txt": "", + })); + + let settings = settings::channel(&app.font_cache()).unwrap().1; + let (window_id, workspace) = app.add_window(|ctx| { + let mut workspace = Workspace::new(0, settings, ctx); + workspace.add_worktree(dir.path(), ctx); + workspace + }); + let tree = app.read(|ctx| { + let mut trees = workspace.read(ctx).worktrees().iter(); + trees.next().unwrap().clone() + }); + tree.flush_fs_events(&app).await; + + // Open a file within an existing worktree. + app.update(|ctx| { + workspace.update(ctx, |view, ctx| { + view.open_paths(&[dir.path().join("a.txt")], ctx) + }) + }) + .await; + let editor = app.read(|ctx| { + let pane = workspace.read(ctx).active_pane().read(ctx); + let item = pane.active_item().unwrap(); + item.to_any().downcast::().unwrap() + }); + + app.update(|ctx| { + editor.update(ctx, |editor, ctx| editor.insert(&"x".to_string(), ctx)) + }); + fs::write(dir.path().join("a.txt"), "changed").unwrap(); + tree.flush_fs_events(&app).await; + app.read(|ctx| { + assert!(editor.is_dirty(ctx)); + assert!(editor.has_conflict(ctx)); + }); + + app.update(|ctx| workspace.update(ctx, |w, ctx| w.save_active_item(&(), ctx))); + app.simulate_prompt_answer(window_id, 0); + tree.update(&mut app, |tree, ctx| tree.next_scan_complete(ctx)) + .await; + app.read(|ctx| { + assert!(!editor.is_dirty(ctx)); + assert!(!editor.has_conflict(ctx)); + }); + }); + } + #[test] fn test_pane_actions() { App::test_async((), |mut app| async move {