1use std::collections::VecDeque;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10use crate::Model;
11use crate::command::Cmd;
12use crate::message::{BatchMsg, Message, QuitMsg, SequenceMsg};
13
14#[derive(Debug, Clone, Default)]
16pub struct SimulationStats {
17 pub init_calls: usize,
19 pub update_calls: usize,
21 pub view_calls: usize,
23 pub commands_returned: usize,
25 pub quit_requested: bool,
27}
28
29pub struct ProgramSimulator<M: Model> {
60 model: M,
61 input_queue: VecDeque<Message>,
62 output_views: Vec<String>,
63 stats: SimulationStats,
64 initialized: bool,
65}
66
67impl<M: Model> ProgramSimulator<M> {
68 pub fn new(model: M) -> Self {
70 Self {
71 model,
72 input_queue: VecDeque::new(),
73 output_views: Vec::new(),
74 stats: SimulationStats::default(),
75 initialized: false,
76 }
77 }
78
79 pub fn init(&mut self) -> Option<Cmd> {
81 if self.initialized {
82 return None;
83 }
84 self.initialized = true;
85 self.stats.init_calls += 1;
86
87 let cmd = self.model.init();
89 if cmd.is_some() {
90 self.stats.commands_returned += 1;
91 }
92
93 self.stats.view_calls += 1;
95 self.output_views.push(self.model.view());
96
97 cmd
98 }
99
100 pub fn send(&mut self, msg: Message) {
102 self.input_queue.push_back(msg);
103 }
104
105 pub fn step(&mut self) -> Option<Cmd> {
109 if !self.initialized {
111 self.init();
112 }
113
114 if let Some(msg) = self.input_queue.pop_front() {
115 if msg.is::<QuitMsg>() {
117 self.stats.quit_requested = true;
118 return Some(crate::quit());
119 }
120
121 if msg.is::<BatchMsg>() {
123 if let Some(batch) = msg.downcast::<BatchMsg>() {
124 for cmd in batch.0 {
125 if let Some(result_msg) = cmd.execute() {
126 self.input_queue.push_back(result_msg);
127 }
128 }
129 }
130 self.stats.view_calls += 1;
132 self.output_views.push(self.model.view());
133 return None;
134 }
135
136 if msg.is::<SequenceMsg>() {
138 if let Some(seq) = msg.downcast::<SequenceMsg>() {
139 for cmd in seq.0 {
140 if let Some(result_msg) = cmd.execute() {
141 self.input_queue.push_back(result_msg);
142 }
143 }
144 }
145 self.stats.view_calls += 1;
147 self.output_views.push(self.model.view());
148 return None;
149 }
150
151 self.stats.update_calls += 1;
153 let cmd = self.model.update(msg);
154 if cmd.is_some() {
155 self.stats.commands_returned += 1;
156 }
157
158 self.stats.view_calls += 1;
160 self.output_views.push(self.model.view());
161
162 return cmd;
163 }
164
165 None
166 }
167
168 pub fn run_until_empty(&mut self) -> usize {
173 const MAX_ITERATIONS: usize = 1000;
174 let mut processed = 0;
175 while !self.input_queue.is_empty()
176 && !self.stats.quit_requested
177 && processed < MAX_ITERATIONS
178 {
179 if let Some(cmd) = self.step() {
180 if let Some(msg) = cmd.execute() {
182 self.input_queue.push_back(msg);
183 }
184 }
185 processed += 1;
186 }
187 processed
188 }
189
190 pub fn run_until_quit(&mut self, max_steps: usize) -> usize {
194 let mut steps = 0;
195 while steps < max_steps && !self.stats.quit_requested {
196 if self.input_queue.is_empty() {
197 break;
198 }
199 if let Some(cmd) = self.step() {
200 if let Some(msg) = cmd.execute() {
202 self.input_queue.push_back(msg);
203 }
204 }
205 steps += 1;
206 }
207 steps
208 }
209
210 pub fn model(&self) -> &M {
212 &self.model
213 }
214
215 pub fn model_mut(&mut self) -> &mut M {
217 &mut self.model
218 }
219
220 pub fn into_model(self) -> M {
222 self.model
223 }
224
225 pub fn stats(&self) -> &SimulationStats {
227 &self.stats
228 }
229
230 pub fn views(&self) -> &[String] {
232 &self.output_views
233 }
234
235 pub fn last_view(&self) -> Option<&str> {
237 self.output_views.last().map(String::as_str)
238 }
239
240 pub fn is_quit(&self) -> bool {
242 self.stats.quit_requested
243 }
244
245 pub fn is_initialized(&self) -> bool {
247 self.initialized
248 }
249
250 pub fn pending_count(&self) -> usize {
252 self.input_queue.len()
253 }
254
255 pub fn sim_key(&mut self, c: char) {
279 use crate::key::KeyMsg;
280 self.send(Message::new(KeyMsg::from_char(c)));
281 }
282
283 pub fn sim_key_type(&mut self, key_type: crate::key::KeyType) {
285 use crate::key::KeyMsg;
286 self.send(Message::new(KeyMsg::from_type(key_type)));
287 }
288
289 pub fn sim_mouse(
298 &mut self,
299 x: u16,
300 y: u16,
301 button: crate::mouse::MouseButton,
302 action: crate::mouse::MouseAction,
303 ) {
304 use crate::mouse::MouseMsg;
305 self.send(Message::new(MouseMsg {
306 x,
307 y,
308 button,
309 action,
310 shift: false,
311 alt: false,
312 ctrl: false,
313 }));
314 }
315
316 pub fn sim_resize(&mut self, width: u16, height: u16) {
318 use crate::message::WindowSizeMsg;
319 self.send(Message::new(WindowSizeMsg { width, height }));
320 }
321
322 pub fn sim_paste(&mut self, text: &str) {
324 use crate::key::KeyMsg;
325 let runes: Vec<char> = text.chars().collect();
326 self.send(Message::new(KeyMsg::from_runes(runes).with_paste()));
327 }
328}
329
330pub struct TrackingModel {
335 pub init_count: Arc<AtomicUsize>,
337 pub update_count: Arc<AtomicUsize>,
339 pub view_count: Arc<AtomicUsize>,
341 pub value: i32,
343}
344
345impl TrackingModel {
346 pub fn new() -> Self {
348 Self {
349 init_count: Arc::new(AtomicUsize::new(0)),
350 update_count: Arc::new(AtomicUsize::new(0)),
351 view_count: Arc::new(AtomicUsize::new(0)),
352 value: 0,
353 }
354 }
355
356 pub fn with_counters(
358 init_count: Arc<AtomicUsize>,
359 update_count: Arc<AtomicUsize>,
360 view_count: Arc<AtomicUsize>,
361 ) -> Self {
362 Self {
363 init_count,
364 update_count,
365 view_count,
366 value: 0,
367 }
368 }
369
370 pub fn init_calls(&self) -> usize {
372 self.init_count.load(Ordering::SeqCst)
373 }
374
375 pub fn update_calls(&self) -> usize {
377 self.update_count.load(Ordering::SeqCst)
378 }
379
380 pub fn view_calls(&self) -> usize {
382 self.view_count.load(Ordering::SeqCst)
383 }
384}
385
386impl Default for TrackingModel {
387 fn default() -> Self {
388 Self::new()
389 }
390}
391
392impl Model for TrackingModel {
393 fn init(&self) -> Option<Cmd> {
394 self.init_count.fetch_add(1, Ordering::SeqCst);
395 None
396 }
397
398 fn update(&mut self, msg: Message) -> Option<Cmd> {
399 self.update_count.fetch_add(1, Ordering::SeqCst);
400
401 if let Some(n) = msg.downcast::<i32>() {
403 self.value += n;
404 }
405
406 None
407 }
408
409 fn view(&self) -> String {
410 self.view_count.fetch_add(1, Ordering::SeqCst);
411 format!("Value: {}", self.value)
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 #[test]
420 fn test_simulator_init_called_once() {
421 let model = TrackingModel::new();
422 let init_count = model.init_count.clone();
423
424 let mut sim = ProgramSimulator::new(model);
425
426 assert_eq!(init_count.load(Ordering::SeqCst), 0);
428
429 sim.init();
431 assert_eq!(init_count.load(Ordering::SeqCst), 1);
432
433 sim.init();
435 assert_eq!(init_count.load(Ordering::SeqCst), 1);
436 }
437
438 #[test]
439 fn test_simulator_view_called_after_init() {
440 let model = TrackingModel::new();
441 let view_count = model.view_count.clone();
442
443 let mut sim = ProgramSimulator::new(model);
444 sim.init();
445
446 assert_eq!(view_count.load(Ordering::SeqCst), 1);
448 assert_eq!(sim.views().len(), 1);
449 assert_eq!(sim.last_view(), Some("Value: 0"));
450 }
451
452 #[test]
453 fn test_simulator_update_increments_value() {
454 let model = TrackingModel::new();
455 let mut sim = ProgramSimulator::new(model);
456
457 sim.init();
458 sim.send(Message::new(5));
459 sim.send(Message::new(3));
460 sim.step();
461 sim.step();
462
463 assert_eq!(sim.model().value, 8);
464 assert_eq!(sim.stats().update_calls, 2);
465 }
466
467 #[test]
468 fn test_simulator_view_called_after_each_update() {
469 let model = TrackingModel::new();
470 let view_count = model.view_count.clone();
471
472 let mut sim = ProgramSimulator::new(model);
473 sim.init();
474
475 assert_eq!(view_count.load(Ordering::SeqCst), 1);
477
478 sim.send(Message::new(1));
479 sim.step();
480 assert_eq!(view_count.load(Ordering::SeqCst), 2);
482
483 sim.send(Message::new(2));
484 sim.step();
485 assert_eq!(view_count.load(Ordering::SeqCst), 3);
487 }
488
489 #[test]
490 fn test_simulator_quit_stops_processing() {
491 let model = TrackingModel::new();
492 let mut sim = ProgramSimulator::new(model);
493
494 sim.init();
495 sim.send(Message::new(1));
496 sim.send(Message::new(QuitMsg));
497 sim.send(Message::new(2)); sim.run_until_quit(10);
500
501 assert!(sim.is_quit());
502 assert_eq!(sim.model().value, 1); }
504
505 #[test]
506 fn test_simulator_run_until_empty() {
507 let model = TrackingModel::new();
508 let mut sim = ProgramSimulator::new(model);
509
510 sim.init();
511 sim.send(Message::new(1));
512 sim.send(Message::new(2));
513 sim.send(Message::new(3));
514
515 let processed = sim.run_until_empty();
516
517 assert_eq!(processed, 3);
518 assert_eq!(sim.model().value, 6);
519 }
520
521 #[test]
522 fn test_simulator_stats() {
523 let model = TrackingModel::new();
524 let mut sim = ProgramSimulator::new(model);
525
526 sim.init();
527 sim.send(Message::new(1));
528 sim.send(Message::new(2));
529 sim.step();
530 sim.step();
531
532 let stats = sim.stats();
533 assert_eq!(stats.init_calls, 1);
534 assert_eq!(stats.update_calls, 2);
535 assert_eq!(stats.view_calls, 3); assert!(!stats.quit_requested);
537 }
538
539 #[test]
540 fn test_simulator_into_model() {
541 let model = TrackingModel::new();
542 let mut sim = ProgramSimulator::new(model);
543
544 sim.init();
545 sim.send(Message::new(42));
546 sim.step();
547
548 let final_model = sim.into_model();
549 assert_eq!(final_model.value, 42);
550 }
551
552 #[test]
553 fn test_simulator_implicit_init() {
554 let model = TrackingModel::new();
555 let init_count = model.init_count.clone();
556
557 let mut sim = ProgramSimulator::new(model);
558
559 sim.send(Message::new(1));
561 sim.step();
562
563 assert_eq!(init_count.load(Ordering::SeqCst), 1);
564 assert!(sim.is_initialized());
565 }
566
567 #[test]
568 fn test_simulator_batch_command() {
569 use crate::batch;
570
571 struct BatchTrigger;
573 #[derive(Clone, Copy)]
574 struct SetValue(i32);
575 #[derive(Clone, Copy)]
576 struct AddValue(i32);
577
578 struct BatchModel {
579 value: i32,
580 }
581
582 impl Model for BatchModel {
583 fn init(&self) -> Option<crate::Cmd> {
584 None
585 }
586
587 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
588 if msg.is::<BatchTrigger>() {
589 return batch(vec![
591 Some(crate::Cmd::new(|| Message::new(SetValue(10)))),
592 Some(crate::Cmd::new(|| Message::new(AddValue(5)))),
593 ]);
594 }
595 if let Some(SetValue(v)) = msg.downcast_ref::<SetValue>() {
596 self.value = *v;
597 } else if let Some(AddValue(v)) = msg.downcast_ref::<AddValue>() {
598 self.value += *v;
599 }
600 None
601 }
602
603 fn view(&self) -> String {
604 format!("Value: {}", self.value)
605 }
606 }
607
608 let mut sim = ProgramSimulator::new(BatchModel { value: 0 });
609 sim.init();
610
611 sim.send(Message::new(BatchTrigger));
613
614 let cmd = sim.step();
616 assert!(cmd.is_some(), "Should return batch command");
617
618 let batch_msg = cmd.unwrap().execute();
620 assert!(batch_msg.is_some(), "Batch command should return BatchMsg");
621
622 sim.send(batch_msg.unwrap());
624
625 sim.run_until_empty();
627
628 assert_eq!(
630 sim.model().value,
631 15,
632 "Batch commands should set 10 then add 5"
633 );
634 }
635
636 #[test]
641 fn test_sim_key_sends_char() {
642 use crate::key::{KeyMsg, KeyType};
643
644 struct KeyModel {
645 keys: Vec<char>,
646 }
647
648 impl Model for KeyModel {
649 fn init(&self) -> Option<crate::Cmd> {
650 None
651 }
652 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
653 if let Some(key) = msg.downcast_ref::<KeyMsg>()
654 && key.key_type == KeyType::Runes
655 {
656 self.keys.extend(&key.runes);
657 }
658 None
659 }
660 fn view(&self) -> String {
661 format!("Keys: {:?}", self.keys)
662 }
663 }
664
665 let mut sim = ProgramSimulator::new(KeyModel { keys: Vec::new() });
666 sim.init();
667 sim.sim_key('a');
668 sim.sim_key('b');
669 sim.sim_key('c');
670 sim.run_until_empty();
671
672 assert_eq!(sim.model().keys, vec!['a', 'b', 'c']);
673 }
674
675 #[test]
676 fn test_sim_key_type_sends_special_keys() {
677 use crate::key::{KeyMsg, KeyType};
678
679 struct KeyModel {
680 special_keys: Vec<KeyType>,
681 }
682
683 impl Model for KeyModel {
684 fn init(&self) -> Option<crate::Cmd> {
685 None
686 }
687 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
688 if let Some(key) = msg.downcast_ref::<KeyMsg>()
689 && key.key_type != KeyType::Runes
690 {
691 self.special_keys.push(key.key_type);
692 }
693 None
694 }
695 fn view(&self) -> String {
696 format!("Keys: {:?}", self.special_keys)
697 }
698 }
699
700 let mut sim = ProgramSimulator::new(KeyModel {
701 special_keys: Vec::new(),
702 });
703 sim.init();
704 sim.sim_key_type(KeyType::Enter);
705 sim.sim_key_type(KeyType::Esc);
706 sim.sim_key_type(KeyType::Tab);
707 sim.sim_key_type(KeyType::Up);
708 sim.sim_key_type(KeyType::Down);
709 sim.run_until_empty();
710
711 assert_eq!(
712 sim.model().special_keys,
713 vec![
714 KeyType::Enter,
715 KeyType::Esc,
716 KeyType::Tab,
717 KeyType::Up,
718 KeyType::Down,
719 ]
720 );
721 }
722
723 #[test]
724 fn test_sim_mouse_sends_clicks() {
725 use crate::mouse::{MouseAction, MouseButton, MouseMsg};
726
727 struct MouseModel {
728 clicks: Vec<(u16, u16)>,
729 }
730
731 impl Model for MouseModel {
732 fn init(&self) -> Option<crate::Cmd> {
733 None
734 }
735 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
736 if let Some(mouse) = msg.downcast_ref::<MouseMsg>()
737 && mouse.button == MouseButton::Left
738 && mouse.action == MouseAction::Press
739 {
740 self.clicks.push((mouse.x, mouse.y));
741 }
742 None
743 }
744 fn view(&self) -> String {
745 format!("Clicks: {:?}", self.clicks)
746 }
747 }
748
749 let mut sim = ProgramSimulator::new(MouseModel { clicks: Vec::new() });
750 sim.init();
751 sim.sim_mouse(10, 5, MouseButton::Left, MouseAction::Press);
752 sim.sim_mouse(20, 15, MouseButton::Left, MouseAction::Press);
753 sim.run_until_empty();
754
755 assert_eq!(sim.model().clicks, vec![(10, 5), (20, 15)]);
756 }
757
758 #[test]
759 fn test_sim_resize_sends_dimensions() {
760 use crate::message::WindowSizeMsg;
761
762 struct SizeModel {
763 width: u16,
764 height: u16,
765 }
766
767 impl Model for SizeModel {
768 fn init(&self) -> Option<crate::Cmd> {
769 None
770 }
771 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
772 if let Some(size) = msg.downcast_ref::<WindowSizeMsg>() {
773 self.width = size.width;
774 self.height = size.height;
775 }
776 None
777 }
778 fn view(&self) -> String {
779 format!("{}x{}", self.width, self.height)
780 }
781 }
782
783 let mut sim = ProgramSimulator::new(SizeModel {
784 width: 0,
785 height: 0,
786 });
787 sim.init();
788 sim.sim_resize(120, 40);
789 sim.run_until_empty();
790
791 assert_eq!(sim.model().width, 120);
792 assert_eq!(sim.model().height, 40);
793 }
794
795 #[test]
796 fn test_sim_paste_sends_text() {
797 use crate::key::KeyMsg;
798
799 struct PasteModel {
800 pasted: String,
801 }
802
803 impl Model for PasteModel {
804 fn init(&self) -> Option<crate::Cmd> {
805 None
806 }
807 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
808 if let Some(key) = msg.downcast_ref::<KeyMsg>()
809 && key.paste
810 {
811 self.pasted = key.runes.iter().collect();
812 }
813 None
814 }
815 fn view(&self) -> String {
816 format!("Pasted: {}", self.pasted)
817 }
818 }
819
820 let mut sim = ProgramSimulator::new(PasteModel {
821 pasted: String::new(),
822 });
823 sim.init();
824 sim.sim_paste("Hello, World!");
825 sim.run_until_empty();
826
827 assert_eq!(sim.model().pasted, "Hello, World!");
828 }
829
830 #[test]
835 fn test_simulator_sequence_command() {
836 use crate::sequence;
837
838 struct SequenceTrigger;
839 #[derive(Clone, Copy)]
840 struct Append(char);
841
842 struct SequenceModel {
843 chars: String,
844 }
845
846 impl Model for SequenceModel {
847 fn init(&self) -> Option<crate::Cmd> {
848 None
849 }
850 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
851 if msg.is::<SequenceTrigger>() {
852 return sequence(vec![
853 Some(crate::Cmd::new(|| Message::new(Append('A')))),
854 Some(crate::Cmd::new(|| Message::new(Append('B')))),
855 Some(crate::Cmd::new(|| Message::new(Append('C')))),
856 ]);
857 }
858 if let Some(Append(c)) = msg.downcast_ref::<Append>() {
859 self.chars.push(*c);
860 }
861 None
862 }
863 fn view(&self) -> String {
864 self.chars.clone()
865 }
866 }
867
868 let mut sim = ProgramSimulator::new(SequenceModel {
869 chars: String::new(),
870 });
871 sim.init();
872 sim.send(Message::new(SequenceTrigger));
873
874 let cmd = sim.step();
876 assert!(cmd.is_some());
877
878 if let Some(msg) = cmd.unwrap().execute() {
880 sim.send(msg);
881 }
882
883 sim.run_until_empty();
884
885 assert_eq!(sim.model().chars, "ABC");
887 }
888
889 #[test]
894 fn test_empty_batch_does_not_panic() {
895 use crate::batch;
896
897 struct EmptyBatchModel;
898
899 impl Model for EmptyBatchModel {
900 fn init(&self) -> Option<crate::Cmd> {
901 batch(vec![])
903 }
904 fn update(&mut self, _: Message) -> Option<crate::Cmd> {
905 None
906 }
907 fn view(&self) -> String {
908 "ok".to_string()
909 }
910 }
911
912 let mut sim = ProgramSimulator::new(EmptyBatchModel);
913 let cmd = sim.init();
914
915 if let Some(c) = cmd {
917 let msg = c.execute();
918 if let Some(m) = msg {
919 sim.send(m);
920 sim.run_until_empty();
921 }
922 }
923
924 assert_eq!(sim.last_view(), Some("ok"));
925 }
926
927 #[test]
928 fn test_recursive_updates_bounded() {
929 struct RecursiveModel {
931 count: usize,
932 }
933
934 impl Model for RecursiveModel {
935 fn init(&self) -> Option<crate::Cmd> {
936 None
937 }
938 fn update(&mut self, msg: Message) -> Option<crate::Cmd> {
939 if let Some(&n) = msg.downcast_ref::<usize>() {
940 self.count += 1;
941 if n > 0 {
942 return Some(crate::Cmd::new(move || Message::new(n - 1)));
944 }
945 }
946 None
947 }
948 fn view(&self) -> String {
949 format!("Count: {}", self.count)
950 }
951 }
952
953 let mut sim = ProgramSimulator::new(RecursiveModel { count: 0 });
954 sim.init();
955 sim.send(Message::new(100usize)); let processed = sim.run_until_empty();
958
959 assert!(processed <= 1000);
961 assert_eq!(sim.model().count, 101); }
963
964 #[test]
965 fn test_large_message_queue() {
966 let model = TrackingModel::new();
967 let mut sim = ProgramSimulator::new(model);
968 sim.init();
969
970 for i in 0_i32..500 {
972 sim.send(Message::new(i));
973 }
974
975 let processed = sim.run_until_empty();
976
977 assert_eq!(processed, 500);
978 assert_eq!(sim.model().value, 124750);
980 }
981
982 #[test]
983 fn test_model_mut_allows_direct_modification() {
984 let model = TrackingModel::new();
985 let mut sim = ProgramSimulator::new(model);
986 sim.init();
987
988 sim.model_mut().value = 999;
990
991 assert_eq!(sim.model().value, 999);
992 }
993
994 #[test]
995 fn test_step_without_messages_returns_none() {
996 let model = TrackingModel::new();
997 let mut sim = ProgramSimulator::new(model);
998 sim.init();
999
1000 let cmd = sim.step();
1002 assert!(cmd.is_none());
1003 }
1004
1005 #[test]
1006 fn test_views_accumulate() {
1007 let model = TrackingModel::new();
1008 let mut sim = ProgramSimulator::new(model);
1009 sim.init();
1010
1011 assert_eq!(sim.views().len(), 1);
1012
1013 sim.send(Message::new(1));
1014 sim.step();
1015 assert_eq!(sim.views().len(), 2);
1016
1017 sim.send(Message::new(2));
1018 sim.step();
1019 assert_eq!(sim.views().len(), 3);
1020
1021 assert_eq!(sim.views()[0], "Value: 0");
1023 assert_eq!(sim.views()[1], "Value: 1");
1024 assert_eq!(sim.views()[2], "Value: 3");
1025 }
1026}