boxmux_lib/
thread_manager.rs

1use crate::model::app::AppContext;
2use crate::model::muxbox::Choice;
3use crate::{run_script, run_script_with_pty_and_redirect, FieldUpdate, MuxBox, Updatable};
4use bincode;
5use log::error;
6use std::collections::hash_map::DefaultHasher;
7use std::collections::HashMap;
8use std::hash::{Hash, Hasher};
9use std::sync::mpsc::{self, Sender};
10use std::thread;
11use uuid::Uuid;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum Message {
15    Exit,
16    Terminate,
17    Pause,
18    Start,
19    NextMuxBox(),
20    PreviousMuxBox(),
21    ScrollMuxBoxDown(),
22    ScrollMuxBoxUp(),
23    ScrollMuxBoxLeft(),
24    ScrollMuxBoxRight(),
25    ScrollMuxBoxPageUp(),
26    ScrollMuxBoxPageDown(),
27    ScrollMuxBoxPageLeft(),
28    ScrollMuxBoxPageRight(),
29    ScrollMuxBoxToBeginning(), // Home key - scroll to beginning horizontally
30    ScrollMuxBoxToEnd(),       // End key - scroll to end horizontally
31    ScrollMuxBoxToTop(),       // Ctrl+Home - scroll to top vertically
32    ScrollMuxBoxToBottom(),    // Ctrl+End - scroll to bottom vertically
33    CopyFocusedMuxBoxContent(),
34    Resize,
35    RedrawMuxBox(String),
36    RedrawApp,
37    RedrawAppDiff, // Redraw entire app using diff-based rendering (no screen clear)
38    MuxBoxEventRefresh(String),
39    MuxBoxOutputUpdate(String, bool, String),
40    MuxBoxScriptUpdate(String, Vec<String>),
41    ReplaceMuxBox(String, MuxBox),
42    StopBoxRefresh(String),
43    StartBoxRefresh(String),
44    SwitchActiveLayout(String),
45    KeyPress(String),
46    ExecuteHotKeyChoice(String),
47    MouseClick(u16, u16),                               // x, y coordinates
48    MouseDragStart(u16, u16),                           // x, y coordinates - start drag
49    MouseDrag(u16, u16),                                // x, y coordinates - continue drag
50    MouseDragEnd(u16, u16),                             // x, y coordinates - end drag
51    MuxBoxBorderDrag(String, u16, u16), // muxbox_id, x, y coordinates - resize muxbox
52    MuxBoxResizeComplete(String),       // muxbox_id - save changes to YAML
53    MuxBoxMove(String, u16, u16),       // muxbox_id, x, y coordinates - move muxbox
54    MuxBoxMoveComplete(String),         // muxbox_id - save position changes to YAML
55    SaveYamlState,                      // F0200: Trigger complete YAML state persistence
56    SaveActiveLayout(String),           // F0200: Save active layout to YAML
57    SaveMuxBoxContent(String, String),  // F0200: Save muxbox content to YAML
58    SaveMuxBoxScroll(String, usize, usize), // F0200: Save muxbox scroll position
59    PTYInput(String, String),           // muxbox_id, input_text
60    ExecuteChoice(Choice, String, Option<Vec<String>>), // choice, muxbox_id, libs
61    ChoiceExecutionComplete(String, String, Result<String, String>), // choice_id, muxbox_id, result
62    ExternalMessage(String),
63    AddBox(String, MuxBox),
64    RemoveBox(String),
65}
66
67impl Hash for Message {
68    fn hash<H: Hasher>(&self, state: &mut H) {
69        match self {
70            Message::Exit => "exit".hash(state),
71            Message::Terminate => "terminate".hash(state),
72            Message::NextMuxBox() => "next_muxbox".hash(state),
73            Message::PreviousMuxBox() => "previous_muxbox".hash(state),
74            Message::Resize => "resize".hash(state),
75            Message::RedrawMuxBox(muxbox_id) => {
76                "redraw_muxbox".hash(state);
77                muxbox_id.hash(state);
78            }
79            Message::RedrawApp => "redraw_app".hash(state),
80            Message::RedrawAppDiff => "redraw_app_diff".hash(state),
81            Message::SwitchActiveLayout(layout_id) => {
82                "switch_active_layout".hash(state);
83                layout_id.hash(state);
84            }
85            Message::MuxBoxEventRefresh(muxbox_id) => {
86                "muxbox_event_refresh".hash(state);
87                muxbox_id.hash(state);
88            }
89            Message::ScrollMuxBoxDown() => "scroll_muxbox_down".hash(state),
90            Message::ScrollMuxBoxUp() => "scroll_muxbox_up".hash(state),
91            Message::ScrollMuxBoxLeft() => "scroll_muxbox_left".hash(state),
92            Message::ScrollMuxBoxRight() => "scroll_muxbox_right".hash(state),
93            Message::ScrollMuxBoxPageUp() => "scroll_muxbox_page_up".hash(state),
94            Message::ScrollMuxBoxPageDown() => "scroll_muxbox_page_down".hash(state),
95            Message::ScrollMuxBoxPageLeft() => "scroll_muxbox_page_left".hash(state),
96            Message::ScrollMuxBoxPageRight() => "scroll_muxbox_page_right".hash(state),
97            Message::ScrollMuxBoxToBeginning() => "scroll_muxbox_to_beginning".hash(state),
98            Message::ScrollMuxBoxToEnd() => "scroll_muxbox_to_end".hash(state),
99            Message::ScrollMuxBoxToTop() => "scroll_muxbox_to_top".hash(state),
100            Message::ScrollMuxBoxToBottom() => "scroll_muxbox_to_bottom".hash(state),
101            Message::CopyFocusedMuxBoxContent() => "copy_focused_muxbox_content".hash(state),
102            Message::MuxBoxOutputUpdate(muxbox_id, success, output) => {
103                "muxbox_output_update".hash(state);
104                muxbox_id.hash(state);
105                success.hash(state);
106                output.hash(state);
107            }
108            Message::MuxBoxScriptUpdate(muxbox_id, script) => {
109                "muxbox_script_update".hash(state);
110                muxbox_id.hash(state);
111                script.hash(state);
112            }
113            Message::ReplaceMuxBox(muxbox_id, muxbox) => {
114                "replace_muxbox".hash(state);
115                muxbox_id.hash(state);
116                muxbox.hash(state);
117            }
118            Message::KeyPress(pressed_key) => {
119                "key_press".hash(state);
120                pressed_key.hash(state);
121            }
122            Message::ExecuteHotKeyChoice(choice_id) => {
123                "execute_hot_key_choice".hash(state);
124                choice_id.hash(state);
125            }
126            Message::MouseClick(x, y) => {
127                "mouse_click".hash(state);
128                x.hash(state);
129                y.hash(state);
130            }
131            Message::MouseDragStart(x, y) => {
132                "mouse_drag_start".hash(state);
133                x.hash(state);
134                y.hash(state);
135            }
136            Message::MouseDrag(x, y) => {
137                "mouse_drag".hash(state);
138                x.hash(state);
139                y.hash(state);
140            }
141            Message::MouseDragEnd(x, y) => {
142                "mouse_drag_end".hash(state);
143                x.hash(state);
144                y.hash(state);
145            }
146            Message::MuxBoxBorderDrag(muxbox_id, x, y) => {
147                "muxbox_border_drag".hash(state);
148                muxbox_id.hash(state);
149                x.hash(state);
150                y.hash(state);
151            }
152            Message::MuxBoxResizeComplete(muxbox_id) => {
153                "muxbox_resize_complete".hash(state);
154                muxbox_id.hash(state);
155            }
156            Message::MuxBoxMove(muxbox_id, x, y) => {
157                "muxbox_move".hash(state);
158                muxbox_id.hash(state);
159                x.hash(state);
160                y.hash(state);
161            }
162            Message::MuxBoxMoveComplete(muxbox_id) => {
163                "muxbox_move_complete".hash(state);
164                muxbox_id.hash(state);
165            }
166            Message::SaveYamlState => "save_yaml_state".hash(state),
167            Message::SaveActiveLayout(layout_id) => {
168                "save_active_layout".hash(state);
169                layout_id.hash(state);
170            }
171            Message::SaveMuxBoxContent(muxbox_id, content) => {
172                "save_muxbox_content".hash(state);
173                muxbox_id.hash(state);
174                content.hash(state);
175            }
176            Message::SaveMuxBoxScroll(muxbox_id, x, y) => {
177                "save_muxbox_scroll".hash(state);
178                muxbox_id.hash(state);
179                x.hash(state);
180                y.hash(state);
181            }
182            Message::PTYInput(muxbox_id, input) => {
183                "pty_input".hash(state);
184                muxbox_id.hash(state);
185                input.hash(state);
186            }
187            Message::ExecuteChoice(choice, muxbox_id, libs) => {
188                "execute_choice".hash(state);
189                choice.hash(state);
190                muxbox_id.hash(state);
191                libs.hash(state);
192            }
193            Message::ChoiceExecutionComplete(choice_id, muxbox_id, result) => {
194                "choice_execution_complete".hash(state);
195                choice_id.hash(state);
196                muxbox_id.hash(state);
197                match result {
198                    Ok(output) => {
199                        "ok".hash(state);
200                        output.hash(state);
201                    }
202                    Err(error) => {
203                        "err".hash(state);
204                        error.hash(state);
205                    }
206                }
207            }
208            Message::Pause => "pause".hash(state),
209            Message::ExternalMessage(msg) => {
210                "external_message".hash(state);
211                msg.hash(state);
212            }
213            Message::Start => "start".hash(state),
214            Message::StopBoxRefresh(box_id) => {
215                "stop_box_refresh".hash(state);
216                box_id.hash(state);
217            }
218            Message::StartBoxRefresh(box_id) => {
219                "start_box_refresh".hash(state);
220                box_id.hash(state);
221            }
222            Message::AddBox(box_id, muxbox) => {
223                "add_box".hash(state);
224                box_id.hash(state);
225                muxbox.hash(state);
226            }
227            Message::RemoveBox(box_id) => {
228                "remove_box".hash(state);
229                box_id.hash(state);
230            }
231        }
232    }
233}
234
235pub trait Runnable: Send + 'static {
236    fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>>;
237    fn receive_updates(&mut self) -> (AppContext, Vec<Message>);
238    fn process(&mut self, app_context: AppContext, messages: Vec<Message>);
239
240    fn update_app_context(&mut self, app_context: AppContext);
241    fn set_uuid(&mut self, uuid: Uuid);
242    fn get_uuid(&self) -> Uuid;
243    fn set_app_context_sender(
244        &mut self,
245        app_context_sender: mpsc::Sender<(Uuid, Vec<FieldUpdate>)>,
246    );
247    fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>);
248    fn set_app_context_receiver(
249        &mut self,
250        app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
251    );
252    fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>);
253    fn get_app_context(&self) -> &AppContext;
254    fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>>;
255    fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>>;
256
257    fn send_app_context_update(&self, old_app_context: AppContext);
258    fn send_message(&self, msg: Message);
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub enum RunnableState {
263    Created,
264    Running,
265    Paused,
266    Terminated,
267}
268
269pub struct RunnableImpl {
270    pub app_context: AppContext,
271    uuid: Uuid,
272    running_state: RunnableState,
273    app_context_sender: Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>>,
274    message_sender: Option<mpsc::Sender<(Uuid, Message)>>,
275    app_context_receiver: Option<mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>>,
276    message_receiver: Option<mpsc::Receiver<(Uuid, Message)>>,
277}
278
279impl RunnableImpl {
280    pub fn new(app_context: AppContext) -> Self {
281        RunnableImpl {
282            app_context,
283            uuid: Uuid::new_v4(),
284            running_state: RunnableState::Created,
285            app_context_sender: None,
286            message_sender: None,
287            app_context_receiver: None,
288            message_receiver: None,
289        }
290    }
291
292    pub fn _run(
293        &mut self,
294        process_fn: &mut dyn FnMut(&mut Self, AppContext, Vec<Message>) -> (bool, AppContext),
295    ) -> Result<bool, Box<dyn std::error::Error>> {
296        let (updated_app_context, new_messages) = self.receive_updates();
297        let original_app_context = updated_app_context.clone();
298        let mut should_continue = true;
299        for message in new_messages.iter() {
300            match message {
301                Message::Exit => {
302                    self.running_state = RunnableState::Terminated;
303                    return Ok(false);
304                }
305                Message::Terminate => {
306                    self.running_state = RunnableState::Terminated;
307                    return Ok(false);
308                }
309                Message::Pause => {
310                    self.running_state = RunnableState::Paused;
311                }
312                Message::Start => {
313                    self.running_state = RunnableState::Running;
314                }
315                _ => {}
316            }
317        }
318
319        //keep app_context in sync even if not running
320        if updated_app_context != self.app_context {
321            self.app_context = updated_app_context;
322        }
323
324        log::info!(
325            "RunnableImpl _run: state={:?}, messages={}",
326            self.running_state,
327            new_messages.len()
328        );
329        if self.running_state == RunnableState::Running {
330            log::info!(
331                "RunnableImpl calling process function with {} messages",
332                new_messages.len()
333            );
334            let (process_should_continue, result_app_context) =
335                process_fn(self, self.app_context.clone(), new_messages);
336            if result_app_context != original_app_context {
337                self.app_context = result_app_context;
338                self.send_app_context_update(original_app_context);
339            }
340            should_continue = process_should_continue;
341            log::info!(
342                "RunnableImpl process function returned should_continue={}",
343                should_continue
344            );
345        } else {
346            log::info!(
347                "RunnableImpl NOT calling process function - state is {:?}",
348                self.running_state
349            );
350        }
351
352        if !should_continue {
353            return Ok(false);
354        }
355        Ok(true)
356    }
357}
358
359impl Runnable for RunnableImpl {
360    fn receive_updates(&mut self) -> (AppContext, Vec<Message>) {
361        let mut app_context_updates = Vec::new();
362        let mut new_messages = Vec::new();
363
364        if let Some(ref app_context_receiver) = self.app_context_receiver {
365            while let Ok((_, received_field_updates)) = app_context_receiver.try_recv() {
366                if !received_field_updates.is_empty() {
367                    log::trace!(
368                        "Received app_context update: {:?} in thread {}",
369                        received_field_updates,
370                        self.uuid
371                    );
372                    app_context_updates = received_field_updates;
373                }
374            }
375        }
376
377        let mut updated_app_context = self.app_context.clone();
378        updated_app_context.apply_updates(app_context_updates);
379
380        if let Some(ref message_receiver) = self.message_receiver {
381            while let Ok((_, message)) = message_receiver.try_recv() {
382                new_messages.push(message);
383            }
384        }
385
386        (updated_app_context, new_messages)
387    }
388
389    fn process(&mut self, app_context: AppContext, messages: Vec<Message>) {
390        // Default implementation: update app context and handle basic messages
391        self.update_app_context(app_context);
392
393        // Process any messages that need handling
394        for message in messages {
395            match message {
396                Message::Terminate => {
397                    self.running_state = RunnableState::Terminated;
398                }
399                Message::Pause => {
400                    self.running_state = RunnableState::Paused;
401                }
402                Message::Start => {
403                    self.running_state = RunnableState::Running;
404                }
405                _ => {
406                    // Other messages are handled by specific implementations
407                }
408            }
409        }
410    }
411
412    fn update_app_context(&mut self, app_context: AppContext) {
413        let old_app_context = self.app_context.clone();
414        self.app_context = app_context;
415        self.send_app_context_update(old_app_context);
416    }
417
418    fn set_uuid(&mut self, uuid: Uuid) {
419        self.uuid = uuid;
420    }
421
422    fn get_uuid(&self) -> Uuid {
423        self.uuid
424    }
425
426    fn set_app_context_sender(&mut self, app_context_sender: Sender<(Uuid, Vec<FieldUpdate>)>) {
427        self.app_context_sender = Some(app_context_sender);
428    }
429
430    fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>) {
431        self.message_sender = Some(message_sender);
432    }
433
434    fn set_app_context_receiver(
435        &mut self,
436        app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
437    ) {
438        self.app_context_receiver = Some(app_context_receiver);
439    }
440
441    fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>) {
442        self.message_receiver = Some(message_receiver);
443    }
444
445    fn get_app_context(&self) -> &AppContext {
446        &self.app_context
447    }
448
449    fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>> {
450        &self.app_context_sender
451    }
452
453    fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>> {
454        &self.message_sender
455    }
456
457    fn send_app_context_update(&self, old_app_context: AppContext) {
458        if let Some(ref app_context_sender) = self.get_app_context_sender() {
459            if let Err(e) = app_context_sender.send((
460                self.get_uuid(),
461                self.get_app_context().generate_diff(&old_app_context),
462            )) {
463                error!("Failed to send update to main thread: {}", e);
464            }
465        }
466    }
467
468    fn send_message(&self, msg: Message) {
469        if let Some(ref message_sender) = self.get_message_sender() {
470            if let Err(e) = message_sender.send((self.get_uuid(), msg)) {
471                error!("Failed to send message to main thread: {}", e);
472            }
473        }
474    }
475
476    fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
477        todo!()
478    }
479}
480
481#[derive(Debug)]
482pub struct ThreadManager {
483    threads: HashMap<Uuid, thread::JoinHandle<()>>,
484    app_context_senders: HashMap<Uuid, mpsc::Sender<(Uuid, Vec<FieldUpdate>)>>,
485    app_context_receivers: HashMap<Uuid, mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>>,
486    message_senders: HashMap<Uuid, mpsc::Sender<(Uuid, Message)>>,
487    message_receivers: HashMap<Uuid, mpsc::Receiver<(Uuid, Message)>>,
488    app_context: AppContext,
489}
490
491impl ThreadManager {
492    pub fn new(app_context: AppContext) -> Self {
493        ThreadManager {
494            threads: HashMap::new(),
495            app_context_senders: HashMap::new(),
496            message_senders: HashMap::new(),
497            app_context_receivers: HashMap::new(),
498            message_receivers: HashMap::new(),
499            app_context,
500        }
501    }
502
503    pub fn stop(&self) {
504        self.send_message_to_all_threads((Uuid::new_v4(), Message::Exit));
505    }
506
507    pub fn pause(&self) {
508        self.send_message_to_all_threads((Uuid::new_v4(), Message::Pause));
509    }
510
511    pub fn run(&mut self) {
512        for message_sender in self.message_senders.values() {
513            if let Err(e) = message_sender.send((Uuid::new_v4(), Message::Start)) {
514                error!("Failed to send start message to thread: {}", e);
515            }
516        }
517        let mut should_continue: bool = true;
518        while should_continue {
519            let mut has_updates = false;
520
521            // Handle app_context updates
522            for reciever in self.app_context_receivers.values() {
523                if let Ok((uuid, app_context_updates)) = reciever.try_recv() {
524                    if app_context_updates.is_empty() {
525                        // log::trace!("No updates received from thread {}", uuid);
526                        continue;
527                    } else {
528                        let app_context_updates_size_in_bytes =
529                            bincode::serialize(&app_context_updates)
530                                .unwrap_or_default()
531                                .len();
532                        log::trace!(
533                            "Received {} updates from thread {} with total size {} bytes. Will relay to all other threads.",
534                            app_context_updates.len(),
535                            uuid,
536                            app_context_updates_size_in_bytes
537                        );
538                    }
539
540                    let original_app_context = self.app_context.clone();
541
542                    // log::trace!(
543                    //     "Sending app_context update to all threads: {:?}",
544                    //     app_context_updates
545                    // );
546                    self.app_context.app.apply_updates(app_context_updates);
547                    self.send_app_context_update_to_all_threads((
548                        uuid,
549                        self.app_context.generate_diff(&original_app_context),
550                    ));
551                    has_updates = true;
552                }
553            }
554
555            // Handle messages - collect first to avoid borrow conflicts
556            let mut messages_to_process = Vec::new();
557            for reciever in self.message_receivers.values() {
558                if let Ok((uuid, received_msg)) = reciever.try_recv() {
559                    messages_to_process.push((uuid, received_msg));
560                }
561            }
562
563            // Process collected messages
564            for (uuid, received_msg) in messages_to_process {
565                // log::info!("Received message from thread {}: {:?}", uuid, received_msg);
566                match received_msg {
567                    Message::Exit => {
568                        self.send_message_to_all_threads((Uuid::new_v4(), Message::Terminate));
569                        should_continue = false;
570                    }
571                    Message::ExecuteChoice(choice, muxbox_id, libs) => {
572                        // Handle choice execution request by spawning ChoiceExecutionRunnable
573                        log::info!(
574                            "ThreadManager spawning choice execution: {} on muxbox {}",
575                            choice.id,
576                            muxbox_id
577                        );
578                        let choice_runnable =
579                            ChoiceExecutionRunnable::new(self.app_context.clone());
580                        let choice_uuid = self.spawn_thread(choice_runnable);
581
582                        // Send ExecuteChoice message to the spawned runnable via unified message system
583                        log::info!(
584                            "ThreadManager sending ExecuteChoice message to runnable thread: {}",
585                            choice_uuid
586                        );
587                        self.send_message_to_thread(
588                            (
589                                Uuid::new_v4(),
590                                Message::ExecuteChoice(choice, muxbox_id, libs),
591                            ),
592                            choice_uuid,
593                        );
594                        has_updates = true;
595                    }
596                    Message::ChoiceExecutionComplete(ref choice_id, ref muxbox_id, ref result) => {
597                        log::info!("ThreadManager received ChoiceExecutionComplete for choice: {} on muxbox: {}", choice_id, muxbox_id);
598                        match result {
599                            Ok(output) => log::info!(
600                                "ThreadManager broadcasting choice success: {} chars of output",
601                                output.len()
602                            ),
603                            Err(error) => {
604                                log::error!("ThreadManager broadcasting choice error: {}", error)
605                            }
606                        }
607                        // Broadcast to all threads including DrawLoop
608                        self.send_message_to_all_threads((uuid, received_msg));
609                        has_updates = true;
610                    }
611                    _ => {
612                        // For all other messages, broadcast to all threads
613                        self.send_message_to_all_threads((uuid, received_msg));
614                        has_updates = true;
615                    }
616                }
617            }
618
619            // Sleep only if there were no updates to process
620            if !has_updates {
621                std::thread::sleep(std::time::Duration::from_millis(
622                    self.app_context.config.frame_delay,
623                ));
624            }
625        }
626    }
627
628    pub fn spawn_thread<R: Runnable + 'static>(&mut self, mut runnable: R) -> Uuid {
629        let uuid = Uuid::new_v4();
630        let (s_tm_t_s, s_tm_t_r) = mpsc::channel::<(Uuid, Vec<FieldUpdate>)>();
631        let (s_t_tm_s, s_t_tm_r) = mpsc::channel::<(Uuid, Vec<FieldUpdate>)>();
632        let (m_tm_t_s, m_tm_t_r) = mpsc::channel::<(Uuid, Message)>();
633        let (m_t_tm_s, m_t_tm_r) = mpsc::channel::<(Uuid, Message)>();
634
635        runnable.set_uuid(uuid);
636        runnable.set_app_context_sender(s_t_tm_s);
637        runnable.set_message_sender(m_t_tm_s);
638        runnable.set_app_context_receiver(s_tm_t_r);
639        runnable.set_message_receiver(m_tm_t_r);
640
641        self.app_context_senders.insert(uuid, s_tm_t_s);
642        self.message_senders.insert(uuid, m_tm_t_s);
643        self.app_context_receivers.insert(uuid, s_t_tm_r);
644        self.message_receivers.insert(uuid, m_t_tm_r);
645
646        let runnable_class_name = std::any::type_name::<R>();
647        let thread_name = format!("{}_{}", runnable_class_name, uuid);
648
649        let handle = thread::Builder::new()
650            .name(thread_name)
651            .spawn(move || {
652                let mut continue_running = true;
653                while continue_running {
654                    let result = runnable.run();
655                    if let Err(e) = result {
656                        error!("Runnable encountered an error: {}", e);
657                        continue_running = false;
658                    } else if let Ok(should_continue) = result {
659                        continue_running = should_continue;
660                        if !continue_running {
661                            log::trace!("Stopping thread as directed by run method");
662                        }
663                    }
664                }
665            })
666            .unwrap();
667
668        self.threads.insert(uuid, handle);
669
670        log::trace!("Thread spawned: {}", uuid);
671
672        uuid
673    }
674
675    pub fn send_app_context_update_to_thread(&self, field_updates: Vec<FieldUpdate>, uuid: Uuid) {
676        if let Some(sender) = self.app_context_senders.get(&uuid) {
677            if let Err(e) = sender.send((uuid, field_updates)) {
678                error!("Failed to send data to thread: {}", e);
679            }
680        }
681    }
682
683    pub fn send_app_context_update_to_all_threads(&self, field_updates: (Uuid, Vec<FieldUpdate>)) {
684        for (&uuid, sender) in &self.app_context_senders {
685            if uuid != field_updates.0 {
686                if let Err(e) = sender.send(field_updates.clone()) {
687                    error!("Failed to send update to thread: {}", e);
688                }
689            } else {
690                log::trace!("Skipping sending update to thread: {}", uuid);
691            }
692        }
693    }
694
695    pub fn send_message_to_thread(&self, msg: (Uuid, Message), uuid: Uuid) {
696        if let Some(sender) = self.message_senders.get(&uuid) {
697            log::info!("ThreadManager found message sender for thread: {}", uuid);
698            if let Err(e) = sender.send(msg) {
699                log::error!("Failed to send message to thread {}: {}", uuid, e);
700            } else {
701                log::info!(
702                    "ThreadManager successfully sent message to thread: {}",
703                    uuid
704                );
705            }
706        } else {
707            log::error!(
708                "ThreadManager could not find message sender for thread: {}",
709                uuid
710            );
711        }
712    }
713
714    pub fn send_message_to_all_threads(&self, msg: (Uuid, Message)) {
715        for (&uuid, sender) in &self.message_senders {
716            if uuid != msg.0 {
717                if let Err(e) = sender.send(msg.clone()) {
718                    error!("Failed to send message to thread: {}", e);
719                }
720            }
721        }
722    }
723
724    pub fn join_threads(&mut self) {
725        for handle in self.threads.drain() {
726            if let Err(e) = handle.1.join() {
727                error!("Failed to join thread: {:?}", e);
728            }
729        }
730    }
731
732    pub fn get_hash<T: Hash>(&self, t: &T) -> u64 {
733        let mut hasher = DefaultHasher::new();
734        t.hash(&mut hasher);
735        hasher.finish()
736    }
737
738    pub fn remove_thread(&mut self, uuid: Uuid) {
739        if let Some(handle) = self.threads.remove(&uuid) {
740            if let Err(e) = handle.join() {
741                error!("Failed to join thread: {:?}", e);
742            }
743        }
744        let msg = (Uuid::new_v4(), Message::Exit);
745        self.send_message_to_thread(msg, uuid);
746        self.app_context_senders.remove(&uuid);
747        self.message_senders.remove(&uuid);
748    }
749}
750
751#[macro_export]
752macro_rules! create_runnable {
753    ($name:ident, $init_body:expr, $process_body:expr) => {
754        pub struct $name {
755            inner: RunnableImpl,
756        }
757
758        impl $name {
759            pub fn new(app_context: AppContext) -> Self {
760                $name {
761                    inner: RunnableImpl::new(app_context),
762                }
763            }
764        }
765
766        impl Runnable for $name {
767            fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
768                // Call the init block before the loop
769                {
770                    let inner = &mut self.inner;
771                    let app_context = inner.app_context.clone();
772                    let messages = Vec::new();
773                    let init_result = $init_body(inner, app_context, messages);
774                    if !init_result {
775                        return Ok(false);
776                    }
777                }
778                self.inner._run(&mut |inner, app_context, messages| {
779                    $process_body(inner, app_context, messages)
780                })
781            }
782
783            fn receive_updates(&mut self) -> (AppContext, Vec<Message>) {
784                self.inner.receive_updates()
785            }
786
787            fn process(&mut self, app_context: AppContext, messages: Vec<Message>) {
788                self.inner.process(app_context, messages)
789            }
790
791            fn update_app_context(&mut self, app_context: AppContext) {
792                self.inner.update_app_context(app_context)
793            }
794
795            fn set_uuid(&mut self, uuid: Uuid) {
796                self.inner.set_uuid(uuid)
797            }
798
799            fn get_uuid(&self) -> Uuid {
800                self.inner.get_uuid()
801            }
802
803            fn set_app_context_sender(
804                &mut self,
805                app_context_sender: mpsc::Sender<(Uuid, Vec<FieldUpdate>)>,
806            ) {
807                self.inner.set_app_context_sender(app_context_sender)
808            }
809
810            fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>) {
811                self.inner.set_message_sender(message_sender)
812            }
813
814            fn set_app_context_receiver(
815                &mut self,
816                app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
817            ) {
818                self.inner.set_app_context_receiver(app_context_receiver)
819            }
820
821            fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>) {
822                self.inner.set_message_receiver(message_receiver)
823            }
824
825            fn get_app_context(&self) -> &AppContext {
826                self.inner.get_app_context()
827            }
828
829            fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>> {
830                self.inner.get_app_context_sender()
831            }
832
833            fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>> {
834                self.inner.get_message_sender()
835            }
836
837            fn send_app_context_update(&self, old_app_context: AppContext) {
838                self.inner.send_app_context_update(old_app_context)
839            }
840
841            fn send_message(&self, msg: Message) {
842                self.inner.send_message(msg)
843            }
844        }
845    };
846}
847
848// T312: ChoiceExecutionRunnable - Convert choice execution to Runnable pattern
849create_runnable!(
850    ChoiceExecutionRunnable,
851    |inner: &mut RunnableImpl, _app_context: AppContext, _messages: Vec<Message>| -> bool {
852        // Initialize - no setup needed for choice execution
853        log::info!(
854            "ChoiceExecutionRunnable initialization: state={:?}",
855            inner.running_state
856        );
857        inner.running_state = RunnableState::Running;
858        log::info!("ChoiceExecutionRunnable set state to Running");
859        true
860    },
861    |inner: &mut RunnableImpl,
862     app_context: AppContext,
863     messages: Vec<Message>|
864     -> (bool, AppContext) {
865        // Debug: Always log to see if processing function is called
866        let message_count = messages.len();
867        log::info!(
868            "ChoiceExecutionRunnable processing function called with {} messages",
869            message_count
870        );
871
872        let mut has_executed_choice = false;
873        for message in messages {
874            if let Message::ExecuteChoice(choice, muxbox_id, libs) = message {
875                log::info!(
876                    "ChoiceExecutionRunnable executing choice: {} for muxbox: {}",
877                    choice.id,
878                    muxbox_id
879                );
880                has_executed_choice = true;
881
882                // Execute the choice script
883                let choice_id = choice.id.clone();
884                let use_pty = choice.pty.unwrap_or(false);
885                let redirect_target = choice.redirect_output.clone();
886
887                let result = if let Some(script) = &choice.script {
888                    log::info!(
889                        "ChoiceExecutionRunnable running script for choice: {} (pty: {})",
890                        choice_id,
891                        use_pty
892                    );
893
894                    if use_pty {
895                        // PTY execution
896                        let message_sender = inner
897                            .message_sender
898                            .clone()
899                            .map(|sender| (sender, inner.uuid));
900                        match run_script_with_pty_and_redirect(
901                            libs,
902                            script,
903                            use_pty,
904                            app_context.pty_manager.as_ref().map(|v| &**v),
905                            Some(muxbox_id.clone()),
906                            message_sender,
907                            redirect_target,
908                        ) {
909                            Ok(output) => {
910                                log::info!("ChoiceExecutionRunnable PTY script started successfully for choice: {}", choice_id);
911                                Ok(output)
912                            }
913                            Err(e) => {
914                                log::error!(
915                                    "ChoiceExecutionRunnable PTY script failed for choice: {}: {}",
916                                    choice_id,
917                                    e
918                                );
919                                Err(e.to_string())
920                            }
921                        }
922                    } else {
923                        // Regular execution
924                        match run_script(libs, script) {
925                            Ok(output) => {
926                                log::info!("ChoiceExecutionRunnable script completed successfully for choice: {}", choice_id);
927                                Ok(output)
928                            }
929                            Err(e) => {
930                                log::error!(
931                                    "ChoiceExecutionRunnable script failed for choice: {}: {}",
932                                    choice_id,
933                                    e
934                                );
935                                Err(e.to_string())
936                            }
937                        }
938                    }
939                } else {
940                    log::error!(
941                        "ChoiceExecutionRunnable no script defined for choice: {}",
942                        choice_id
943                    );
944                    Err("No script defined for choice".to_string())
945                };
946
947                log::info!(
948                    "ChoiceExecutionRunnable sending completion message for choice: {}",
949                    choice_id
950                );
951                // Send completion message back to ThreadManager
952                inner.send_message(Message::ChoiceExecutionComplete(
953                    choice_id, muxbox_id, result,
954                ));
955            }
956        }
957
958        // Continue if no messages received yet, terminate after processing ExecuteChoice
959        let should_continue = !has_executed_choice; // Continue until we process an ExecuteChoice
960        log::info!(
961            "ChoiceExecutionRunnable: messages={}, has_executed_choice={}, should_continue={}",
962            message_count,
963            has_executed_choice,
964            should_continue
965        );
966        (should_continue, app_context)
967    }
968);
969
970// ChoiceExecutionRunnable implementation removed - using unified message system
971
972#[macro_export]
973macro_rules! create_runnable_with_dynamic_input {
974    ($name:ident, $vec_fn:expr, $init_body:expr, $process_body:expr) => {
975        pub struct $name {
976            inner: RunnableImpl,
977            vec_fn: Box<dyn Fn() -> Vec<String> + Send>,
978        }
979
980        impl $name {
981            pub fn new(
982                app_context: AppContext,
983                vec_fn: Box<dyn Fn() -> Vec<String> + Send>,
984            ) -> Self {
985                $name {
986                    inner: RunnableImpl::new(app_context),
987                    vec_fn,
988                }
989            }
990        }
991
992        impl Runnable for $name {
993            fn run(&mut self) -> Result<bool, Box<dyn std::error::Error>> {
994                // Call the init block before the loop
995                {
996                    let inner = &mut self.inner;
997                    let app_context = inner.app_context.clone();
998                    let messages = Vec::new();
999                    let vec = (self.vec_fn)();
1000                    let init_result = $init_body(inner, app_context, messages, vec);
1001                    if !init_result {
1002                        return Ok(false);
1003                    }
1004                }
1005                self.inner._run(&mut |inner, app_context, messages| {
1006                    let vec = (self.vec_fn)();
1007                    $process_body(inner, app_context, messages, vec)
1008                })
1009            }
1010
1011            fn receive_updates(&mut self) -> (AppContext, Vec<Message>) {
1012                self.inner.receive_updates()
1013            }
1014
1015            fn process(&mut self, app_context: AppContext, messages: Vec<Message>) {
1016                self.inner.process(app_context.clone(), messages.clone());
1017            }
1018
1019            fn update_app_context(&mut self, app_context: AppContext) {
1020                self.inner.update_app_context(app_context)
1021            }
1022
1023            fn set_uuid(&mut self, uuid: Uuid) {
1024                self.inner.set_uuid(uuid)
1025            }
1026
1027            fn get_uuid(&self) -> Uuid {
1028                self.inner.get_uuid()
1029            }
1030
1031            fn set_app_context_sender(
1032                &mut self,
1033                app_context_sender: mpsc::Sender<(Uuid, Vec<FieldUpdate>)>,
1034            ) {
1035                self.inner.set_app_context_sender(app_context_sender)
1036            }
1037
1038            fn set_message_sender(&mut self, message_sender: mpsc::Sender<(Uuid, Message)>) {
1039                self.inner.set_message_sender(message_sender)
1040            }
1041
1042            fn set_app_context_receiver(
1043                &mut self,
1044                app_context_receiver: mpsc::Receiver<(Uuid, Vec<FieldUpdate>)>,
1045            ) {
1046                self.inner.set_app_context_receiver(app_context_receiver)
1047            }
1048
1049            fn set_message_receiver(&mut self, message_receiver: mpsc::Receiver<(Uuid, Message)>) {
1050                self.inner.set_message_receiver(message_receiver)
1051            }
1052
1053            fn get_app_context(&self) -> &AppContext {
1054                self.inner.get_app_context()
1055            }
1056
1057            fn get_app_context_sender(&self) -> &Option<mpsc::Sender<(Uuid, Vec<FieldUpdate>)>> {
1058                self.inner.get_app_context_sender()
1059            }
1060
1061            fn get_message_sender(&self) -> &Option<mpsc::Sender<(Uuid, Message)>> {
1062                self.inner.get_message_sender()
1063            }
1064
1065            fn send_app_context_update(&self, old_app_context: AppContext) {
1066                self.inner.send_app_context_update(old_app_context)
1067            }
1068
1069            fn send_message(&self, msg: Message) {
1070                self.inner.send_message(msg)
1071            }
1072        }
1073    };
1074}
1075
1076pub fn run_script_in_thread(
1077    app_context: AppContext,
1078    manager: &mut ThreadManager,
1079    muxbox_id: String,
1080    choice_id: String,
1081) -> Uuid {
1082    let vec_fn = move || vec![muxbox_id.clone(), choice_id.clone()];
1083
1084    create_runnable_with_dynamic_input!(
1085        ChoiceScriptRunner,
1086        Box::new(vec_fn),
1087        |_inner: &mut RunnableImpl,
1088         _app_context: AppContext,
1089         _messages: Vec<Message>,
1090         _vec: Vec<String>|
1091         -> bool { true },
1092        |inner: &mut RunnableImpl,
1093         app_context: AppContext,
1094         _messages: Vec<Message>,
1095         vec: Vec<String>|
1096         -> (bool, AppContext) {
1097            let mut app_context_unwrapped = app_context.clone();
1098            let app_graph = app_context_unwrapped.app.generate_graph();
1099            let libs = app_context_unwrapped.app.libs.clone();
1100            let muxbox = app_context_unwrapped
1101                .app
1102                .get_muxbox_by_id_mut(&vec[0])
1103                .unwrap();
1104            let choice = muxbox
1105                .choices
1106                .as_mut()
1107                .unwrap()
1108                .iter_mut()
1109                .find(|c| c.id == vec[1])
1110                .unwrap();
1111            // Check if choice should use PTY
1112            let use_pty = crate::utils::should_use_pty_for_choice(choice);
1113            let pty_manager = app_context_unwrapped.pty_manager.as_ref();
1114            let message_sender = Some((
1115                inner.get_message_sender().as_ref().unwrap().clone(),
1116                inner.get_uuid(),
1117            ));
1118
1119            match crate::utils::run_script_with_pty(
1120                libs,
1121                choice.script.clone().unwrap().as_ref(),
1122                use_pty,
1123                pty_manager.map(|arc| arc.as_ref()),
1124                Some(choice.id.clone()),
1125                message_sender,
1126            ) {
1127                Ok(output) => {
1128                    inner.send_message(Message::MuxBoxOutputUpdate(choice.id.clone(), true, output))
1129                }
1130                Err(e) => inner.send_message(Message::MuxBoxOutputUpdate(
1131                    choice.id.clone(),
1132                    false,
1133                    e.to_string(),
1134                )),
1135            }
1136            std::thread::sleep(std::time::Duration::from_millis(
1137                muxbox.calc_refresh_interval(&app_context, &app_graph),
1138            ));
1139            (false, app_context_unwrapped)
1140        }
1141    );
1142
1143    let choice_refresh_loop = ChoiceScriptRunner::new(app_context.clone(), Box::new(vec_fn));
1144    manager.spawn_thread(choice_refresh_loop)
1145}
1146
1147// create_runnable!(
1148//     ExampleRunnable,z
1149//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| -> bool {
1150//         // Initialization block
1151//         info!("Initializing ExampleRunnable with app_context: {:?}", app_context);
1152//         inner.update_app_context(app_context);
1153//         true // Initialization complete, continue running
1154//     },
1155//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| -> bool {
1156//         // Processing block
1157//         info!("Processing in ExampleRunnable with app_context: {:?} and messages: {:?}", app_context, messages);
1158
1159//         for message in messages {
1160//             match message {
1161//                 Message::Exit => return false, // Stop running
1162//                 Message::NextMuxBox(muxbox_id) => {
1163//                     info!("Next muxbox: {}", muxbox_id);
1164//                     // Handle NextMuxBox logic
1165//                 },
1166//                 Message::PreviousMuxBox(muxbox_id) => {
1167//                     info!("Previous muxbox: {}", muxbox_id);
1168//                     // Handle PreviousMuxBox logic
1169//                 },
1170//                 _ => {
1171//                     info!("Unhandled message: {:?}", message);
1172//                     // Handle other messages
1173//                 },
1174//             }
1175//         }
1176
1177//         true // Continue running
1178//     }
1179// );
1180
1181// create_runnable!(
1182//     TestRunnableOne,
1183//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1184//         info!("TestRunnableOne initialization");
1185//         true  // Assuming initialization is always successful
1186//     },
1187//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1188//         info!("TestRunnableOne received app_context: {:?}", app_context);
1189//         for message in messages.iter() {
1190//             info!("TestRunnableOne received message: {:?}", message);
1191//         }
1192//         info!("TestRunnableOne running with data: {:?}", inner.get_app_context());
1193//         inner.send_message(Message::RedrawApp);
1194//         false  // Intentionally stopping the thread for demonstration
1195//     }
1196// );
1197
1198// create_runnable!(
1199//     TestRunnableTwo,
1200//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1201//         info!("TestRunnableOne initialization");
1202//         true  // Assuming initialization is always successful
1203//     },
1204//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1205//         info!("TestRunnableOne received app_context: {:?}", app_context);
1206//         for message in messages.iter() {
1207//             info!("TestRunnableOne received message: {:?}", message);
1208//         }
1209//         info!("TestRunnableOne running with data: {:?}", inner.get_app_context());
1210//         inner.send_message(Message::RedrawApp);
1211//         false  // Intentionally stopping the thread for demonstration
1212//     }
1213// );
1214
1215// create_runnable!(
1216//     TestRunnableThree,
1217//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1218//         info!("TestRunnableOne initialization");
1219//         true  // Assuming initialization is always successful
1220//     },
1221//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1222//         info!("TestRunnableOne received app_context: {:?}", app_context);
1223//         for message in messages.iter() {
1224//             info!("TestRunnableOne received message: {:?}", message);
1225//         }
1226//         info!("TestRunnableOne running with data: {:?}", inner.get_app_context());
1227//         inner.send_message(Message::RedrawApp);
1228//         false  // Intentionally stopping the thread for demonstration
1229//     }
1230// );
1231
1232// create_runnable!(
1233//     TestRunnableTwo,
1234//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message>| {
1235//         info!("TestRunnableTwo initialization");
1236//         true  // Initialization success
1237//     },
1238//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message)| {
1239//         info!("TestRunnableTwo received app_context: {:?}", app_context);
1240//         for message in messages.iter() {
1241//             info!("TestRunnableTwo received message: {:?}", message);
1242//         }
1243//         info!("TestRunnableTwo running with data: {:?}", inner.get_app_context());
1244//         inner.send_message(Message::ReplaceMuxBox("MuxBox2".to_string()));
1245//         true  // Continue running
1246//     }
1247// );
1248
1249// create_runnable!(
1250//     TestRunnableThree,
1251//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message)| {
1252//         info!("TestRunnableThree initialization");
1253//         true  // Initialization success
1254//     },
1255//     |inner: &mut RunnableImpl, app_context: AppContext, messages: Vec<Message)| {
1256//         info!("TestRunnableThree received app_context: {:?}", app_context);
1257//         for message in messages.iter() {
1258//             info!("TestRunnableThree received message: {:?}", message);
1259//         }
1260//         info!("TestRunnableThree running with data: {:?}", inner.get_app_context());
1261//         inner.send_message(Message::MuxBoxEventEnter("MuxBox3".to_string()));
1262//         true  // Continue running
1263//     }
1264// );
1265
1266// #[cfg(test)]
1267// mod tests {
1268//     use crate::App;
1269
1270//     use super::*;
1271//     use std::sync::mpsc::TryRecvError;
1272
1273//     #[test]
1274//     fn test_message_delivery() {
1275//         let app_context = AppContext::new(App::new());
1276//         let mut manager = ThreadManager::new(app_context.clone());
1277//         let uuid1 = manager.spawn_thread(TestRunnableOne::new(app_context.clone()));
1278//         let uuid2 = manager.spawn_thread(TestRunnableTwo::new(app_context.clone()));
1279//         let uuid3 = manager.spawn_thread(TestRunnableThree::new(app_context.clone()));
1280
1281//         let data = AppContext::new(App::new());
1282//         manager.send_app_context_update_to_thread(data.clone(), uuid1);
1283//         manager.send_app_context_update_to_thread(data.clone(), uuid2);
1284//         manager.send_app_context_update_to_thread(data.clone(), uuid3);
1285
1286//         manager.send_message_to_all_threads((uuid1, Message::NextMuxBox("MuxBox1".to_string())));
1287
1288//         // Run the manager's loop in a separate thread to allow message handling
1289//         let manager = manager;
1290//         // let manager_clone = Arc::clone(&manager);
1291
1292//         let handle = thread::spawn(move || {
1293//             manager_clone.run();
1294//         });
1295
1296//         // Give the threads some time to process the messages
1297//         thread::sleep(std::time::Duration::from_secs(1));
1298
1299//         // Ensure that each runnable received the message
1300//         let runnables = manager.runnables.clone();
1301//         for (_, runnable) in runnables.iter() {
1302//             let mut runnable = runnable.lock().unwrap();
1303//             let (_, messages) = runnable.receive_updates();
1304//             assert!(messages.iter().any(|msg| matches!(msg, Message::NextMuxBox(muxbox_id) if muxbox_id == "MuxBox1")));
1305//         }
1306//         manager.stop();
1307//         handle.join().unwrap();
1308//     }
1309
1310//     #[test]
1311//     fn test_state_update_propagation() {
1312//         let app_context = AppContext::new(App::new());
1313//         let mut manager = ThreadManager::new(app_context.clone());
1314//         let uuid1 = manager.spawn_thread(TestRunnableOne::new(app_context.clone()));
1315//         let uuid2 = manager.spawn_thread(TestRunnableTwo::new(app_context.clone()));
1316//         let uuid3 = manager.spawn_thread(TestRunnableThree::new(app_context.clone()));
1317
1318//         let data = AppContext::new(App::new());
1319//         manager.send_app_context_update_to_thread(data.clone(), uuid1);
1320
1321//         // Run the manager's loop in a separate thread to allow app_context handling
1322//         let manager = Arc::new(manager);
1323//         let manager_clone = Arc::clone(&manager);
1324
1325//         let handle = thread::spawn(move || {
1326//             manager_clone.run();
1327//         });
1328
1329//         // Give the threads some time to process the app_context update
1330//         thread::sleep(std::time::Duration::from_secs(1));
1331
1332//         // Ensure that the app_context was propagated to all runnables
1333//         let runnables = manager.runnables.clone();
1334//         for (_, runnable) in runnables.iter() {
1335//             let mut runnable = runnable.lock().unwrap();
1336//             let (app_context, _) = runnable.receive_updates();
1337//             assert_eq!(app_context, data);
1338//         }
1339//         manager.stop();
1340//         handle.join().unwrap();
1341//     }
1342
1343//     #[test]
1344//     fn test_concurrent_message_handling() {
1345//         let app_context = AppContext::new(App::new());
1346//         let mut manager = ThreadManager::new(app_context.clone());
1347//         let uuid1 = manager.spawn_thread(TestRunnableOne::new(app_context.clone()));
1348//         let uuid2 = manager.spawn_thread(TestRunnableTwo::new(app_context.clone()));
1349//         let uuid3 = manager.spawn_thread(TestRunnableThree::new(app_context.clone()));
1350
1351//         let data = AppContext::new(App::new());
1352//         manager.send_app_context_update_to_thread(data.clone(), uuid1);
1353//         manager.send_app_context_update_to_thread(data.clone(), uuid2);
1354//         manager.send_app_context_update_to_thread(data.clone(), uuid3);
1355
1356//         manager.send_message_to_all_threads((uuid1, Message::RedrawApp));
1357//         manager.send_message_to_all_threads((uuid2, Message::ReplaceMuxBox("MuxBox2".to_string())));
1358//         manager.send_message_to_all_threads((uuid3, Message::MuxBoxEventEnter("MuxBox3".to_string())));
1359
1360//         // Run the manager's loop in a separate thread to allow message handling
1361//         let manager = Arc::new(manager);
1362//         let manager_clone = Arc::clone(&manager);
1363
1364//         let handle = thread::spawn(move || {
1365//             manager_clone.run();
1366//         });
1367
1368//         // Give the threads some time to process the messages
1369//         thread::sleep(std::time::Duration::from_secs(1));
1370
1371//         // Ensure that each runnable received the messages
1372//         let runnables = manager.runnables.clone();
1373//         for (_, runnable) in runnables.iter() {
1374//             let mut runnable = runnable.lock().unwrap();
1375//             let (_, messages) = runnable.receive_updates();
1376//             assert!(messages.iter().any(|msg| matches!(msg, Message::RedrawApp)));
1377//             assert!(messages.iter().any(|msg| matches!(msg, Message::ReplaceMuxBox(muxbox_id) if muxbox_id == "MuxBox2")));
1378//             assert!(messages.iter().any(|msg| matches!(msg, Message::MuxBoxEventEnter(muxbox_id) if muxbox_id == "MuxBox3")));
1379//         }
1380//         manager.stop();
1381//         handle.join().unwrap();
1382//     }
1383
1384//     #[test]
1385//     fn test_message_delivery_once() {
1386//         let app_context = AppContext::new(App::new());
1387//         let mut manager = ThreadManager::new(app_context.clone());
1388//         let uuid1 = manager.spawn_thread(TestRunnableOne::new(app_context.clone()));
1389//         let uuid2 = manager.spawn_thread(TestRunnableTwo::new(app_context.clone()));
1390//         let uuid3 = manager.spawn_thread(TestRunnableThree::new(app_context.clone()));
1391
1392//         let data = AppContext::new(App::new());
1393//         manager.send_app_context_update_to_thread(data.clone(), uuid1);
1394//         manager.send_app_context_update_to_thread(data.clone(), uuid2);
1395//         manager.send_app_context_update_to_thread(data.clone(), uuid3);
1396
1397//         manager.send_message_to_all_threads((uuid1, Message::RedrawApp));
1398
1399//         // Run the manager's loop in a separate thread to allow message handling
1400//         let manager = Arc::new(manager);
1401//         let manager_clone = Arc::clone(&manager);
1402
1403//         let handle = thread::spawn(move || {
1404//             manager_clone.run();
1405//         });
1406
1407//         // Give the threads some time to process the messages
1408//         thread::sleep(std::time::Duration::from_secs(1));
1409
1410//         // Ensure that the message was delivered only once
1411//         let runnables = manager.runnables.clone();
1412//         for (_, runnable) in runnables.iter() {
1413//             let mut runnable = runnable.lock().unwrap();
1414//             let (_, messages) = runnable.receive_updates();
1415//             let count = messages.iter().filter(|msg| matches!(msg, Message::RedrawApp)).count();
1416//             assert_eq!(count, 1);
1417//         }
1418//         manager.stop();
1419//         handle.join().unwrap();
1420//     }
1421// }
1422
1423#[cfg(test)]
1424mod tests {
1425    use super::*;
1426    use crate::model::app::App;
1427    use crate::model::common::{Config, EntityType, FieldUpdate};
1428    use crate::model::layout::Layout;
1429    use crate::model::muxbox::MuxBox;
1430    use serde_json::Value;
1431    use std::sync::mpsc;
1432
1433    // Helper function to create test AppContext
1434    fn create_test_app_context() -> AppContext {
1435        let mut app = App::new();
1436        let layout = create_test_layout("test_layout");
1437        app.layouts.push(layout);
1438        let config = Config::default();
1439        AppContext::new(app, config)
1440    }
1441
1442    // Helper function to create test MuxBox
1443    fn create_test_muxbox(id: &str) -> MuxBox {
1444        MuxBox {
1445            id: id.to_string(),
1446            position: crate::model::common::InputBounds {
1447                x1: "0%".to_string(),
1448                y1: "0%".to_string(),
1449                x2: "100%".to_string(),
1450                y2: "100%".to_string(),
1451            },
1452            ..Default::default()
1453        }
1454    }
1455
1456    // Helper function to create test Layout
1457    fn create_test_layout(id: &str) -> Layout {
1458        Layout {
1459            id: id.to_string(),
1460            ..Default::default()
1461        }
1462    }
1463
1464    // Helper function to create test FieldUpdate
1465    fn create_test_field_update(
1466        entity_type: EntityType,
1467        entity_id: &str,
1468        field_name: &str,
1469        value: Value,
1470    ) -> FieldUpdate {
1471        FieldUpdate {
1472            entity_type,
1473            entity_id: Some(entity_id.to_string()),
1474            field_name: field_name.to_string(),
1475            new_value: value,
1476        }
1477    }
1478
1479    /// Tests that Message enum implements Hash correctly for different variants.
1480    /// This test demonstrates the message hashing feature.
1481    #[test]
1482    fn test_message_hash() {
1483        let mut hasher1 = DefaultHasher::new();
1484        let mut hasher2 = DefaultHasher::new();
1485
1486        let msg1 = Message::Exit;
1487        let msg2 = Message::Exit;
1488        msg1.hash(&mut hasher1);
1489        msg2.hash(&mut hasher2);
1490        assert_eq!(hasher1.finish(), hasher2.finish());
1491
1492        let msg3 = Message::RedrawMuxBox("muxbox1".to_string());
1493        let msg4 = Message::RedrawMuxBox("muxbox1".to_string());
1494        let mut hasher3 = DefaultHasher::new();
1495        let mut hasher4 = DefaultHasher::new();
1496        msg3.hash(&mut hasher3);
1497        msg4.hash(&mut hasher4);
1498        assert_eq!(hasher3.finish(), hasher4.finish());
1499    }
1500
1501    /// Tests that Message enum implements PartialEq correctly.
1502    /// This test demonstrates the message equality comparison feature.
1503    #[test]
1504    fn test_message_equality() {
1505        assert_eq!(Message::Exit, Message::Exit);
1506        assert_eq!(Message::Terminate, Message::Terminate);
1507        assert_eq!(
1508            Message::RedrawMuxBox("muxbox1".to_string()),
1509            Message::RedrawMuxBox("muxbox1".to_string())
1510        );
1511        assert_ne!(
1512            Message::RedrawMuxBox("muxbox1".to_string()),
1513            Message::RedrawMuxBox("muxbox2".to_string())
1514        );
1515        assert_ne!(Message::Exit, Message::Terminate);
1516    }
1517
1518    /// Tests that Message enum correctly hashes different message types.
1519    /// This test demonstrates the message type differentiation feature.
1520    #[test]
1521    fn test_message_hash_different_types() {
1522        let msg1 = Message::KeyPress("a".to_string());
1523        let msg2 = Message::KeyPress("a".to_string());
1524        let msg3 = Message::KeyPress("b".to_string());
1525
1526        let mut hasher1 = DefaultHasher::new();
1527        let mut hasher2 = DefaultHasher::new();
1528        let mut hasher3 = DefaultHasher::new();
1529
1530        msg1.hash(&mut hasher1);
1531        msg2.hash(&mut hasher2);
1532        msg3.hash(&mut hasher3);
1533
1534        assert_eq!(hasher1.finish(), hasher2.finish());
1535        assert_ne!(hasher1.finish(), hasher3.finish());
1536    }
1537
1538    /// Tests that RunnableState enum implements Clone and PartialEq.
1539    /// This test demonstrates the runnable state management feature.
1540    #[test]
1541    fn test_runnable_state() {
1542        let state1 = RunnableState::Created;
1543        let state2 = state1.clone();
1544        assert_eq!(state1, state2);
1545
1546        let state3 = RunnableState::Running;
1547        assert_ne!(state1, state3);
1548
1549        let state4 = RunnableState::Paused;
1550        let state5 = RunnableState::Terminated;
1551        assert_ne!(state4, state5);
1552    }
1553
1554    /// Tests that RunnableImpl can be created with an AppContext.
1555    /// This test demonstrates the runnable implementation creation feature.
1556    #[test]
1557    fn test_runnable_impl_new() {
1558        let app_context = create_test_app_context();
1559        let runnable = RunnableImpl::new(app_context.clone());
1560
1561        assert_eq!(runnable.get_app_context(), &app_context);
1562        assert_eq!(runnable.running_state, RunnableState::Created);
1563        assert!(runnable.app_context_sender.is_none());
1564        assert!(runnable.message_sender.is_none());
1565        assert!(runnable.app_context_receiver.is_none());
1566        assert!(runnable.message_receiver.is_none());
1567    }
1568
1569    /// Tests that RunnableImpl can set and get UUID.
1570    /// This test demonstrates the runnable UUID management feature.
1571    #[test]
1572    fn test_runnable_impl_uuid() {
1573        let app_context = create_test_app_context();
1574        let mut runnable = RunnableImpl::new(app_context);
1575
1576        let original_uuid = runnable.get_uuid();
1577        let new_uuid = Uuid::new_v4();
1578        runnable.set_uuid(new_uuid);
1579
1580        assert_eq!(runnable.get_uuid(), new_uuid);
1581        assert_ne!(runnable.get_uuid(), original_uuid);
1582    }
1583
1584    /// Tests that RunnableImpl can set and get senders and receivers.
1585    /// This test demonstrates the runnable channel management feature.
1586    #[test]
1587    fn test_runnable_impl_channels() {
1588        let app_context = create_test_app_context();
1589        let mut runnable = RunnableImpl::new(app_context);
1590
1591        let (app_context_sender, app_context_receiver) = mpsc::channel();
1592        let (message_sender, message_receiver) = mpsc::channel();
1593
1594        runnable.set_app_context_sender(app_context_sender);
1595        runnable.set_message_sender(message_sender);
1596        runnable.set_app_context_receiver(app_context_receiver);
1597        runnable.set_message_receiver(message_receiver);
1598
1599        assert!(runnable.get_app_context_sender().is_some());
1600        assert!(runnable.get_message_sender().is_some());
1601        assert!(runnable.app_context_receiver.is_some());
1602        assert!(runnable.message_receiver.is_some());
1603    }
1604
1605    /// Tests that RunnableImpl can update app context.
1606    /// This test demonstrates the app context update feature.
1607    #[test]
1608    fn test_runnable_impl_update_app_context() {
1609        let app_context = create_test_app_context();
1610        let mut runnable = RunnableImpl::new(app_context);
1611
1612        let mut new_app_context = create_test_app_context();
1613        new_app_context.config.frame_delay = 100;
1614
1615        runnable.update_app_context(new_app_context.clone());
1616        assert_eq!(runnable.get_app_context(), &new_app_context);
1617    }
1618
1619    /// Tests that RunnableImpl can receive updates from channels.
1620    /// This test demonstrates the message receiving feature.
1621    #[test]
1622    fn test_runnable_impl_receive_updates() {
1623        let app_context = create_test_app_context();
1624        let mut runnable = RunnableImpl::new(app_context);
1625
1626        let (app_context_sender, app_context_receiver) = mpsc::channel();
1627        let (message_sender, message_receiver) = mpsc::channel();
1628
1629        runnable.set_app_context_receiver(app_context_receiver);
1630        runnable.set_message_receiver(message_receiver);
1631
1632        // Send test data
1633        let field_update = create_test_field_update(
1634            EntityType::App,
1635            "test",
1636            "field",
1637            Value::String("value".to_string()),
1638        );
1639        app_context_sender
1640            .send((Uuid::new_v4(), vec![field_update]))
1641            .unwrap();
1642        message_sender
1643            .send((Uuid::new_v4(), Message::RedrawApp))
1644            .unwrap();
1645
1646        let (updated_app_context, messages) = runnable.receive_updates();
1647        assert_eq!(messages.len(), 1);
1648        assert_eq!(messages[0], Message::RedrawApp);
1649    }
1650
1651    /// Tests that RunnableImpl processes messages correctly.
1652    /// This test demonstrates the message processing feature.
1653    #[test]
1654    fn test_runnable_impl_process_messages() {
1655        let app_context = create_test_app_context();
1656        let mut runnable = RunnableImpl::new(app_context.clone());
1657
1658        let messages = vec![Message::Start, Message::Pause, Message::Terminate];
1659
1660        runnable.process(app_context.clone(), vec![Message::Start]);
1661        assert_eq!(runnable.running_state, RunnableState::Running);
1662
1663        runnable.process(app_context.clone(), vec![Message::Pause]);
1664        assert_eq!(runnable.running_state, RunnableState::Paused);
1665
1666        runnable.process(app_context.clone(), vec![Message::Terminate]);
1667        assert_eq!(runnable.running_state, RunnableState::Terminated);
1668    }
1669
1670    /// Tests that RunnableImpl can send messages through channels.
1671    /// This test demonstrates the message sending feature.
1672    #[test]
1673    fn test_runnable_impl_send_message() {
1674        let app_context = create_test_app_context();
1675        let mut runnable = RunnableImpl::new(app_context);
1676
1677        let (message_sender, message_receiver) = mpsc::channel();
1678        runnable.set_message_sender(message_sender);
1679
1680        runnable.send_message(Message::RedrawApp);
1681
1682        let (uuid, received_message) = message_receiver.recv().unwrap();
1683        assert_eq!(received_message, Message::RedrawApp);
1684        assert_eq!(uuid, runnable.get_uuid());
1685    }
1686
1687    /// Tests that ThreadManager can be created with an AppContext.
1688    /// This test demonstrates the thread manager creation feature.
1689    #[test]
1690    fn test_thread_manager_new() {
1691        let app_context = create_test_app_context();
1692        let manager = ThreadManager::new(app_context.clone());
1693
1694        assert_eq!(manager.app_context, app_context);
1695        assert!(manager.threads.is_empty());
1696        assert!(manager.app_context_senders.is_empty());
1697        assert!(manager.message_senders.is_empty());
1698        assert!(manager.app_context_receivers.is_empty());
1699        assert!(manager.message_receivers.is_empty());
1700    }
1701
1702    /// Tests that ThreadManager can spawn threads.
1703    /// This test demonstrates the thread spawning feature.
1704    #[test]
1705    fn test_thread_manager_spawn_thread() {
1706        let app_context = create_test_app_context();
1707        let mut manager = ThreadManager::new(app_context.clone());
1708        let runnable = RunnableImpl::new(app_context);
1709
1710        let uuid = manager.spawn_thread(runnable);
1711
1712        assert!(manager.threads.contains_key(&uuid));
1713        assert!(manager.app_context_senders.contains_key(&uuid));
1714        assert!(manager.message_senders.contains_key(&uuid));
1715        assert!(manager.app_context_receivers.contains_key(&uuid));
1716        assert!(manager.message_receivers.contains_key(&uuid));
1717
1718        // Clean up
1719        manager.stop();
1720        manager.join_threads();
1721    }
1722
1723    /// Tests that ThreadManager can remove threads.
1724    /// This test demonstrates the thread removal feature.
1725    #[test]
1726    fn test_thread_manager_remove_thread() {
1727        let app_context = create_test_app_context();
1728        let mut manager = ThreadManager::new(app_context.clone());
1729        let runnable = RunnableImpl::new(app_context);
1730
1731        let uuid = manager.spawn_thread(runnable);
1732        assert!(manager.threads.contains_key(&uuid));
1733
1734        manager.remove_thread(uuid);
1735        assert!(!manager.threads.contains_key(&uuid));
1736        assert!(!manager.app_context_senders.contains_key(&uuid));
1737        assert!(!manager.message_senders.contains_key(&uuid));
1738    }
1739
1740    /// Tests that ThreadManager can send messages to specific threads.
1741    /// This test demonstrates the targeted message sending feature.
1742    #[test]
1743    fn test_thread_manager_send_message_to_thread() {
1744        let app_context = create_test_app_context();
1745        let mut manager = ThreadManager::new(app_context.clone());
1746        let runnable = RunnableImpl::new(app_context);
1747
1748        let uuid = manager.spawn_thread(runnable);
1749        let message = (Uuid::new_v4(), Message::RedrawApp);
1750
1751        manager.send_message_to_thread(message, uuid);
1752
1753        // Clean up
1754        manager.stop();
1755        manager.join_threads();
1756    }
1757
1758    /// Tests that ThreadManager can send messages to all threads.
1759    /// This test demonstrates the broadcast message sending feature.
1760    #[test]
1761    fn test_thread_manager_send_message_to_all_threads() {
1762        let app_context = create_test_app_context();
1763        let mut manager = ThreadManager::new(app_context.clone());
1764        let runnable1 = RunnableImpl::new(app_context.clone());
1765        let runnable2 = RunnableImpl::new(app_context.clone());
1766
1767        let uuid1 = manager.spawn_thread(runnable1);
1768        let uuid2 = manager.spawn_thread(runnable2);
1769
1770        let message = (Uuid::new_v4(), Message::RedrawApp);
1771        manager.send_message_to_all_threads(message);
1772
1773        // Clean up
1774        manager.stop();
1775        manager.join_threads();
1776    }
1777
1778    /// Tests that ThreadManager can send app context updates to specific threads.
1779    /// This test demonstrates the targeted app context update feature.
1780    #[test]
1781    fn test_thread_manager_send_app_context_update_to_thread() {
1782        let app_context = create_test_app_context();
1783        let mut manager = ThreadManager::new(app_context.clone());
1784        let runnable = RunnableImpl::new(app_context);
1785
1786        let uuid = manager.spawn_thread(runnable);
1787        let field_update = create_test_field_update(
1788            EntityType::App,
1789            "test",
1790            "field",
1791            Value::String("value".to_string()),
1792        );
1793
1794        manager.send_app_context_update_to_thread(vec![field_update], uuid);
1795
1796        // Clean up
1797        manager.stop();
1798        manager.join_threads();
1799    }
1800
1801    /// Tests that ThreadManager can send app context updates to all threads.
1802    /// This test demonstrates the broadcast app context update feature.
1803    #[test]
1804    fn test_thread_manager_send_app_context_update_to_all_threads() {
1805        let app_context = create_test_app_context();
1806        let mut manager = ThreadManager::new(app_context.clone());
1807        let runnable1 = RunnableImpl::new(app_context.clone());
1808        let runnable2 = RunnableImpl::new(app_context.clone());
1809
1810        let uuid1 = manager.spawn_thread(runnable1);
1811        let uuid2 = manager.spawn_thread(runnable2);
1812
1813        let field_update = create_test_field_update(
1814            EntityType::App,
1815            "test",
1816            "field",
1817            Value::String("value".to_string()),
1818        );
1819        let sender_uuid = Uuid::new_v4();
1820
1821        manager.send_app_context_update_to_all_threads((sender_uuid, vec![field_update]));
1822
1823        // Clean up
1824        manager.stop();
1825        manager.join_threads();
1826    }
1827
1828    /// Tests that ThreadManager can stop all threads.
1829    /// This test demonstrates the thread stopping feature.
1830    #[test]
1831    fn test_thread_manager_stop() {
1832        let app_context = create_test_app_context();
1833        let mut manager = ThreadManager::new(app_context.clone());
1834        let runnable = RunnableImpl::new(app_context);
1835
1836        let uuid = manager.spawn_thread(runnable);
1837        assert!(manager.threads.contains_key(&uuid));
1838
1839        manager.stop();
1840        manager.join_threads();
1841
1842        assert!(manager.threads.is_empty());
1843    }
1844
1845    /// Tests that ThreadManager can pause all threads.
1846    /// This test demonstrates the thread pausing feature.
1847    #[test]
1848    fn test_thread_manager_pause() {
1849        let app_context = create_test_app_context();
1850        let mut manager = ThreadManager::new(app_context.clone());
1851        let runnable = RunnableImpl::new(app_context);
1852
1853        let uuid = manager.spawn_thread(runnable);
1854        manager.pause();
1855
1856        // Clean up
1857        manager.stop();
1858        manager.join_threads();
1859    }
1860
1861    /// Tests that ThreadManager can calculate hash values.
1862    /// This test demonstrates the hash calculation feature.
1863    #[test]
1864    fn test_thread_manager_get_hash() {
1865        let app_context = create_test_app_context();
1866        let manager = ThreadManager::new(app_context);
1867
1868        let test_string = "test";
1869        let hash1 = manager.get_hash(&test_string);
1870        let hash2 = manager.get_hash(&test_string);
1871        assert_eq!(hash1, hash2);
1872
1873        let test_string2 = "different";
1874        let hash3 = manager.get_hash(&test_string2);
1875        assert_ne!(hash1, hash3);
1876    }
1877
1878    /// Tests that ThreadManager can join all threads.
1879    /// This test demonstrates the thread joining feature.
1880    #[test]
1881    fn test_thread_manager_join_threads() {
1882        let app_context = create_test_app_context();
1883        let mut manager = ThreadManager::new(app_context.clone());
1884        let runnable = RunnableImpl::new(app_context);
1885
1886        let uuid = manager.spawn_thread(runnable);
1887        assert!(manager.threads.contains_key(&uuid));
1888
1889        manager.stop();
1890        manager.join_threads();
1891
1892        assert!(manager.threads.is_empty());
1893    }
1894
1895    /// Tests that run_script_in_thread spawns a thread correctly.
1896    /// This test demonstrates the script thread spawning feature.
1897    #[test]
1898    fn test_run_script_in_thread() {
1899        let app_context = create_test_app_context();
1900        let mut manager = ThreadManager::new(app_context.clone());
1901
1902        let muxbox_id = "test_muxbox".to_string();
1903        let choice_id = "test_choice".to_string();
1904
1905        let uuid = run_script_in_thread(app_context, &mut manager, muxbox_id, choice_id);
1906
1907        assert!(manager.threads.contains_key(&uuid));
1908        assert!(manager.app_context_senders.contains_key(&uuid));
1909        assert!(manager.message_senders.contains_key(&uuid));
1910
1911        // Clean up
1912        manager.stop();
1913        manager.join_threads();
1914    }
1915
1916    /// Tests that Message::MuxBoxOutputUpdate contains correct data.
1917    /// This test demonstrates the muxbox output update message feature.
1918    #[test]
1919    fn test_message_muxbox_output_update() {
1920        let muxbox_id = "test_muxbox".to_string();
1921        let success = true;
1922        let output = "test output".to_string();
1923
1924        let message = Message::MuxBoxOutputUpdate(muxbox_id.clone(), success, output.clone());
1925
1926        match message {
1927            Message::MuxBoxOutputUpdate(id, success_flag, content) => {
1928                assert_eq!(id, muxbox_id);
1929                assert_eq!(success_flag, success);
1930                assert_eq!(content, output);
1931            }
1932            _ => panic!("Expected MuxBoxOutputUpdate message"),
1933        }
1934    }
1935
1936    /// Tests that Message::MuxBoxScriptUpdate contains correct data.
1937    /// This test demonstrates the muxbox script update message feature.
1938    #[test]
1939    fn test_message_muxbox_script_update() {
1940        let muxbox_id = "test_muxbox".to_string();
1941        let script = vec!["echo 'test'".to_string(), "ls".to_string()];
1942
1943        let message = Message::MuxBoxScriptUpdate(muxbox_id.clone(), script.clone());
1944
1945        match message {
1946            Message::MuxBoxScriptUpdate(id, script_content) => {
1947                assert_eq!(id, muxbox_id);
1948                assert_eq!(script_content, script);
1949            }
1950            _ => panic!("Expected MuxBoxScriptUpdate message"),
1951        }
1952    }
1953
1954    /// Tests that Message::ReplaceMuxBox contains correct data.
1955    /// This test demonstrates the muxbox replacement message feature.
1956    #[test]
1957    fn test_message_replace_muxbox() {
1958        let muxbox_id = "test_muxbox".to_string();
1959        let muxbox = create_test_muxbox("new_muxbox");
1960
1961        let message = Message::ReplaceMuxBox(muxbox_id.clone(), muxbox.clone());
1962
1963        match message {
1964            Message::ReplaceMuxBox(id, new_muxbox) => {
1965                assert_eq!(id, muxbox_id);
1966                assert_eq!(new_muxbox.id, muxbox.id);
1967            }
1968            _ => panic!("Expected ReplaceMuxBox message"),
1969        }
1970    }
1971
1972    /// Tests that Message::SwitchActiveLayout contains correct data.
1973    /// This test demonstrates the layout switching message feature.
1974    #[test]
1975    fn test_message_switch_active_layout() {
1976        let layout_id = "test_layout".to_string();
1977        let message = Message::SwitchActiveLayout(layout_id.clone());
1978
1979        match message {
1980            Message::SwitchActiveLayout(id) => {
1981                assert_eq!(id, layout_id);
1982            }
1983            _ => panic!("Expected SwitchActiveLayout message"),
1984        }
1985    }
1986
1987    /// Tests that Message::KeyPress contains correct data.
1988    /// This test demonstrates the key press message feature.
1989    #[test]
1990    fn test_message_key_press() {
1991        let key = "ctrl+c".to_string();
1992        let message = Message::KeyPress(key.clone());
1993
1994        match message {
1995            Message::KeyPress(pressed_key) => {
1996                assert_eq!(pressed_key, key);
1997            }
1998            _ => panic!("Expected KeyPress message"),
1999        }
2000    }
2001
2002    /// Tests that Message::ExternalMessage contains correct data.
2003    /// This test demonstrates the external message feature.
2004    #[test]
2005    fn test_message_external_message() {
2006        let external_msg = "external command".to_string();
2007        let message = Message::ExternalMessage(external_msg.clone());
2008
2009        match message {
2010            Message::ExternalMessage(msg) => {
2011                assert_eq!(msg, external_msg);
2012            }
2013            _ => panic!("Expected ExternalMessage message"),
2014        }
2015    }
2016
2017    /// Tests that Message::AddBox contains correct data.
2018    /// This test demonstrates the box addition message feature.
2019    #[test]
2020    fn test_message_add_box() {
2021        let box_id = "test_box".to_string();
2022        let muxbox = create_test_muxbox("new_box");
2023
2024        let message = Message::AddBox(box_id.clone(), muxbox.clone());
2025
2026        match message {
2027            Message::AddBox(id, new_muxbox) => {
2028                assert_eq!(id, box_id);
2029                assert_eq!(new_muxbox.id, muxbox.id);
2030            }
2031            _ => panic!("Expected AddBox message"),
2032        }
2033    }
2034
2035    /// Tests that Message::RemoveBox contains correct data.
2036    /// This test demonstrates the box removal message feature.
2037    #[test]
2038    fn test_message_remove_box() {
2039        let box_id = "test_box".to_string();
2040        let message = Message::RemoveBox(box_id.clone());
2041
2042        match message {
2043            Message::RemoveBox(id) => {
2044                assert_eq!(id, box_id);
2045            }
2046            _ => panic!("Expected RemoveBox message"),
2047        }
2048    }
2049
2050    /// Tests that simple messages are created correctly.
2051    /// This test demonstrates the simple message creation feature.
2052    #[test]
2053    fn test_simple_messages() {
2054        let exit_msg = Message::Exit;
2055        let terminate_msg = Message::Terminate;
2056        let pause_msg = Message::Pause;
2057        let start_msg = Message::Start;
2058        let resize_msg = Message::Resize;
2059        let redraw_app_msg = Message::RedrawApp;
2060
2061        // Test that they can be created and compared
2062        assert_eq!(exit_msg, Message::Exit);
2063        assert_eq!(terminate_msg, Message::Terminate);
2064        assert_eq!(pause_msg, Message::Pause);
2065        assert_eq!(start_msg, Message::Start);
2066        assert_eq!(resize_msg, Message::Resize);
2067        assert_eq!(redraw_app_msg, Message::RedrawApp);
2068
2069        // Test that they are different from each other
2070        assert_ne!(exit_msg, terminate_msg);
2071        assert_ne!(pause_msg, start_msg);
2072        assert_ne!(resize_msg, redraw_app_msg);
2073    }
2074
2075    /// Tests that scroll messages are created correctly.
2076    /// This test demonstrates the scroll message creation feature.
2077    #[test]
2078    fn test_scroll_messages() {
2079        let scroll_down = Message::ScrollMuxBoxDown();
2080        let scroll_up = Message::ScrollMuxBoxUp();
2081        let scroll_left = Message::ScrollMuxBoxLeft();
2082        let scroll_right = Message::ScrollMuxBoxRight();
2083        let scroll_page_up = Message::ScrollMuxBoxPageUp();
2084        let scroll_page_down = Message::ScrollMuxBoxPageDown();
2085
2086        assert_eq!(scroll_down, Message::ScrollMuxBoxDown());
2087        assert_eq!(scroll_up, Message::ScrollMuxBoxUp());
2088        assert_eq!(scroll_left, Message::ScrollMuxBoxLeft());
2089        assert_eq!(scroll_right, Message::ScrollMuxBoxRight());
2090        assert_eq!(scroll_page_up, Message::ScrollMuxBoxPageUp());
2091        assert_eq!(scroll_page_down, Message::ScrollMuxBoxPageDown());
2092
2093        assert_ne!(scroll_down, scroll_up);
2094        assert_ne!(scroll_left, scroll_right);
2095    }
2096
2097    /// Tests that navigation messages are created correctly.
2098    /// This test demonstrates the navigation message creation feature.
2099    #[test]
2100    fn test_navigation_messages() {
2101        let next_muxbox = Message::NextMuxBox();
2102        let previous_muxbox = Message::PreviousMuxBox();
2103
2104        assert_eq!(next_muxbox, Message::NextMuxBox());
2105        assert_eq!(previous_muxbox, Message::PreviousMuxBox());
2106        assert_ne!(next_muxbox, previous_muxbox);
2107    }
2108
2109    /// Tests that box refresh messages are created correctly.
2110    /// This test demonstrates the box refresh message feature.
2111    #[test]
2112    fn test_box_refresh_messages() {
2113        let box_id = "test_box".to_string();
2114        let start_refresh = Message::StartBoxRefresh(box_id.clone());
2115        let stop_refresh = Message::StopBoxRefresh(box_id.clone());
2116        let event_refresh = Message::MuxBoxEventRefresh(box_id.clone());
2117
2118        match start_refresh {
2119            Message::StartBoxRefresh(id) => assert_eq!(id, box_id),
2120            _ => panic!("Expected StartBoxRefresh"),
2121        }
2122
2123        match stop_refresh {
2124            Message::StopBoxRefresh(id) => assert_eq!(id, box_id),
2125            _ => panic!("Expected StopBoxRefresh"),
2126        }
2127
2128        match event_refresh {
2129            Message::MuxBoxEventRefresh(id) => assert_eq!(id, box_id),
2130            _ => panic!("Expected MuxBoxEventRefresh"),
2131        }
2132    }
2133
2134    /// Tests that RedrawMuxBox message is created correctly.
2135    /// This test demonstrates the muxbox redraw message feature.
2136    #[test]
2137    fn test_redraw_muxbox_message() {
2138        let muxbox_id = "test_muxbox".to_string();
2139        let redraw_msg = Message::RedrawMuxBox(muxbox_id.clone());
2140
2141        match redraw_msg {
2142            Message::RedrawMuxBox(id) => assert_eq!(id, muxbox_id),
2143            _ => panic!("Expected RedrawMuxBox message"),
2144        }
2145    }
2146}