blob: a20f66f171ffb2ecb43b8a474134b838f3aed803 [file] [log] [blame]
Adam Snaider644a41e2023-10-20 15:14:44 -04001#[cfg(test)]
2mod tests {
3 use std::{cell::Cell, time::Duration};
4
5 use aos_configuration::read_config_from;
6 use aos_events_event_loop_runtime::{EventLoopRuntimeHolder, Watcher};
7 use aos_events_simulated_event_loop::{SimulatedEventLoopFactory, SimulatedEventLoopHolder};
8 use aos_test_init::test_init;
9 use ping_lib::PingTask;
10 use ping_rust_fbs::aos::examples as ping;
11 use pong_rust_fbs::aos::examples as pong;
12 use runfiles::Runfiles;
13
14 // We use this trait to simplify leaking memory. For now, the event loop only allows
15 // data with a `'static` lifetime. Until that restriction is lifted, we may leak
16 // some memory in tests.
17 trait Leak: Sized {
18 fn leak(self) -> &'static mut Self {
19 Box::leak(Box::new(self))
20 }
21 }
22
23 impl<T> Leak for T {}
24
25 #[allow(unused)]
26 struct PingPongTest {
27 ping_event_loop: EventLoopRuntimeHolder<SimulatedEventLoopHolder>,
28 pong_event_loop: EventLoopRuntimeHolder<SimulatedEventLoopHolder>,
29 event_loop_factory: SimulatedEventLoopFactory<'static>,
30 }
31
32 impl PingPongTest {
33 pub fn init() -> PingPongTest {
34 test_init();
35 let r = Runfiles::create().unwrap();
36 let config =
37 read_config_from(&r.rlocation("org_frc971/aos/events/pingpong_config.json"))
38 .unwrap()
39 .leak();
40 let mut event_loop_factory = SimulatedEventLoopFactory::new(config);
41
42 let ping_event_loop = event_loop_factory.make_runtime("ping", None, |runtime| {
43 let ping = PingTask::new();
44 runtime.spawn(async move { ping.tasks(runtime, 10000).await });
45 });
46
47 let pong_event_loop = event_loop_factory.make_runtime("pong", None, |runtime| {
48 runtime.spawn(async move { pong_lib::pong(runtime).await })
49 });
50 PingPongTest {
51 event_loop_factory,
52 ping_event_loop,
53 pong_event_loop,
54 }
55 }
56
57 pub fn event_loop(&mut self) -> &mut SimulatedEventLoopFactory<'static> {
58 &mut self.event_loop_factory
59 }
60 }
61
62 #[test]
63 fn starts() {
64 let mut pingpong = PingPongTest::init();
65 pingpong.event_loop().run_for(Duration::from_secs(10));
66 }
67
68 #[test]
69 fn always_replies() {
70 let mut pingpong = PingPongTest::init();
71
72 // For now, the simulated event loop requires all references in the tasks
73 // to be `'static`, so we leak them for now until the restriction is lifted.
74 let ping_count: &Cell<i32> = Cell::new(1).leak();
75 let pong_count: &Cell<i32> = Cell::new(1).leak();
76
77 let _test_runtime = pingpong.event_loop().make_runtime("test", None, |runtime| {
78 let count_pings = async move {
79 let mut ping_watcher: Watcher<ping::Ping> = runtime.make_watcher("/test").unwrap();
80 loop {
81 let ping = ping_watcher.next().await;
82 assert_eq!(ping.message().unwrap().value(), ping_count.get());
83 ping_count.set(ping_count.get() + 1);
84 }
85 };
86 let count_pongs = async move {
87 let mut pong_watcher: Watcher<pong::Pong> = runtime.make_watcher("/test").unwrap();
88 loop {
89 let pong = pong_watcher.next().await;
90 assert_eq!(pong.message().unwrap().value(), pong_count.get());
91 pong_count.set(pong_count.get() + 1);
92 }
93 };
94
95 runtime.spawn(async move {
96 futures::join!(count_pings, count_pongs);
97 unreachable!();
98 });
99 });
100
101 pingpong.event_loop().run_for(Duration::from_secs(10));
102
103 // We run at t=0 and t=10 seconds, which means we run 1 extra time (Note that we started
104 // the count at 1, not 0).
105 assert_eq!(ping_count.get(), 1002);
106 assert_eq!(pong_count.get(), 1002);
107 }
108}