Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-core/src/core/autotracking/autotracking_test.rs
StratoSDK / crates / strato-ui-core / src / core / autotracking / autotracking_test.rs
1use super::*;
2use crate::{
3 elements::Empty, platform::WindowStyle, App, AppContext, Element, Entity, ModelHandle,
4 TypedActionView, View,
5};
6use std::sync::{
7 atomic::{AtomicUsize, Ordering},
8 Arc,
9};
10 
11#[derive(Default)]
12struct Model {
13 first: Tracked<usize>,
14 second: Tracked<bool>,
15 third: Tracked<isize>,
16}
17 
18impl Entity for Model {
19 type Event = ();
20}
21 
22struct TestView {
23 model: ModelHandle<Model>,
24 field: Tracked<usize>,
25 other_field: Tracked<bool>,
26 counter: Arc<AtomicUsize>,
27}
28 
29impl TestView {
30 fn new(model: ModelHandle<Model>, counter: Arc<AtomicUsize>) -> Self {
31 TestView {
32 model,
33 field: Tracked::new(0),
34 other_field: Tracked::new(false),
35 counter,
36 }
37 }
38}
39 
40impl Entity for TestView {
41 type Event = ();
42}
43 
44impl View for TestView {
45 fn ui_name() -> &'static str {
46 "TestView"
47 }
48 
49 fn render(&self, app: &AppContext) -> Box<dyn Element> {
50 // While rendering, we explicitly read / depend on the fields `first` and `second` of
51 // model, as well as `field` of the View itself.
52 
53 // We explicitly do _not_ depend on `Model::third` nor `other_field` on the View.
54 let model = self.model.as_ref(app);
55 let _first = *model.first;
56 let _second = *model.second;
57 let _field = *self.field;
58 
59 // Increment the render counter so that we can track how often a View is rendered
60 self.counter.fetch_add(1, Ordering::Relaxed);
61 
62 Empty::new().finish()
63 }
64}
65 
66impl TypedActionView for TestView {
67 type Action = ();
68}
69 
70#[test]
71fn test_update_view_dependency_rerenders() {
72 App::test((), |mut app| async move {
73 let model_handle = app.add_model(|_| Model::default());
74 
75 let render_counter = Arc::new(AtomicUsize::new(0));
76 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_| {
77 TestView::new(model_handle.clone(), render_counter.clone())
78 });
79 
80 // Force the window to be rendered the first time
81 view_handle.update(&mut app, |_, _| {});
82 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
83 
84 // Update an internal View dependency and confirm that it causes a rerender of the View
85 view_handle.update(&mut app, |view, _| {
86 *view.field += 1;
87 });
88 assert_eq!(render_counter.load(Ordering::Relaxed), 2);
89 });
90}
91 
92#[test]
93fn test_update_view_non_dependency_no_rerender() {
94 App::test((), |mut app| async move {
95 let model_handle = app.add_model(|_| Model::default());
96 
97 let render_counter = Arc::new(AtomicUsize::new(0));
98 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_| {
99 TestView::new(model_handle.clone(), render_counter.clone())
100 });
101 
102 // Force the window to be rendered the first time
103 view_handle.update(&mut app, |_, _| {});
104 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
105 
106 // Update an internal View field that is not a dependency and confirm that it does not
107 // cause a rerender of the View
108 view_handle.update(&mut app, |view, _| {
109 *view.other_field = true;
110 });
111 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
112 });
113}
114 
115#[test]
116fn test_update_model_dependency_rerenders() {
117 App::test((), |mut app| async move {
118 let model_handle = app.add_model(|_| Model::default());
119 
120 let render_counter = Arc::new(AtomicUsize::new(0));
121 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_| {
122 TestView::new(model_handle.clone(), render_counter.clone())
123 });
124 
125 // Force the window to be rendered the first time
126 view_handle.update(&mut app, |_, _| {});
127 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
128 
129 // Update a Model dependency and confirm that it causes a rerender of the View
130 model_handle.update(&mut app, |model, _| {
131 *model.first += 1;
132 });
133 assert_eq!(render_counter.load(Ordering::Relaxed), 2);
134 
135 // Update another Model dependency and confirm that it causes a rerender
136 model_handle.update(&mut app, |model, _| {
137 *model.second = true;
138 });
139 assert_eq!(render_counter.load(Ordering::Relaxed), 3);
140 });
141}
142 
143#[test]
144fn test_update_model_non_dependency_no_rerender() {
145 App::test((), |mut app| async move {
146 let model_handle = app.add_model(|_| Model::default());
147 
148 let render_counter = Arc::new(AtomicUsize::new(0));
149 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_| {
150 TestView::new(model_handle.clone(), render_counter.clone())
151 });
152 
153 // Force the window to be rendered the first time
154 view_handle.update(&mut app, |_, _| {});
155 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
156 
157 // Update a Model field that is not a dependency and confirm that it does not
158 // cause a rerender of the View
159 model_handle.update(&mut app, |model, _| {
160 *model.third -= 1000;
161 });
162 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
163 });
164}
165 
166#[test]
167fn test_updates_multiple_sources() {
168 App::test((), |mut app| async move {
169 let model_handle = app.add_model(|_| Model::default());
170 
171 let render_counter = Arc::new(AtomicUsize::new(0));
172 let (_, view_handle) = app.add_window(WindowStyle::NotStealFocus, |_| {
173 TestView::new(model_handle.clone(), render_counter.clone())
174 });
175 
176 // Force the window to be rendered the first time
177 view_handle.update(&mut app, |_, _| {});
178 assert_eq!(render_counter.load(Ordering::Relaxed), 1);
179 
180 // Update several Model dependencies and confirm it causes a single rerender
181 model_handle.update(&mut app, |model, _| {
182 *model.first += 1;
183 *model.second = true;
184 });
185 assert_eq!(render_counter.load(Ordering::Relaxed), 2);
186 
187 // Update a View dependency and confirm it causes another rerender
188 view_handle.update(&mut app, |view, _| {
189 *view.field += 100;
190 });
191 assert_eq!(render_counter.load(Ordering::Relaxed), 3);
192 
193 // Update a field that is not a dependency and confirm that it doesn't cause a rerender
194 view_handle.update(&mut app, |view, _| {
195 *view.other_field = true;
196 });
197 assert_eq!(render_counter.load(Ordering::Relaxed), 3);
198 
199 // Update a non-dependency on the Model and confirm that there is no rerender
200 model_handle.update(&mut app, |model, _| {
201 *model.third -= 100;
202 });
203 assert_eq!(render_counter.load(Ordering::Relaxed), 3);
204 
205 // Update the view _and_ the model in the same call and confirm that it causes a single
206 // rerender
207 view_handle.update(&mut app, |view, ctx| {
208 *view.field += 20;
209 view.model.update(ctx, |model, _| {
210 *model.first += 23;
211 });
212 });
213 assert_eq!(render_counter.load(Ordering::Relaxed), 4);
214 });
215}
216 
217#[test]
218fn test_model_updates_multiple_views() {
219 struct OtherView {
220 model: ModelHandle<Model>,
221 counter: Arc<AtomicUsize>,
222 }
223 
224 impl OtherView {
225 fn new(model: ModelHandle<Model>, counter: Arc<AtomicUsize>) -> Self {
226 OtherView { model, counter }
227 }
228 }
229 
230 impl Entity for OtherView {
231 type Event = ();
232 }
233 
234 impl View for OtherView {
235 fn ui_name() -> &'static str {
236 "OtherView"
237 }
238 
239 fn render(&self, app: &AppContext) -> Box<dyn Element> {
240 let model = self.model.as_ref(app);
241 // This view depends on `second` and `third` in the model, so updates to those should
242 // cause it to rerender (overlapping `second` with the main test view)
243 let _second = *model.second;
244 let _third = *model.third;
245 
246 self.counter.fetch_add(1, Ordering::Relaxed);
247 
248 Empty::new().finish()
249 }
250 }
251 
252 App::test((), |mut app| async move {
253 let model_handle = app.add_model(|_| Model::default());
254 
255 let first_render_counter = Arc::new(AtomicUsize::new(0));
256 let (window_id, first_view) = app.add_window(WindowStyle::NotStealFocus, |_| {
257 TestView::new(model_handle.clone(), first_render_counter.clone())
258 });
259 
260 let second_render_counter = Arc::new(AtomicUsize::new(0));
261 let _second_view = app.add_view(window_id, |_| {
262 OtherView::new(model_handle.clone(), second_render_counter.clone())
263 });
264 
265 // Force the window to be rendered the first time
266 first_view.update(&mut app, |_, _| {});
267 assert_eq!(first_render_counter.load(Ordering::Relaxed), 1);
268 assert_eq!(second_render_counter.load(Ordering::Relaxed), 1);
269 
270 // Update a field on the model that _only_ the first view depends on and confirm that is
271 // the only view rerendered
272 model_handle.update(&mut app, |model, _| {
273 *model.first += 100;
274 });
275 assert_eq!(first_render_counter.load(Ordering::Relaxed), 2);
276 assert_eq!(second_render_counter.load(Ordering::Relaxed), 1);
277 
278 // Update a field on the model that _both_ views depend on and confirm both are rerendered
279 model_handle.update(&mut app, |model, _| {
280 *model.second = true;
281 });
282 assert_eq!(first_render_counter.load(Ordering::Relaxed), 3);
283 assert_eq!(second_render_counter.load(Ordering::Relaxed), 2);
284 
285 // Update a field on the model that _only_ the second view depends on and confirm that is
286 // the only view rerendered
287 model_handle.update(&mut app, |model, _| {
288 *model.third -= 55;
289 });
290 assert_eq!(first_render_counter.load(Ordering::Relaxed), 3);
291 assert_eq!(second_render_counter.load(Ordering::Relaxed), 3);
292 });
293}
294