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