test_context.rs

  1use crate::{
  2    AnyWindowHandle, AppContext, AsyncAppContext, Context, EventEmitter, Executor, MainThread,
  3    Model, ModelContext, Result, Task, TestDispatcher, TestPlatform, WindowContext,
  4};
  5use futures::SinkExt;
  6use parking_lot::Mutex;
  7use std::{future::Future, sync::Arc};
  8
  9#[derive(Clone)]
 10pub struct TestAppContext {
 11    pub app: Arc<Mutex<AppContext>>,
 12    pub executor: Executor,
 13}
 14
 15impl Context for TestAppContext {
 16    type ModelContext<'a, T> = ModelContext<'a, T>;
 17    type Result<T> = T;
 18
 19    fn build_model<T: 'static>(
 20        &mut self,
 21        build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T,
 22    ) -> Self::Result<Model<T>>
 23    where
 24        T: 'static + Send,
 25    {
 26        let mut lock = self.app.lock();
 27        lock.build_model(build_model)
 28    }
 29
 30    fn update_model<T: 'static, R>(
 31        &mut self,
 32        handle: &Model<T>,
 33        update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
 34    ) -> Self::Result<R> {
 35        let mut lock = self.app.lock();
 36        lock.update_model(handle, update)
 37    }
 38}
 39
 40impl TestAppContext {
 41    pub fn new(dispatcher: TestDispatcher) -> Self {
 42        let executor = Executor::new(Arc::new(dispatcher));
 43        let platform = Arc::new(TestPlatform::new(executor.clone()));
 44        let asset_source = Arc::new(());
 45        let http_client = util::http::FakeHttpClient::with_404_response();
 46        Self {
 47            app: AppContext::new(platform, asset_source, http_client),
 48            executor,
 49        }
 50    }
 51
 52    pub fn quit(&self) {
 53        self.app.lock().quit();
 54    }
 55
 56    pub fn refresh(&mut self) -> Result<()> {
 57        let mut lock = self.app.lock();
 58        lock.refresh();
 59        Ok(())
 60    }
 61
 62    pub fn executor(&self) -> &Executor {
 63        &self.executor
 64    }
 65
 66    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
 67        let mut cx = self.app.lock();
 68        cx.update(f)
 69    }
 70
 71    pub fn read_window<R>(
 72        &self,
 73        handle: AnyWindowHandle,
 74        read: impl FnOnce(&WindowContext) -> R,
 75    ) -> R {
 76        let mut app_context = self.app.lock();
 77        app_context.read_window(handle.id, read).unwrap()
 78    }
 79
 80    pub fn update_window<R>(
 81        &self,
 82        handle: AnyWindowHandle,
 83        update: impl FnOnce(&mut WindowContext) -> R,
 84    ) -> R {
 85        let mut app = self.app.lock();
 86        app.update_window(handle.id, update).unwrap()
 87    }
 88
 89    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
 90    where
 91        Fut: Future<Output = R> + Send + 'static,
 92        R: Send + 'static,
 93    {
 94        let cx = self.to_async();
 95        self.executor.spawn(async move { f(cx).await })
 96    }
 97
 98    pub fn spawn_on_main<Fut, R>(
 99        &self,
100        f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
101    ) -> Task<R>
102    where
103        Fut: Future<Output = R> + 'static,
104        R: Send + 'static,
105    {
106        let cx = self.to_async();
107        self.executor.spawn_on_main(|| f(cx))
108    }
109
110    pub fn run_on_main<R>(
111        &self,
112        f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
113    ) -> Task<R>
114    where
115        R: Send + 'static,
116    {
117        let mut app_context = self.app.lock();
118        app_context.run_on_main(f)
119    }
120
121    pub fn has_global<G: 'static>(&self) -> bool {
122        let lock = self.app.lock();
123        lock.has_global::<G>()
124    }
125
126    pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
127        let lock = self.app.lock();
128        read(lock.global(), &lock)
129    }
130
131    pub fn try_read_global<G: 'static, R>(
132        &self,
133        read: impl FnOnce(&G, &AppContext) -> R,
134    ) -> Option<R> {
135        let lock = self.app.lock();
136        Some(read(lock.try_global()?, &lock))
137    }
138
139    pub fn update_global<G: 'static, R>(
140        &mut self,
141        update: impl FnOnce(&mut G, &mut AppContext) -> R,
142    ) -> R {
143        let mut lock = self.app.lock();
144        lock.update_global(update)
145    }
146
147    pub fn to_async(&self) -> AsyncAppContext {
148        AsyncAppContext {
149            app: Arc::downgrade(&self.app),
150            executor: self.executor.clone(),
151        }
152    }
153
154    pub fn subscribe<T: 'static + EventEmitter + Send>(
155        &mut self,
156        entity: &Model<T>,
157    ) -> futures::channel::mpsc::UnboundedReceiver<T::Event>
158    where
159        T::Event: 'static + Send + Clone,
160    {
161        let (mut tx, rx) = futures::channel::mpsc::unbounded();
162        entity
163            .update(self, |_, cx: &mut ModelContext<T>| {
164                cx.subscribe(entity, move |_, _, event, cx| {
165                    cx.executor().block(tx.send(event.clone())).unwrap();
166                })
167            })
168            .detach();
169        rx
170    }
171}