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