1use crate::{
2 AnyWindowHandle, AppContext, AsyncAppContext, BackgroundExecutor, Context, EventEmitter,
3 ForegroundExecutor, Model, ModelContext, Result, Task, TestDispatcher, TestPlatform,
4 WindowContext,
5};
6use anyhow::anyhow;
7use futures::{SinkExt, StreamExt};
8use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc, time::Duration};
9
10#[derive(Clone)]
11pub struct TestAppContext {
12 pub app: Rc<RefCell<AppContext>>,
13 pub background_executor: BackgroundExecutor,
14 pub foreground_executor: ForegroundExecutor,
15}
16
17impl Context for TestAppContext {
18 type ModelContext<'a, T> = ModelContext<'a, T>;
19 type Result<T> = T;
20
21 fn build_model<T: 'static>(
22 &mut self,
23 build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T,
24 ) -> Self::Result<Model<T>>
25 where
26 T: 'static,
27 {
28 let mut app = self.app.borrow_mut();
29 app.build_model(build_model)
30 }
31
32 fn update_model<T: 'static, R>(
33 &mut self,
34 handle: &Model<T>,
35 update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
36 ) -> Self::Result<R> {
37 let mut app = self.app.borrow_mut();
38 app.update_model(handle, update)
39 }
40}
41
42impl TestAppContext {
43 pub fn new(dispatcher: TestDispatcher) -> Self {
44 let dispatcher = Arc::new(dispatcher);
45 let background_executor = BackgroundExecutor::new(dispatcher.clone());
46 let foreground_executor = ForegroundExecutor::new(dispatcher);
47 let platform = Rc::new(TestPlatform::new(
48 background_executor.clone(),
49 foreground_executor.clone(),
50 ));
51 let asset_source = Arc::new(());
52 let http_client = util::http::FakeHttpClient::with_404_response();
53 Self {
54 app: AppContext::new(platform, asset_source, http_client),
55 background_executor,
56 foreground_executor,
57 }
58 }
59
60 pub fn quit(&self) {
61 self.app.borrow_mut().quit();
62 }
63
64 pub fn refresh(&mut self) -> Result<()> {
65 let mut app = self.app.borrow_mut();
66 app.refresh();
67 Ok(())
68 }
69
70 pub fn executor(&self) -> &BackgroundExecutor {
71 &self.background_executor
72 }
73
74 pub fn foreground_executor(&self) -> &ForegroundExecutor {
75 &self.foreground_executor
76 }
77
78 pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
79 let mut cx = self.app.borrow_mut();
80 cx.update(f)
81 }
82
83 pub fn read_window<R>(
84 &self,
85 handle: AnyWindowHandle,
86 read: impl FnOnce(&WindowContext) -> R,
87 ) -> R {
88 let app_context = self.app.borrow();
89 app_context.read_window(handle.id, read).unwrap()
90 }
91
92 pub fn update_window<R>(
93 &self,
94 handle: AnyWindowHandle,
95 update: impl FnOnce(&mut WindowContext) -> R,
96 ) -> R {
97 let mut app = self.app.borrow_mut();
98 app.update_window(handle.id, update).unwrap()
99 }
100
101 pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
102 where
103 Fut: Future<Output = R> + 'static,
104 R: 'static,
105 {
106 self.foreground_executor.spawn(f(self.to_async()))
107 }
108
109 pub fn has_global<G: 'static>(&self) -> bool {
110 let app = self.app.borrow();
111 app.has_global::<G>()
112 }
113
114 pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
115 let app = self.app.borrow();
116 read(app.global(), &app)
117 }
118
119 pub fn try_read_global<G: 'static, R>(
120 &self,
121 read: impl FnOnce(&G, &AppContext) -> R,
122 ) -> Option<R> {
123 let lock = self.app.borrow();
124 Some(read(lock.try_global()?, &lock))
125 }
126
127 pub fn update_global<G: 'static, R>(
128 &mut self,
129 update: impl FnOnce(&mut G, &mut AppContext) -> R,
130 ) -> R {
131 let mut lock = self.app.borrow_mut();
132 lock.update_global(update)
133 }
134
135 pub fn to_async(&self) -> AsyncAppContext {
136 AsyncAppContext {
137 app: Rc::downgrade(&self.app),
138 background_executor: self.background_executor.clone(),
139 foreground_executor: self.foreground_executor.clone(),
140 }
141 }
142
143 pub fn subscribe<T: 'static + EventEmitter>(
144 &mut self,
145 entity: &Model<T>,
146 ) -> futures::channel::mpsc::UnboundedReceiver<T::Event>
147 where
148 T::Event: 'static + Clone,
149 {
150 let (mut tx, rx) = futures::channel::mpsc::unbounded();
151 entity
152 .update(self, |_, cx: &mut ModelContext<T>| {
153 cx.subscribe(entity, move |_, _, event, cx| {
154 cx.background_executor()
155 .block(tx.send(event.clone()))
156 .unwrap();
157 })
158 })
159 .detach();
160 rx
161 }
162
163 pub async fn condition<T: EventEmitter + 'static>(
164 &mut self,
165 model: &Model<T>,
166 mut predicate: impl FnMut(&mut T, &mut ModelContext<T>) -> bool,
167 ) {
168 let (mut tx, mut rx) = futures::channel::mpsc::unbounded::<()>();
169 let timer = self.executor().timer(Duration::from_secs(3));
170
171 let subscriptions = model.update(self, move |_, cx| {
172 (
173 cx.observe(model, move |_, _, _| {
174 // let _ = tx.send(());
175 }),
176 cx.subscribe(model, move |_, _, _, _| {
177 let _ = tx.send(());
178 }),
179 )
180 });
181
182 use futures::FutureExt as _;
183 use smol::future::FutureExt as _;
184
185 async {
186 while rx.next().await.is_some() {
187 if model.update(self, &mut predicate) {
188 return Ok(());
189 }
190 }
191 drop(subscriptions);
192 unreachable!()
193 }
194 .race(timer.map(|_| Err(anyhow!("condition timed out"))))
195 .await
196 .unwrap();
197 }
198}