Implement `TestAppContext::new`

Antonio Scandurra , Max , Conrad , and Kyle created

Co-Authored-By: Max <max@zed.dev>
Co-Authored-By: Conrad <conrad@zed.dev>
Co-Authored-By: Kyle <kyle@zed.dev>

Change summary

crates/gpui2/src/app.rs                      | 127 ++++++++++-----------
crates/gpui2/src/app/test_context.rs         |  43 ++++--
crates/gpui2/src/platform/test/platform.rs   |  10 -
crates/gpui2/src/text_system/line_wrapper.rs |   8 +
4 files changed, 97 insertions(+), 91 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -39,72 +39,11 @@ pub struct App(Arc<Mutex<AppContext>>);
 
 impl App {
     pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
-        let http_client = http::client();
-        Self::new(current_platform(), asset_source, http_client)
-    }
-
-    #[cfg(any(test, feature = "test-support"))]
-    pub fn test(seed: u64) -> Self {
-        let platform = Arc::new(crate::TestPlatform::new(seed));
-        let asset_source = Arc::new(());
-        let http_client = util::http::FakeHttpClient::with_404_response();
-        Self::new(platform, asset_source, http_client)
-    }
-
-    fn new(
-        platform: Arc<dyn Platform>,
-        asset_source: Arc<dyn AssetSource>,
-        http_client: Arc<dyn HttpClient>,
-    ) -> Self {
-        let executor = platform.executor();
-        assert!(
-            executor.is_main_thread(),
-            "must construct App on main thread"
-        );
-
-        let text_system = Arc::new(TextSystem::new(platform.text_system()));
-        let mut entities = EntityMap::new();
-        let unit_entity = entities.insert(entities.reserve(), ());
-        let app_metadata = AppMetadata {
-            os_name: platform.os_name(),
-            os_version: platform.os_version().ok(),
-            app_version: platform.app_version().ok(),
-        };
-
-        Self(Arc::new_cyclic(|this| {
-            Mutex::new(AppContext {
-                this: this.clone(),
-                text_system,
-                platform: MainThreadOnly::new(platform, executor.clone()),
-                app_metadata,
-                flushing_effects: false,
-                pending_updates: 0,
-                next_frame_callbacks: Default::default(),
-                executor,
-                svg_renderer: SvgRenderer::new(asset_source.clone()),
-                asset_source,
-                image_cache: ImageCache::new(http_client),
-                text_style_stack: Vec::new(),
-                globals_by_type: HashMap::default(),
-                unit_entity,
-                entities,
-                windows: SlotMap::with_key(),
-                keymap: Arc::new(RwLock::new(Keymap::default())),
-                global_action_listeners: HashMap::default(),
-                action_builders: HashMap::default(),
-                pending_effects: VecDeque::new(),
-                pending_notifications: HashSet::default(),
-                pending_global_notifications: HashSet::default(),
-                observers: SubscriberSet::new(),
-                event_listeners: SubscriberSet::new(),
-                release_listeners: SubscriberSet::new(),
-                global_observers: SubscriberSet::new(),
-                quit_observers: SubscriberSet::new(),
-                layout_id_buffer: Default::default(),
-                propagate_event: true,
-                active_drag: None,
-            })
-        }))
+        Self(AppContext::new(
+            current_platform(),
+            asset_source,
+            http::client(),
+        ))
     }
 
     pub fn run<F>(self, on_finish_launching: F)
