1pub use hecs::Entity;
8
9pub mod commands;
10pub mod events;
11pub mod local;
12pub mod observers;
13pub mod plugin;
14pub mod promise;
15pub mod query;
16pub mod resources;
17pub mod schedule;
18pub mod system;
19pub mod system_param;
20
21#[cfg(test)]
22mod tests {
23 use std::{cell::RefCell, rc::Rc};
24
25 use crate::ecs::{
26 commands::{Commands, ResourceCommandQueue, TriggerQueue},
27 local::Local,
28 resources::{Read, Resources, Write},
29 schedule::Schedule,
30 };
31
32 #[test]
33 fn resources_insert_and_get() {
34 let mut resources = Resources::default();
35 resources.insert(42i32);
36
37 assert_eq!(*resources.get::<i32>(), 42);
38 }
39
40 #[test]
41 fn resources_get_mut_updates_value() {
42 let mut resources = Resources::default();
43 resources.insert(1i32);
44
45 *resources.get_mut::<i32>() += 1;
46
47 assert_eq!(*resources.get::<i32>(), 2);
48 }
49
50 #[test]
51 fn resources_contains() {
52 let mut resources = Resources::default();
53 assert!(!resources.contains::<i32>());
54
55 resources.insert(1i32);
56 assert!(resources.contains::<i32>());
57 }
58
59 #[test]
60 fn resources_remove() {
61 let mut resources = Resources::default();
62 resources.insert(7i32);
63
64 let removed = resources.remove::<i32>();
65
66 assert_eq!(removed, Some(7));
67 assert!(!resources.contains::<i32>());
68 }
69
70 #[test]
71 #[should_panic(expected = "Resource not found")]
72 fn resources_get_missing_panics() {
73 let resources = Resources::default();
74 resources.get::<i32>();
75 }
76
77 #[test]
78 fn schedule_runs_systems_in_order() {
79 let mut world = hecs::World::default();
80 let mut resources = Resources::default();
81 resources.insert(0i32);
82 resources.insert(hecs::CommandBuffer::default());
83
84 let mut schedule = Schedule::default();
85 schedule.add_system(|mut counter: Write<i32>| {
86 *counter += 1;
87 });
88
89 schedule.run(&mut world, &mut resources);
90 schedule.run(&mut world, &mut resources);
91
92 assert_eq!(*resources.get::<i32>(), 2);
93 }
94
95 #[test]
96 fn commands_spawn_is_applied_on_sync() {
97 let mut world = hecs::World::default();
98 let mut resources = Resources::default();
99 resources.insert(hecs::CommandBuffer::default());
100 resources.insert(ResourceCommandQueue::default());
101 resources.insert(TriggerQueue::default());
102
103 let mut schedule = Schedule::default();
104 schedule.add_system(|mut commands: Commands| {
105 commands.spawn((1i32,));
106 });
107
108 assert_eq!(world.len(), 0);
109
110 schedule.run(&mut world, &mut resources);
111
112 assert_eq!(world.len(), 1);
113 }
114
115 #[test]
116 fn commands_insert_resource_is_applied_on_sync() {
117 let mut world = hecs::World::default();
118 let mut resources = Resources::default();
119 resources.insert(hecs::CommandBuffer::default());
120 resources.insert(ResourceCommandQueue::default());
121 resources.insert(TriggerQueue::default());
122
123 let mut schedule = Schedule::default();
124 schedule.add_system(|mut commands: Commands| {
125 commands.insert_resource(42i32);
126 });
127
128 assert!(!resources.contains::<i32>());
129
130 schedule.run(&mut world, &mut resources);
131
132 assert_eq!(*resources.get::<i32>(), 42);
133 }
134
135 #[test]
136 fn commands_remove_resource_is_applied_on_sync() {
137 let mut world = hecs::World::default();
138 let mut resources = Resources::default();
139 resources.insert(hecs::CommandBuffer::default());
140 resources.insert(ResourceCommandQueue::default());
141 resources.insert(TriggerQueue::default());
142 resources.insert(42i32);
143
144 let mut schedule = Schedule::default();
145 schedule.add_system(|mut commands: Commands| {
146 commands.remove_resource::<i32>();
147 });
148
149 schedule.run(&mut world, &mut resources);
150
151 assert!(!resources.contains::<i32>());
152 }
153
154 #[test]
155 fn option_read_is_some_when_resource_present() {
156 let mut world = hecs::World::default();
157 let mut resources = Resources::default();
158 resources.insert(hecs::CommandBuffer::default());
159 resources.insert(42i32);
160
161 let mut schedule = Schedule::default();
162 schedule.add_system(|value: Option<Read<i32>>| {
163 assert_eq!(*value.expect("resource should be present"), 42);
164 });
165
166 schedule.run(&mut world, &mut resources);
167 }
168
169 #[test]
170 fn option_read_is_none_when_resource_missing() {
171 let mut world = hecs::World::default();
172 let mut resources = Resources::default();
173 resources.insert(hecs::CommandBuffer::default());
174
175 let mut schedule = Schedule::default();
176 schedule.add_system(|value: Option<Read<i32>>| {
177 assert!(value.is_none());
178 });
179
180 schedule.run(&mut world, &mut resources);
181 }
182
183 #[test]
184 fn option_write_mutates_resource_when_present() {
185 let mut world = hecs::World::default();
186 let mut resources = Resources::default();
187 resources.insert(hecs::CommandBuffer::default());
188 resources.insert(1i32);
189
190 let mut schedule = Schedule::default();
191 schedule.add_system(|value: Option<Write<i32>>| {
192 *value.expect("resource should be present") += 1;
193 });
194
195 schedule.run(&mut world, &mut resources);
196
197 assert_eq!(*resources.get::<i32>(), 2);
198 }
199
200 #[test]
201 fn option_write_is_none_when_resource_missing() {
202 let mut world = hecs::World::default();
203 let mut resources = Resources::default();
204 resources.insert(hecs::CommandBuffer::default());
205
206 let mut schedule = Schedule::default();
207 schedule.add_system(|value: Option<Write<i32>>| {
208 assert!(value.is_none());
209 });
210
211 schedule.run(&mut world, &mut resources);
212 }
213
214 #[test]
215 fn local_defaults_to_type_default() {
216 let mut world = hecs::World::default();
217 let mut resources = Resources::default();
218 resources.insert(hecs::CommandBuffer::default());
219
220 let mut schedule = Schedule::default();
221 schedule.add_system(|counter: Local<i32>| {
222 assert_eq!(*counter, 0);
223 });
224
225 schedule.run(&mut world, &mut resources);
226 }
227
228 #[test]
229 fn local_persists_across_runs() {
230 let mut world = hecs::World::default();
231 let mut resources = Resources::default();
232 resources.insert(hecs::CommandBuffer::default());
233
234 let log = Rc::new(RefCell::new(Vec::new()));
235 let log_clone = log.clone();
236
237 let mut schedule = Schedule::default();
238 schedule.add_system(move |mut counter: Local<i32>| {
239 *counter += 1;
240 log_clone.borrow_mut().push(*counter);
241 });
242
243 schedule.run(&mut world, &mut resources);
244 schedule.run(&mut world, &mut resources);
245 schedule.run(&mut world, &mut resources);
246
247 assert_eq!(*log.borrow(), vec![1, 2, 3]);
248 }
249
250 #[test]
251 fn local_state_is_isolated_per_system() {
252 let mut world = hecs::World::default();
253 let mut resources = Resources::default();
254 resources.insert(hecs::CommandBuffer::default());
255
256 let log_a = Rc::new(RefCell::new(Vec::new()));
257 let log_b = Rc::new(RefCell::new(Vec::new()));
258 let (la, lb) = (log_a.clone(), log_b.clone());
259
260 let mut schedule = Schedule::default();
261 schedule
262 .add_system(move |mut counter: Local<i32>| {
263 *counter += 1;
264 la.borrow_mut().push(*counter);
265 })
266 .add_system(move |mut counter: Local<i32>| {
267 *counter += 10;
268 lb.borrow_mut().push(*counter);
269 });
270
271 schedule.run(&mut world, &mut resources);
272 schedule.run(&mut world, &mut resources);
273
274 assert_eq!(*log_a.borrow(), vec![1, 2]);
275 assert_eq!(*log_b.borrow(), vec![10, 20]);
276 }
277
278 fn log_a(mut log: Write<Vec<&'static str>>) {
279 log.push("a");
280 }
281
282 fn log_b(mut log: Write<Vec<&'static str>>) {
283 log.push("b");
284 }
285
286 #[test]
287 fn before_reorders_execution_relative_to_registration_order() {
288 use crate::ecs::system_param::IntoSystemConfig;
289
290 let mut world = hecs::World::default();
291 let mut resources = Resources::default();
292 resources.insert(hecs::CommandBuffer::default());
293 resources.insert(Vec::<&'static str>::new());
294
295 let mut schedule = Schedule::default();
297 schedule.add_system(log_b).add_system(log_a.before(log_b));
298
299 schedule.run(&mut world, &mut resources);
300
301 assert_eq!(*resources.get::<Vec<&'static str>>(), vec!["a", "b"]);
302 }
303
304 #[test]
305 fn after_resolves_against_a_system_added_later() {
306 use crate::ecs::system_param::IntoSystemConfig;
307
308 let mut world = hecs::World::default();
309 let mut resources = Resources::default();
310 resources.insert(hecs::CommandBuffer::default());
311 resources.insert(Vec::<&'static str>::new());
312
313 let mut schedule = Schedule::default();
316 schedule.add_system(log_a.after(log_b)).add_system(log_b);
317
318 schedule.run(&mut world, &mut resources);
319
320 assert_eq!(*resources.get::<Vec<&'static str>>(), vec!["b", "a"]);
321 }
322
323 #[test]
324 #[should_panic(expected = "cycle")]
325 fn cyclic_ordering_constraints_panic() {
326 use crate::ecs::system_param::IntoSystemConfig;
327
328 let mut world = hecs::World::default();
329 let mut resources = Resources::default();
330 resources.insert(hecs::CommandBuffer::default());
331 resources.insert(Vec::<&'static str>::new());
332
333 let mut schedule = Schedule::default();
334 schedule
335 .add_system(log_a.after(log_b))
336 .add_system(log_b.after(log_a));
337
338 schedule.run(&mut world, &mut resources);
339 }
340
341 fn log_c(mut log: Write<Vec<&'static str>>) {
342 log.push("c");
343 }
344
345 #[test]
346 fn higher_priority_runs_first_among_unconstrained_systems() {
347 use crate::ecs::system_param::IntoSystemConfig;
348
349 let mut world = hecs::World::default();
350 let mut resources = Resources::default();
351 resources.insert(hecs::CommandBuffer::default());
352 resources.insert(Vec::<&'static str>::new());
353
354 let mut schedule = Schedule::default();
357 schedule
358 .add_system(log_a)
359 .add_system(log_b.priority(10))
360 .add_system(log_c.priority(5));
361
362 schedule.run(&mut world, &mut resources);
363
364 assert_eq!(*resources.get::<Vec<&'static str>>(), vec!["b", "c", "a"]);
365 }
366
367 #[test]
368 fn explicit_constraint_wins_over_priority() {
369 use crate::ecs::system_param::IntoSystemConfig;
370
371 let mut world = hecs::World::default();
372 let mut resources = Resources::default();
373 resources.insert(hecs::CommandBuffer::default());
374 resources.insert(Vec::<&'static str>::new());
375
376 let mut schedule = Schedule::default();
379 schedule
380 .add_system(log_b.priority(10))
381 .add_system(log_a.priority(0).before(log_b));
382
383 schedule.run(&mut world, &mut resources);
384
385 assert_eq!(*resources.get::<Vec<&'static str>>(), vec!["a", "b"]);
386 }
387
388 #[test]
389 fn chain_forces_relative_order() {
390 use crate::ecs::system_param::Chain;
391
392 let mut world = hecs::World::default();
393 let mut resources = Resources::default();
394 resources.insert(hecs::CommandBuffer::default());
395 resources.insert(Vec::<&'static str>::new());
396
397 let mut schedule = Schedule::default();
400 schedule.add_systems((log_c, log_a, log_b).chain());
401
402 schedule.run(&mut world, &mut resources);
403
404 assert_eq!(*resources.get::<Vec<&'static str>>(), vec!["c", "a", "b"]);
405 }
406
407 #[test]
408 fn chain_after_only_constrains_its_first_system() {
409 use crate::ecs::system_param::Chain;
410
411 fn log_setup(mut log: Write<Vec<&'static str>>) {
412 log.push("setup");
413 }
414
415 let mut world = hecs::World::default();
416 let mut resources = Resources::default();
417 resources.insert(hecs::CommandBuffer::default());
418 resources.insert(Vec::<&'static str>::new());
419
420 let mut schedule = Schedule::default();
421 schedule
422 .add_systems((log_a, log_b).chain().after(log_setup))
423 .add_system(log_setup);
424
425 schedule.run(&mut world, &mut resources);
426
427 assert_eq!(*resources.get::<Vec<&'static str>>(), vec!["setup", "a", "b"]);
428 }
429}