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}