@@ -210,6 +149,62 @@ pub struct AppContext {
 }
 
 impl AppContext {
+    pub(crate) fn new(
+        platform: Arc<dyn Platform>,
+        asset_source: Arc<dyn AssetSource>,
+        http_client: Arc<dyn HttpClient>,
+    ) -> Arc<Mutex<Self>> {
+        let executor = platform.executor();
+        assert!(
+            executor.is_main_thread(),
+            "must construct App on main thread"
+        );
+
+        let text_system = Arc::new(TextSystem::new(platform.text_system()));
+        let mut entities = EntityMap::new();
+        let unit_entity = entities.insert(entities.reserve(), ());
+        let app_metadata = AppMetadata {
+            os_name: platform.os_name(),
+            os_version: platform.os_version().ok(),
+            app_version: platform.app_version().ok(),
+        };
+
+        Arc::new_cyclic(|this| {
+            Mutex::new(AppContext {
+                this: this.clone(),
+                text_system,
+                platform: MainThreadOnly::new(platform, executor.clone()),
+                app_metadata,
+                flushing_effects: false,
+                pending_updates: 0,
+                next_frame_callbacks: Default::default(),
+                executor,
+                svg_renderer: SvgRenderer::new(asset_source.clone()),
+                asset_source,
+                image_cache: ImageCache::new(http_client),
+                text_style_stack: Vec::new(),
+                globals_by_type: HashMap::default(),
+                unit_entity,
+                entities,
+                windows: SlotMap::with_key(),
+                keymap: Arc::new(RwLock::new(Keymap::default())),
+                global_action_listeners: HashMap::default(),
+                action_builders: HashMap::default(),
+                pending_effects: VecDeque::new(),
+                pending_notifications: HashSet::default(),
+                pending_global_notifications: HashSet::default(),
+                observers: SubscriberSet::new(),
+                event_listeners: SubscriberSet::new(),
+                release_listeners: SubscriberSet::new(),
+                global_observers: SubscriberSet::new(),
+                quit_observers: SubscriberSet::new(),
+                layout_id_buffer: Default::default(),
+                propagate_event: true,
+                active_drag: None,
+            })
+        })
+    }
+
     pub fn quit(&mut self) {
         let mut futures = Vec::new();
 

crates/gpui2/src/app/test_context.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, Handle, MainThread,
-    ModelContext, Result, Task, WindowContext,
+    ModelContext, Result, Task, TestDispatcher, TestPlatform, WindowContext,
 };
 use parking_lot::Mutex;
 use std::{any::Any, future::Future, sync::Arc};
@@ -37,6 +37,17 @@ impl Context for TestAppContext {
 }
 
 impl TestAppContext {
+    pub fn new(dispatcher: TestDispatcher) -> Self {
+        let executor = Executor::new(Arc::new(dispatcher));
+        let platform = Arc::new(TestPlatform::new(executor.clone()));
+        let asset_source = Arc::new(());
+        let http_client = util::http::FakeHttpClient::with_404_response();
+        Self {
+            app: AppContext::new(platform, asset_source, http_client),
+            executor,
+        }
+    }
+
     pub fn refresh(&mut self) -> Result<()> {
         let mut lock = self.app.lock();
         lock.refresh();
@@ -47,27 +58,27 @@ impl TestAppContext {
         &self.executor
     }
 
-    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
+    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
         let mut lock = self.app.lock();
-        Ok(f(&mut *lock))
+        f(&mut *lock)
     }
 
     pub fn read_window<R>(
         &self,
         handle: AnyWindowHandle,
-        update: impl FnOnce(&WindowContext) -> R,
-    ) -> Result<R> {
+        read: impl FnOnce(&WindowContext) -> R,
+    ) -> R {
         let mut app_context = self.app.lock();
-        app_context.read_window(handle.id, update)
+        app_context.read_window(handle.id, read).unwrap()
     }
 
     pub fn update_window<R>(
         &self,
         handle: AnyWindowHandle,
         update: impl FnOnce(&mut WindowContext) -> R,
-    ) -> Result<R> {
+    ) -> R {
         let mut app = self.app.lock();
-        app.update_window(handle.id, update)
+        app.update_window(handle.id, update).unwrap()
     }
 
     pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
@@ -94,22 +105,22 @@ impl TestAppContext {
     pub fn run_on_main<R>(
         &self,
         f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
-    ) -> Result<Task<R>>
+    ) -> Task<R>
     where
         R: Send + 'static,
     {
         let mut app_context = self.app.lock();
-        Ok(app_context.run_on_main(f))
+        app_context.run_on_main(f)
     }
 
-    pub fn has_global<G: 'static>(&self) -> Result<bool> {
+    pub fn has_global<G: 'static>(&self) -> bool {
         let lock = self.app.lock();
-        Ok(lock.has_global::<G>())
+        lock.has_global::<G>()
     }
 
-    pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
+    pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
         let lock = self.app.lock();
-        Ok(read(lock.global(), &lock))
+        read(lock.global(), &lock)
     }
 
     pub fn try_read_global<G: 'static, R>(
@@ -123,9 +134,9 @@ impl TestAppContext {
     pub fn update_global<G: 'static, R>(
         &mut self,
         update: impl FnOnce(&mut G, &mut AppContext) -> R,
-    ) -> Result<R> {
+    ) -> R {
         let mut lock = self.app.lock();
-        Ok(lock.update_global(update))
+        lock.update_global(update)
     }
 
     fn to_async(&self) -> AsyncAppContext {

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

@@ -1,5 +1,4 @@
-use crate::{DisplayId, Executor, Platform, PlatformTextSystem, TestDispatcher};
-use rand::prelude::*;
+use crate::{DisplayId, Executor, Platform, PlatformTextSystem};
 use std::sync::Arc;
 
 pub struct TestPlatform {
@@ -7,11 +6,8 @@ pub struct TestPlatform {
 }
 
 impl TestPlatform {
-    pub fn new(seed: u64) -> Self {
-        let rng = StdRng::seed_from_u64(seed);
-        TestPlatform {
-            executor: Executor::new(Arc::new(TestDispatcher::new(rng))),
-        }
+    pub fn new(executor: Executor) -> Self {
+        TestPlatform { executor }
     }
 }
 

crates/gpui2/src/text_system/line_wrapper.rs 🔗

@@ -139,11 +139,15 @@ impl Boundary {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{font, App};
+    use crate::{font, TestAppContext, TestDispatcher};
+    use rand::prelude::*;
 
     #[test]
     fn test_wrap_line() {
-        App::test(0).run(|cx| {
+        let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0));
+        let cx = TestAppContext::new(dispatcher);
+
+        cx.update(|cx| {
             let text_system = cx.text_system().clone();
             let mut wrapper = LineWrapper::new(
                 text_system.font_id(&font("Courier")).unwrap(),