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/view/context_test.rs
StratoSDK / crates / strato-ui-core / src / core / view / context_test.rs
1use crate::elements::Empty;
2use crate::platform::WindowStyle;
3use crate::{App, AppContext, Element, Entity, TypedActionView};
4 
5#[test]
6fn test_spawn_from_view() {
7 #[derive(Default)]
8 struct View {
9 count: usize,
10 }
11 
12 impl Entity for View {
13 type Event = ();
14 }
15 
16 impl super::View for View {
17 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
18 Empty::new().finish()
19 }
20 
21 fn ui_name() -> &'static str {
22 "View"
23 }
24 }
25 
26 impl TypedActionView for View {
27 type Action = ();
28 }
29 
30 App::test((), |mut app| async move {
31 let (_, handle) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
32 
33 let (tx, rx) = futures::channel::oneshot::channel();
34 handle.update(&mut app, move |_, c| {
35 c.spawn(async { 7 }, move |me, output, _| {
36 tx.send(()).unwrap();
37 me.count = output;
38 })
39 });
40 rx.await.unwrap();
41 
42 let (tx, rx) = futures::channel::oneshot::channel();
43 handle.read(&app, |view, _| assert_eq!(view.count, 7));
44 handle.update(&mut app, move |_, c| {
45 c.spawn(async { 14 }, move |me, output, _| {
46 tx.send(()).unwrap();
47 me.count = output;
48 })
49 });
50 rx.await.unwrap();
51 handle.read(&app, |view, _| assert_eq!(view.count, 14));
52 });
53}
54 
55#[ignore]
56#[test]
57fn test_spawn_abortable_from_view() {
58 #[derive(Debug, Default, PartialEq)]
59 enum SpawnedOutcome {
60 #[default]
61 NotStarted,
62 Aborted,
63 Resolved {
64 value: usize,
65 },
66 }
67 
68 #[derive(Default)]
69 struct View {
70 spawned_outcome: SpawnedOutcome,
71 }
72 
73 impl Entity for View {
74 type Event = ();
75 }
76 
77 impl super::View for View {
78 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
79 Empty::new().finish()
80 }
81 
82 fn ui_name() -> &'static str {
83 "View"
84 }
85 }
86 
87 impl TypedActionView for View {
88 type Action = ();
89 }
90 
91 App::test((), |mut app| async move {
92 let (_, handle) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
93 
94 let (tx, rx) = futures::channel::oneshot::channel();
95 handle.update(&mut app, |_, c| {
96 c.spawn_abortable(
97 async { 7 },
98 move |me, output, _| {
99 tx.send(()).unwrap();
100 me.spawned_outcome = SpawnedOutcome::Resolved { value: output }
101 },
102 |_, _| {},
103 )
104 });
105 rx.await.unwrap();
106 
107 handle.read(&app, |view, _| {
108 assert_eq!(view.spawned_outcome, SpawnedOutcome::Resolved { value: 7 })
109 });
110 
111 let (tx, rx) = futures::channel::oneshot::channel();
112 handle.update(&mut app, move |_, c| {
113 let abort_handle = c.spawn_abortable(
114 async { 7 },
115 |_, _, _| {},
116 move |me, _| {
117 me.spawned_outcome = SpawnedOutcome::Aborted;
118 tx.send(()).unwrap();
119 },
120 );
121 abort_handle.abort();
122 });
123 
124 rx.await.unwrap();
125 
126 // The call future passed to `spawn_abortable` was successfully aborted.
127 handle.read(&app, |view, _| {
128 assert_eq!(view.spawned_outcome, SpawnedOutcome::Aborted)
129 });
130 });
131}
132 
133#[test]
134fn test_view_spawner() {
135 #[derive(Default)]
136 struct View {
137 count: usize,
138 }
139 
140 impl Entity for View {
141 type Event = ();
142 }
143 
144 impl super::View for View {
145 fn render<'a>(&self, _: &AppContext) -> Box<dyn Element> {
146 Empty::new().finish()
147 }
148 
149 fn ui_name() -> &'static str {
150 "View"
151 }
152 }
153 
154 impl TypedActionView for View {
155 type Action = ();
156 }
157 
158 App::test((), |mut app| async move {
159 let (_, handle) = app.add_window(WindowStyle::NotStealFocus, |_| View::default());
160 
161 let spawner = handle.update(&mut app, |_, ctx| ctx.spawner());
162 
163 // Test a single spawned task.
164 let result = spawner
165 .spawn(|view, ctx| {
166 view.count += 42;
167 ctx.notify();
168 view.count
169 })
170 .await
171 .unwrap();
172 
173 assert_eq!(result, 42);
174 handle.read(&app, |view, _| assert_eq!(view.count, 42));
175 
176 // Test multiple spawned tasks.
177 let task1 = spawner.spawn(|view, _| {
178 view.count *= 2;
179 view.count
180 });
181 
182 let task2 = spawner.spawn(|view, _| {
183 view.count += 10;
184 view.count
185 });
186 
187 let (result1, result2) = futures::future::join(task1, task2).await;
188 
189 // Note: The exact final value depends on task execution order but both tasks should succeed.
190 assert!(result1.is_ok());
191 assert!(result2.is_ok());
192 
193 handle.read(&app, |view, _| {
194 assert!(view.count > 42);
195 });
196 });
197}
198