diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 9d4c7b6252..65a2504ab8 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -1,12 +1,16 @@ mod async_context; mod entity_map; mod model_context; +#[cfg(any(test, feature = "test-support"))] +mod test_context; pub use async_context::*; pub use entity_map::*; pub use model_context::*; use refineable::Refineable; use smallvec::SmallVec; +#[cfg(any(test, feature = "test-support"))] +pub use test_context::*; use crate::{ current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AppMetadata, AssetSource, diff --git a/crates/gpui2/src/app/test_context.rs b/crates/gpui2/src/app/test_context.rs new file mode 100644 index 0000000000..d31efca2c0 --- /dev/null +++ b/crates/gpui2/src/app/test_context.rs @@ -0,0 +1,137 @@ +use crate::{ + AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, Handle, MainThread, + ModelContext, Result, Task, WindowContext, +}; +use parking_lot::Mutex; +use std::{any::Any, future::Future, sync::Arc}; + +#[derive(Clone)] +pub struct TestAppContext { + pub(crate) app: Arc>, + pub(crate) executor: Executor, +} + +impl Context for TestAppContext { + type EntityContext<'a, 'w, T> = ModelContext<'a, T>; + type Result = T; + + fn entity( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T, + ) -> Self::Result> + where + T: Any + Send + Sync, + { + let mut lock = self.app.lock(); + lock.entity(build_entity) + } + + fn update_entity( + &mut self, + handle: &Handle, + update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, + ) -> Self::Result { + let mut lock = self.app.lock(); + lock.update_entity(handle, update) + } +} + +impl TestAppContext { + pub fn refresh(&mut self) -> Result<()> { + let mut lock = self.app.lock(); + lock.refresh(); + Ok(()) + } + + pub fn executor(&self) -> &Executor { + &self.executor + } + + pub fn update(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result { + let mut lock = self.app.lock(); + Ok(f(&mut *lock)) + } + + pub fn read_window( + &self, + handle: AnyWindowHandle, + update: impl FnOnce(&WindowContext) -> R, + ) -> Result { + let mut app_context = self.app.lock(); + app_context.read_window(handle.id, update) + } + + pub fn update_window( + &self, + handle: AnyWindowHandle, + update: impl FnOnce(&mut WindowContext) -> R, + ) -> Result { + let mut app = self.app.lock(); + app.update_window(handle.id, update) + } + + pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task + where + Fut: Future + Send + 'static, + R: Send + 'static, + { + let cx = self.to_async(); + self.executor.spawn(async move { f(cx).await }) + } + + pub fn spawn_on_main( + &self, + f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static, + ) -> Task + where + Fut: Future + 'static, + R: Send + 'static, + { + let cx = self.to_async(); + self.executor.spawn_on_main(|| f(cx)) + } + + pub fn run_on_main( + &self, + f: impl FnOnce(&mut MainThread) -> R + Send + 'static, + ) -> Result> + where + R: Send + 'static, + { + let mut app_context = self.app.lock(); + Ok(app_context.run_on_main(f)) + } + + pub fn has_global(&self) -> Result { + let lock = self.app.lock(); + Ok(lock.has_global::()) + } + + pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result { + let lock = self.app.lock(); + Ok(read(lock.global(), &lock)) + } + + pub fn try_read_global( + &self, + read: impl FnOnce(&G, &AppContext) -> R, + ) -> Option { + let lock = self.app.lock(); + Some(read(lock.try_global()?, &lock)) + } + + pub fn update_global( + &mut self, + update: impl FnOnce(&mut G, &mut AppContext) -> R, + ) -> Result { + let mut lock = self.app.lock(); + Ok(lock.update_global(update)) + } + + fn to_async(&self) -> AsyncAppContext { + AsyncAppContext { + app: Arc::downgrade(&self.app), + executor: self.executor.clone(), + } + } +}