1use slotmap::{new_key_type, SlotMap};
9use std::cmp::Reverse;
10use std::collections::{BinaryHeap, HashMap};
11use std::io;
12use std::os::unix::io::RawFd;
13use thiserror::Error;
14
15pub const E_READ: u32 = 0x1;
21
22pub const E_WRITE: u32 = 0x2;
24
25pub const EPSILON: f64 = 1.0e-3;
27
28pub const FANOUT_UNLIMITED: i32 = -1;
30
31#[derive(Error, Debug)]
37pub enum EngineError {
38 #[error("Operation aborted (kill={kill})")]
40 Abort { kill: bool },
41
42 #[error("Operation timed out")]
44 Timeout,
45
46 #[error("Illegal operation: {0}")]
48 IllegalOperation(String),
49
50 #[error("Engine is already running")]
52 AlreadyRunning,
53
54 #[error("Not supported: {0}")]
56 NotSupported(String),
57
58 #[error("I/O error: {0}")]
60 Io(#[from] io::Error),
61}
62
63new_key_type! { pub struct TimerId; }
68
69#[derive(Debug, Clone)]
78pub struct EngineBaseTimer {
79 fire_delay: f64,
80 interval: f64,
81 autoclose: bool,
82}
83
84impl EngineBaseTimer {
85 pub fn new(fire_delay: f64, interval: f64, autoclose: bool) -> Self {
92 Self {
93 fire_delay,
94 interval,
95 autoclose,
96 }
97 }
98
99 pub fn invalidate(&mut self) {
101 self.fire_delay = -1.0;
102 }
103
104 pub fn is_valid(&self) -> bool {
106 self.fire_delay >= 0.0
107 }
108
109 pub fn set_nextfire(&mut self, fire_delay: f64, interval: f64) {
115 self.fire_delay = fire_delay;
116 self.interval = interval;
117 }
118
119 pub fn fire_delay(&self) -> f64 {
121 self.fire_delay
122 }
123
124 pub fn interval(&self) -> f64 {
126 self.interval
127 }
128
129 pub fn autoclose(&self) -> bool {
131 self.autoclose
132 }
133}
134
135#[derive(Debug, Clone, PartialEq)]
141struct TimerEntry {
142 fire_date: f64,
143 timer_id: TimerId,
144 armed: bool,
145}
146
147impl Eq for TimerEntry {}
148
149impl Ord for TimerEntry {
150 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
151 self.fire_date
153 .partial_cmp(&other.fire_date)
154 .unwrap_or(std::cmp::Ordering::Equal)
155 .then_with(|| self.timer_id.cmp(&other.timer_id))
156 }
157}
158
159impl PartialOrd for TimerEntry {
160 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
161 Some(self.cmp(other))
162 }
163}
164
165#[derive(Debug)]
170pub struct TimerQueue {
171 timers: BinaryHeap<Reverse<TimerEntry>>,
172 timer_data: SlotMap<TimerId, EngineBaseTimer>,
173 armed_count: usize,
174 current_time: f64,
175}
176
177impl TimerQueue {
178 pub fn new() -> Self {
180 Self {
181 timers: BinaryHeap::new(),
182 timer_data: SlotMap::with_capacity_and_key(16),
183 armed_count: 0,
184 current_time: 0.0,
185 }
186 }
187
188 pub fn schedule(&mut self, timer_id: TimerId) {
193 if let Some(timer) = self.timer_data.get(timer_id) {
194 if timer.fire_delay >= 0.0 {
195 let fire_date = self.current_time + timer.fire_delay;
196 let entry = TimerEntry {
197 fire_date,
198 timer_id,
199 armed: true,
200 };
201 self.timers.push(Reverse(entry));
202 self.armed_count += 1;
203 }
204 }
205 }
206
207 pub fn reschedule(&mut self, timer_id: TimerId) {
212 self.invalidate(timer_id);
214 self.schedule(timer_id);
215 }
216
217 pub fn invalidate(&mut self, timer_id: TimerId) {
222 let mut found = false;
224 let mut new_heap = BinaryHeap::new();
225 for Reverse(entry) in self.timers.drain() {
226 if entry.timer_id == timer_id {
227 found = true;
228 if let Some(timer) = self.timer_data.get_mut(timer_id) {
230 timer.invalidate();
231 }
232 } else {
233 new_heap.push(Reverse(entry));
234 }
235 }
236 self.timers = new_heap;
237 if found {
238 self.armed_count = self.armed_count.saturating_sub(1);
239 }
240 }
241
242 pub fn fire_expired(&mut self) -> Vec<(TimerId, bool)> {
246 let mut fired = Vec::new();
247 let now = self.current_time;
248
249 while let Some(Reverse(entry)) = self.timers.peek() {
251 if entry.fire_date > now + EPSILON {
252 break;
253 }
254
255 let entry = self.timers.pop().unwrap().0;
256 let timer_id = entry.timer_id;
257
258 if let Some(timer) = self.timer_data.get(timer_id) {
259 if entry.armed && timer.fire_delay >= 0.0 {
260 fired.push((timer_id, timer.autoclose));
261
262 if timer.interval >= 0.0 {
264 let new_fire_date = now + timer.interval;
265 let new_entry = TimerEntry {
266 fire_date: new_fire_date,
267 timer_id,
268 armed: true,
269 };
270 self.timers.push(Reverse(new_entry));
271 } else {
272 if let Some(t) = self.timer_data.get_mut(timer_id) {
274 t.invalidate();
275 }
276 self.armed_count = self.armed_count.saturating_sub(1);
277 }
278 }
279 }
280 }
281
282 fired
283 }
284
285 pub fn nextfire_delay(&self) -> Option<f64> {
289 if let Some(Reverse(entry)) = self.timers.peek() {
290 let delay = entry.fire_date - self.current_time;
291 if delay > EPSILON {
292 Some(delay)
293 } else {
294 Some(0.0)
295 }
296 } else {
297 None
298 }
299 }
300
301 pub fn clear(&mut self) {
303 self.timers.clear();
304 self.timer_data.clear();
305 self.armed_count = 0;
306 }
307
308 pub fn add_timer(&mut self, timer: EngineBaseTimer) -> TimerId {
312 let timer_id = self.timer_data.insert(timer);
313 timer_id
314 }
315
316 pub fn remove_timer(&mut self, timer_id: TimerId) {
318 self.invalidate(timer_id);
319 self.timer_data.remove(timer_id);
320 }
321
322 pub fn armed_count(&self) -> usize {
324 self.armed_count
325 }
326
327 pub fn update_time(&mut self, time: f64) {
329 self.current_time = time;
330 }
331
332 pub fn is_valid(&self, timer_id: TimerId) -> bool {
334 self.timer_data
335 .get(timer_id)
336 .map(|t| t.is_valid())
337 .unwrap_or(false)
338 }
339}
340
341impl Default for TimerQueue {
342 fn default() -> Self {
343 Self::new()
344 }
345}
346
347pub struct EngineTimer {
356 base: EngineBaseTimer,
357 handler: Box<dyn FnMut() + Send>,
358}
359
360impl EngineTimer {
361 pub fn new(
369 fire_delay: f64,
370 interval: f64,
371 autoclose: bool,
372 handler: impl FnMut() + Send + 'static,
373 ) -> Self {
374 Self {
375 base: EngineBaseTimer::new(fire_delay, interval, autoclose),
376 handler: Box::new(handler),
377 }
378 }
379
380 pub fn fire(&mut self) {
382 (self.handler)();
383 }
384
385 pub fn base(&self) -> &EngineBaseTimer {
387 &self.base
388 }
389
390 pub fn base_mut(&mut self) -> &mut EngineBaseTimer {
392 &mut self.base
393 }
394}
395
396impl std::fmt::Debug for EngineTimer {
397 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398 f.debug_struct("EngineTimer")
399 .field("base", &self.base)
400 .field("handler", &"<fn>")
401 .finish()
402 }
403}
404
405pub trait EngineClient: Send {
414 fn start(&mut self) -> io::Result<()>;
416
417 fn close(&mut self, abort: bool, timeout: bool);
423
424 fn fd(&self) -> Option<RawFd>;
426
427 fn events(&self) -> u32;
429
430 fn is_delayable(&self) -> bool;
432
433 fn is_registered(&self) -> bool;
435
436 fn set_registered(&mut self, val: bool);
438
439 fn autoclose(&self) -> bool;
441
442 fn handle_read(&mut self) -> io::Result<usize> {
445 Ok(0)
446 }
447
448 fn handle_write(&mut self) -> io::Result<usize> {
451 Ok(0)
452 }
453}
454
455new_key_type! { pub struct ClientId; }
460
461pub trait PollBackend: Send + Sync {
470 fn register(&mut self, fd: RawFd, events: u32, client_id: ClientId) -> io::Result<()>;
477
478 fn modify(&mut self, fd: RawFd, events: u32, client_id: ClientId) -> io::Result<()>;
480
481 fn unregister(&mut self, fd: RawFd) -> io::Result<()>;
483
484 fn wait(&mut self, timeout: Option<f64>) -> io::Result<Vec<(RawFd, u32, ClientId)>>;
488
489 fn name(&self) -> &str;
491}
492
493pub struct Engine<B: PollBackend> {
514 clients: SlotMap<ClientId, Box<dyn EngineClient>>,
515 timerq: TimerQueue,
516 reg_clifds: HashMap<RawFd, ClientId>,
517 poll_backend: B,
518 fanout: usize,
519 running: bool,
520 exited: bool,
521 evlooprefcnt: i32,
522}
523
524impl<B: PollBackend> Engine<B> {
525 pub fn with_backend(backend: B, fanout: usize) -> Self {
531 Self {
532 clients: SlotMap::with_key(),
533 timerq: TimerQueue::new(),
534 reg_clifds: HashMap::new(),
535 poll_backend: backend,
536 fanout,
537 running: false,
538 exited: false,
539 evlooprefcnt: 0,
540 }
541 }
542
543 pub fn add_timer(&mut self, timer: EngineBaseTimer) -> TimerId {
548 let timer_id = self.timerq.add_timer(timer);
549 self.timerq.schedule(timer_id);
550 timer_id
551 }
552
553 pub fn remove_timer(&mut self, id: TimerId) {
558 self.timerq.remove_timer(id);
559 }
560
561 pub fn fire_timers(&mut self) {
563 let _fired = self.timerq.fire_expired();
564 }
568
569 pub fn run(&mut self, timeout: Option<f64>) -> Result<(), EngineError> {
574 if self.running {
575 return Err(EngineError::AlreadyRunning);
576 }
577
578 self.running = true;
579 self.exited = false;
580
581 let start_time = self.timerq.current_time;
582 let max_time = timeout.map(|t| start_time + t);
583
584 while self.running {
585 self.timerq.update_time(self.timerq.current_time + 0.001);
587
588 self.fire_timers();
590
591 if !self.running {
593 break;
594 }
595
596 let next_timer_delay = self.timerq.nextfire_delay();
598
599 let poll_timeout = match (max_time, next_timer_delay) {
601 (Some(max), Some(timer_delay)) => {
602 let remaining = max - self.timerq.current_time;
603 if remaining <= EPSILON {
604 return Err(EngineError::Timeout);
605 }
606 Some(if remaining < timer_delay {
608 remaining
609 } else {
610 timer_delay
611 })
612 }
613 (Some(max), None) => {
614 let remaining = max - self.timerq.current_time;
615 if remaining <= EPSILON {
616 return Err(EngineError::Timeout);
617 }
618 Some(remaining)
619 }
620 (None, Some(timer_delay)) => Some(timer_delay),
621 (None, None) => None,
622 };
623
624 let events = self.poll_backend.wait(poll_timeout.map(|t| t.max(0.0)))?;
626
627 for (_fd, events_mask, client_id) in events {
629 if let Some(client) = self.clients.get_mut(client_id) {
630 if events_mask & E_READ != 0 {
631 let _ = client.handle_read();
632 }
633 if events_mask & E_WRITE != 0 {
634 let _ = client.handle_write();
635 }
636 }
637 }
638
639 if let Some(max) = max_time {
641 if self.timerq.current_time >= max {
642 return Err(EngineError::Timeout);
643 }
644 }
645 }
646
647 self.exited = true;
648 self.running = false;
649 Ok(())
650 }
651
652 pub fn abort(&mut self, kill: bool) {
657 if kill {
658 for (_, client) in self.clients.iter_mut() {
660 client.close(true, false);
661 }
662 }
663 self.running = false;
664 }
665
666 pub fn is_running(&self) -> bool {
668 self.running
669 }
670
671 pub fn exited(&self) -> bool {
673 self.exited
674 }
675
676 pub fn add_client(&mut self, client: Box<dyn EngineClient>) -> Result<ClientId, EngineError> {
681 let client_id = self.clients.insert(client);
682
683 if let Some(client) = self.clients.get(client_id) {
684 if let Some(fd) = client.fd() {
685 if fd >= 0 {
686 let events = client.events();
687 self.poll_backend.register(fd, events, client_id)?;
688 self.reg_clifds.insert(fd, client_id);
689 }
690 }
691 }
692
693 Ok(client_id)
694 }
695
696 pub fn remove_client(&mut self, id: ClientId) {
701 if let Some(client) = self.clients.get(id) {
702 if let Some(fd) = client.fd() {
703 if fd >= 0 {
704 let _ = self.poll_backend.unregister(fd);
705 self.reg_clifds.remove(&fd);
706 }
707 }
708 }
709 self.clients.remove(id);
710 }
711
712 pub fn fanout(&self) -> usize {
714 self.fanout
715 }
716
717 pub fn update_client_events(&mut self, client_id: ClientId) -> Result<(), EngineError> {
719 if let Some(client) = self.clients.get(client_id) {
720 if let Some(fd) = client.fd() {
721 if fd >= 0 {
722 let events = client.events();
723 if self.reg_clifds.contains_key(&fd) {
724 self.poll_backend.modify(fd, events, client_id)?;
725 } else {
726 self.poll_backend.register(fd, events, client_id)?;
727 self.reg_clifds.insert(fd, client_id);
728 }
729 }
730 }
731 }
732 Ok(())
733 }
734
735 pub fn stop(&mut self) {
737 self.running = false;
738 }
739
740 pub fn evloop_acquire(&mut self) {
743 self.evlooprefcnt += 1;
744 }
745
746 pub fn evloop_release(&mut self) {
749 self.evlooprefcnt -= 1;
750 if self.evlooprefcnt <= 0 {
751 self.running = false;
752 }
753 }
754
755 pub fn client_count(&self) -> usize {
757 self.clients.len()
758 }
759
760 pub fn timerq(&self) -> &TimerQueue {
762 &self.timerq
763 }
764
765 pub fn timerq_mut(&mut self) -> &mut TimerQueue {
767 &mut self.timerq
768 }
769
770 pub fn start_all_clients(&mut self) -> io::Result<()> {
772 let ids: Vec<ClientId> = self.clients.keys().collect();
773 for id in ids {
774 if let Some(client) = self.clients.get_mut(id) {
775 client.start()?;
776 }
777 }
778 Ok(())
779 }
780}
781
782#[derive(Debug, Default)]
790pub struct NullBackend;
791
792impl PollBackend for NullBackend {
793 fn register(&mut self, _fd: RawFd, _events: u32, _client_id: ClientId) -> io::Result<()> {
794 Ok(())
795 }
796
797 fn modify(&mut self, _fd: RawFd, _events: u32, _client_id: ClientId) -> io::Result<()> {
798 Ok(())
799 }
800
801 fn unregister(&mut self, _fd: RawFd) -> io::Result<()> {
802 Ok(())
803 }
804
805 fn wait(&mut self, _timeout: Option<f64>) -> io::Result<Vec<(RawFd, u32, ClientId)>> {
806 Ok(Vec::new())
807 }
808
809 fn name(&self) -> &str {
810 "null"
811 }
812}
813
814#[cfg(test)]
819mod tests {
820 use super::*;
821
822 #[test]
823 fn test_engine_constants() {
824 assert_eq!(E_READ, 0x1);
825 assert_eq!(E_WRITE, 0x2);
826 assert!((EPSILON - 1.0e-3).abs() < EPSILON);
827 assert_eq!(FANOUT_UNLIMITED, -1);
828 }
829
830 #[test]
831 fn test_engine_base_timer() {
832 let mut timer = EngineBaseTimer::new(5.0, -1.0, false);
833 assert!(timer.is_valid());
834 assert_eq!(timer.fire_delay(), 5.0);
835 assert_eq!(timer.interval(), -1.0);
836
837 timer.invalidate();
838 assert!(!timer.is_valid());
839 assert_eq!(timer.fire_delay(), -1.0);
840
841 timer.set_nextfire(10.0, 2.0);
842 assert!(timer.is_valid());
843 assert_eq!(timer.fire_delay(), 10.0);
844 assert_eq!(timer.interval(), 2.0);
845 }
846
847 #[test]
848 fn test_timer_queue_scheduling() {
849 let mut timerq = TimerQueue::new();
850
851 let timer1 = timerq.add_timer(EngineBaseTimer::new(1.0, -1.0, false));
853 let timer2 = timerq.add_timer(EngineBaseTimer::new(2.0, -1.0, false));
854 let timer3 = timerq.add_timer(EngineBaseTimer::new(0.5, -1.0, false));
855
856 timerq.schedule(timer1);
858 timerq.schedule(timer2);
859 timerq.schedule(timer3);
860
861 assert_eq!(timerq.armed_count(), 3);
863
864 let delay = timerq.nextfire_delay();
866 assert!(delay.is_some());
867 assert!((delay.unwrap() - 0.5).abs() < EPSILON);
868 }
869
870 #[test]
871 fn test_timer_queue_firing() {
872 let mut timerq = TimerQueue::new();
873
874 let timer1 = timerq.add_timer(EngineBaseTimer::new(0.1, -1.0, false));
876 let timer2 = timerq.add_timer(EngineBaseTimer::new(0.2, -1.0, false));
877
878 timerq.schedule(timer1);
879 timerq.schedule(timer2);
880
881 timerq.update_time(0.3);
883
884 let fired = timerq.fire_expired();
886
887 assert_eq!(fired.len(), 2);
889
890 assert!(!timerq.is_valid(timer1));
892 assert!(!timerq.is_valid(timer2));
893 }
894
895 #[test]
896 fn test_timer_queue_invalidation() {
897 let mut timerq = TimerQueue::new();
898
899 let timer1 = timerq.add_timer(EngineBaseTimer::new(1.0, -1.0, false));
900 let timer2 = timerq.add_timer(EngineBaseTimer::new(2.0, -1.0, false));
901
902 timerq.schedule(timer1);
903 timerq.schedule(timer2);
904
905 timerq.invalidate(timer1);
907
908 assert_eq!(timerq.armed_count(), 1);
910
911 let delay = timerq.nextfire_delay();
913 assert!(delay.is_some());
914 assert!((delay.unwrap() - 2.0).abs() < EPSILON);
915 }
916
917 #[test]
918 fn test_timer_queue_repeat() {
919 let mut timerq = TimerQueue::new();
920
921 let timer = timerq.add_timer(EngineBaseTimer::new(0.1, 0.1, false));
923 timerq.schedule(timer);
924
925 timerq.update_time(0.15);
927 let fired = timerq.fire_expired();
928 assert_eq!(fired.len(), 1);
929
930 assert!(timerq.is_valid(timer));
932
933 timerq.update_time(0.25);
935 let fired = timerq.fire_expired();
936 assert_eq!(fired.len(), 1);
937
938 assert!(timerq.is_valid(timer));
940 }
941
942 #[test]
943 fn test_engine_error_types() {
944 let err = EngineError::Abort { kill: true };
945 assert!(matches!(err, EngineError::Abort { kill: true }));
946
947 let err = EngineError::Timeout;
948 assert!(matches!(err, EngineError::Timeout));
949
950 let err = EngineError::IllegalOperation("test".to_string());
951 assert!(matches!(err, EngineError::IllegalOperation(_)));
952
953 let err = EngineError::AlreadyRunning;
954 assert!(matches!(err, EngineError::AlreadyRunning));
955
956 let err = EngineError::NotSupported("test".to_string());
957 assert!(matches!(err, EngineError::NotSupported(_)));
958
959 let io_err = io::Error::new(io::ErrorKind::NotFound, "not found");
960 let err = EngineError::Io(io_err);
961 assert!(matches!(err, EngineError::Io(_)));
962 }
963
964 #[test]
967 fn test_null_backend_name() {
968 let nb = NullBackend;
969 assert_eq!(nb.name(), "null");
970 }
971
972 #[test]
973 fn test_null_backend_wait_returns_empty() {
974 let mut nb = NullBackend;
975 let events = nb.wait(Some(0.0)).unwrap();
976 assert!(events.is_empty());
977 }
978
979 #[test]
980 fn test_null_backend_register_modify_unregister() {
981 let mut nb = NullBackend;
982 let mut sm: SlotMap<ClientId, ()> = SlotMap::with_key();
985 let cid = sm.insert(());
986 nb.register(0, E_READ, cid).unwrap();
987 nb.modify(0, E_WRITE, cid).unwrap();
988 nb.unregister(0).unwrap();
989 }
990
991 struct DummyClient {
994 registered: bool,
995 started: bool,
996 }
997
998 impl DummyClient {
999 fn new() -> Self {
1000 Self {
1001 registered: false,
1002 started: false,
1003 }
1004 }
1005 }
1006
1007 impl EngineClient for DummyClient {
1008 fn start(&mut self) -> io::Result<()> {
1009 self.started = true;
1010 Ok(())
1011 }
1012
1013 fn close(&mut self, _abort: bool, _timeout: bool) {}
1014
1015 fn fd(&self) -> Option<RawFd> {
1016 None }
1018
1019 fn events(&self) -> u32 {
1020 E_READ
1021 }
1022
1023 fn is_delayable(&self) -> bool {
1024 false
1025 }
1026
1027 fn is_registered(&self) -> bool {
1028 self.registered
1029 }
1030
1031 fn set_registered(&mut self, val: bool) {
1032 self.registered = val;
1033 }
1034
1035 fn autoclose(&self) -> bool {
1036 false
1037 }
1038 }
1039
1040 #[test]
1041 fn test_engine_creation() {
1042 let engine = Engine::with_backend(NullBackend, 0);
1043 assert!(!engine.is_running());
1044 assert!(!engine.exited());
1045 assert_eq!(engine.fanout(), 0);
1046 assert_eq!(engine.client_count(), 0);
1047 }
1048
1049 #[test]
1050 fn test_engine_add_remove_client() {
1051 let mut engine = Engine::with_backend(NullBackend, 0);
1052 let cid = engine.add_client(Box::new(DummyClient::new())).unwrap();
1053 assert_eq!(engine.client_count(), 1);
1054 engine.remove_client(cid);
1055 assert_eq!(engine.client_count(), 0);
1056 }
1057
1058 #[test]
1059 fn test_engine_add_timer() {
1060 let mut engine = Engine::with_backend(NullBackend, 0);
1061 let tid = engine.add_timer(EngineBaseTimer::new(1.0, -1.0, false));
1062 assert!(engine.timerq().is_valid(tid));
1063 engine.remove_timer(tid);
1064 assert!(!engine.timerq().is_valid(tid));
1065 }
1066
1067 #[test]
1068 fn test_engine_fire_timers() {
1069 let mut engine = Engine::with_backend(NullBackend, 0);
1070 let tid = engine.add_timer(EngineBaseTimer::new(0.1, -1.0, false));
1071 assert!(engine.timerq().is_valid(tid));
1072 engine.timerq_mut().update_time(1.0);
1073 engine.fire_timers();
1074 assert!(!engine.timerq().is_valid(tid));
1075 }
1076
1077 #[test]
1078 fn test_engine_abort() {
1079 let mut engine = Engine::with_backend(NullBackend, 0);
1080 engine.abort(false);
1081 assert!(!engine.is_running());
1082 }
1083
1084 #[test]
1085 fn test_engine_evloop_refcnt() {
1086 let mut engine = Engine::with_backend(NullBackend, 0);
1087 engine.evloop_acquire();
1088 engine.evloop_acquire();
1089 engine.evloop_release();
1090 engine.evloop_release();
1093 assert!(!engine.is_running());
1095 }
1096
1097 #[test]
1098 fn test_engine_stop() {
1099 let mut engine = Engine::with_backend(NullBackend, 0);
1100 engine.stop();
1102 assert!(!engine.is_running());
1103 }
1104
1105 #[test]
1106 fn test_engine_start_all_clients() {
1107 let mut engine = Engine::with_backend(NullBackend, 0);
1108 engine.add_client(Box::new(DummyClient::new())).unwrap();
1109 engine.add_client(Box::new(DummyClient::new())).unwrap();
1110 engine.start_all_clients().unwrap();
1111 assert_eq!(engine.client_count(), 2);
1112 }
1113
1114 #[test]
1115 fn test_engine_update_client_events() {
1116 let mut engine = Engine::with_backend(NullBackend, 0);
1117 let cid = engine.add_client(Box::new(DummyClient::new())).unwrap();
1118 engine.update_client_events(cid).unwrap();
1120 }
1121
1122 #[test]
1125 fn test_engine_timer_creation() {
1126 let timer = EngineTimer::new(1.0, -1.0, false, || {});
1127 assert!(timer.base().is_valid());
1128 assert_eq!(timer.base().fire_delay(), 1.0);
1129 assert_eq!(timer.base().interval(), -1.0);
1130 assert!(!timer.base().autoclose());
1131 }
1132
1133 #[test]
1134 fn test_engine_timer_fire_calls_handler() {
1135 use std::sync::{
1136 atomic::{AtomicUsize, Ordering},
1137 Arc,
1138 };
1139 let counter = Arc::new(AtomicUsize::new(0));
1140 let counter_clone = counter.clone();
1141
1142 let mut timer = EngineTimer::new(0.5, -1.0, false, move || {
1143 counter_clone.fetch_add(1, Ordering::SeqCst);
1144 });
1145
1146 assert_eq!(counter.load(Ordering::SeqCst), 0);
1147 timer.fire();
1148 assert_eq!(counter.load(Ordering::SeqCst), 1);
1149 timer.fire();
1150 assert_eq!(counter.load(Ordering::SeqCst), 2);
1151 }
1152
1153 #[test]
1154 fn test_engine_timer_invalidate() {
1155 let mut timer = EngineTimer::new(1.0, -1.0, false, || {});
1156 assert!(timer.base().is_valid());
1157 timer.base_mut().invalidate();
1158 assert!(!timer.base().is_valid());
1159 }
1160
1161 #[test]
1162 fn test_engine_timer_debug() {
1163 let timer = EngineTimer::new(1.0, -1.0, false, || {});
1164 let debug_str = format!("{:?}", timer);
1165 assert!(debug_str.contains("EngineTimer"));
1166 assert!(debug_str.contains("<fn>"));
1167 }
1168
1169 #[test]
1170 fn test_engine_timer_with_autoclose() {
1171 let timer = EngineTimer::new(2.0, 0.5, true, || {});
1172 assert!(timer.base().autoclose());
1173 assert_eq!(timer.base().interval(), 0.5);
1174 }
1175}