From 480086e3d456cb4984c70f50f7290fc06ad5b60a Mon Sep 17 00:00:00 2001 From: Thomas Linford Date: Thu, 15 Sep 2022 20:35:36 +0200 Subject: [PATCH] test: simplify tab integration tests (#1728) * wip * refactor existing tests * rename methods --- .../src/tab/unit/tab_integration_tests.rs | 292 ++++++++---------- 1 file changed, 127 insertions(+), 165 deletions(-) diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index 2803529f..14a9c9e1 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -11,7 +11,9 @@ use crate::{ }; use std::convert::TryInto; use std::path::PathBuf; +use zellij_utils::channels::Receiver; use zellij_utils::envs::set_session_name; +use zellij_utils::errors::ErrorContext; use zellij_utils::input::layout::LayoutTemplate; use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; @@ -104,6 +106,69 @@ impl ServerOsApi for FakeInputOutput { } } +struct MockPtyInstructionBus { + output: Arc>>, + pty_writer_sender: SenderWithContext, + pty_writer_receiver: Arc>, + handle: Option>, +} + +impl MockPtyInstructionBus { + fn new() -> Self { + let output = Arc::new(Mutex::new(vec![])); + let (pty_writer_sender, pty_writer_receiver): ChannelWithContext = + channels::unbounded(); + let pty_writer_sender = SenderWithContext::new(pty_writer_sender); + let pty_writer_receiver = Arc::new(pty_writer_receiver); + + Self { + output, + pty_writer_sender, + pty_writer_receiver, + handle: None, + } + } + + fn start(&mut self) { + let output = self.output.clone(); + let pty_writer_receiver = self.pty_writer_receiver.clone(); + let handle = std::thread::Builder::new() + .name("pty_writer".to_string()) + .spawn({ + move || loop { + let (event, _err_ctx) = pty_writer_receiver + .recv() + .expect("failed to receive event on channel"); + match event { + PtyWriteInstruction::Write(msg, _) => output + .lock() + .unwrap() + .push(String::from_utf8_lossy(&msg).to_string()), + PtyWriteInstruction::Exit => break, + } + } + }) + .unwrap(); + self.handle = Some(handle); + } + + fn exit(&mut self) { + self.pty_writer_sender + .send(PtyWriteInstruction::Exit) + .unwrap(); + let handle = self.handle.take().unwrap(); + handle.join().unwrap(); + } + + fn pty_write_sender(&self) -> SenderWithContext { + self.pty_writer_sender.clone() + } + + fn clone_output(&self) -> Vec { + self.output.lock().unwrap().clone() + } +} + // TODO: move to shared thingy with other test file fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { set_session_name("test".into()); @@ -1793,32 +1858,14 @@ fn pane_in_sgr_button_event_tracking_mouse_mode() { }; let client_id = 1; - let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); - let (to_pty_writer, pty_writer_receiver): ChannelWithContext = - channels::unbounded(); - let to_pty_writer = SenderWithContext::new(to_pty_writer); - let mut tab = create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer); + let mut pty_instruction_bus = MockPtyInstructionBus::new(); + let mut tab = create_new_tab_with_mock_pty_writer( + size, + ModeInfo::default(), + pty_instruction_bus.pty_write_sender(), + ); + pty_instruction_bus.start(); - // TODO: note that this thread does not die when the test dies - // it only dies once all the test process exits... not a biggy if we have only a handful of - // these, but otherwise we might want to think of a better way to handle this - let _pty_writer_thread = std::thread::Builder::new() - .name("pty_writer".to_string()) - .spawn({ - // TODO: kill this thread - let messages_to_pty_writer = messages_to_pty_writer.clone(); - move || loop { - let (event, _err_ctx) = pty_writer_receiver - .recv() - .expect("failed to receive event on channel"); - if let PtyWriteInstruction::Write(msg, _) = event { - messages_to_pty_writer - .lock() - .unwrap() - .push(String::from_utf8_lossy(&msg).to_string()); - } - } - }); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1002;1006h"); // button event tracking (1002) with SGR encoding (1006) tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); tab.handle_left_click(&Position::new(5, 71), client_id); @@ -1832,9 +1879,11 @@ fn pane_in_sgr_button_event_tracking_mouse_mode() { tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); - std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + + pty_instruction_bus.exit(); + assert_eq!( - *messages_to_pty_writer.lock().unwrap(), + pty_instruction_bus.clone_output(), vec![ "\u{1b}[<0;71;5M".to_string(), // SGR left click "\u{1b}[<32;72;9M".to_string(), // SGR left click (hold) @@ -1859,32 +1908,14 @@ fn pane_in_sgr_normal_event_tracking_mouse_mode() { }; let client_id = 1; - let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); - let (to_pty_writer, pty_writer_receiver): ChannelWithContext = - channels::unbounded(); - let to_pty_writer = SenderWithContext::new(to_pty_writer); - let mut tab = create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer); + let mut pty_instruction_bus = MockPtyInstructionBus::new(); + let mut tab = create_new_tab_with_mock_pty_writer( + size, + ModeInfo::default(), + pty_instruction_bus.pty_write_sender(), + ); + pty_instruction_bus.start(); - // TODO: note that this thread does not die when the test dies - // it only dies once all the test process exits... not a biggy if we have only a handful of - // these, but otherwise we might want to think of a better way to handle this - let _pty_writer_thread = std::thread::Builder::new() - .name("pty_writer".to_string()) - .spawn({ - // TODO: kill this thread - let messages_to_pty_writer = messages_to_pty_writer.clone(); - move || loop { - let (event, _err_ctx) = pty_writer_receiver - .recv() - .expect("failed to receive event on channel"); - if let PtyWriteInstruction::Write(msg, _) = event { - messages_to_pty_writer - .lock() - .unwrap() - .push(String::from_utf8_lossy(&msg).to_string()); - } - } - }); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1000;1006h"); // normal event tracking (1000) with sgr encoding (1006) tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); tab.handle_left_click(&Position::new(5, 71), client_id); @@ -1898,9 +1929,11 @@ fn pane_in_sgr_normal_event_tracking_mouse_mode() { tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); - std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + + pty_instruction_bus.exit(); + assert_eq!( - *messages_to_pty_writer.lock().unwrap(), + pty_instruction_bus.clone_output(), vec![ "\u{1b}[<0;71;5M".to_string(), // SGR left click // no hold event here, as hold events are not reported in normal mode @@ -1925,32 +1958,14 @@ fn pane_in_utf8_button_event_tracking_mouse_mode() { }; let client_id = 1; - let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); - let (to_pty_writer, pty_writer_receiver): ChannelWithContext = - channels::unbounded(); - let to_pty_writer = SenderWithContext::new(to_pty_writer); - let mut tab = create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer); + let mut pty_instruction_bus = MockPtyInstructionBus::new(); + let mut tab = create_new_tab_with_mock_pty_writer( + size, + ModeInfo::default(), + pty_instruction_bus.pty_write_sender(), + ); + pty_instruction_bus.start(); - // TODO: note that this thread does not die when the test dies - // it only dies once all the test process exits... not a biggy if we have only a handful of - // these, but otherwise we might want to think of a better way to handle this - let _pty_writer_thread = std::thread::Builder::new() - .name("pty_writer".to_string()) - .spawn({ - // TODO: kill this thread - let messages_to_pty_writer = messages_to_pty_writer.clone(); - move || loop { - let (event, _err_ctx) = pty_writer_receiver - .recv() - .expect("failed to receive event on channel"); - if let PtyWriteInstruction::Write(msg, _) = event { - messages_to_pty_writer - .lock() - .unwrap() - .push(String::from_utf8_lossy(&msg).to_string()); - } - } - }); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1002;1005h"); // button event tracking (1002) with utf8 encoding (1005) tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); tab.handle_left_click(&Position::new(5, 71), client_id); @@ -1964,9 +1979,11 @@ fn pane_in_utf8_button_event_tracking_mouse_mode() { tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); - std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + + pty_instruction_bus.exit(); + assert_eq!( - *messages_to_pty_writer.lock().unwrap(), + pty_instruction_bus.clone_output(), vec![ "\u{1b}[M g%".to_string(), // utf8 left click "\u{1b}[M@h)".to_string(), // utf8 left click (hold) @@ -1991,32 +2008,14 @@ fn pane_in_utf8_normal_event_tracking_mouse_mode() { }; let client_id = 1; - let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); - let (to_pty_writer, pty_writer_receiver): ChannelWithContext = - channels::unbounded(); - let to_pty_writer = SenderWithContext::new(to_pty_writer); - let mut tab = create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer); + let mut pty_instruction_bus = MockPtyInstructionBus::new(); + let mut tab = create_new_tab_with_mock_pty_writer( + size, + ModeInfo::default(), + pty_instruction_bus.pty_write_sender(), + ); + pty_instruction_bus.start(); - // TODO: note that this thread does not die when the test dies - // it only dies once all the test process exits... not a biggy if we have only a handful of - // these, but otherwise we might want to think of a better way to handle this - let _pty_writer_thread = std::thread::Builder::new() - .name("pty_writer".to_string()) - .spawn({ - // TODO: kill this thread - let messages_to_pty_writer = messages_to_pty_writer.clone(); - move || loop { - let (event, _err_ctx) = pty_writer_receiver - .recv() - .expect("failed to receive event on channel"); - if let PtyWriteInstruction::Write(msg, _) = event { - messages_to_pty_writer - .lock() - .unwrap() - .push(String::from_utf8_lossy(&msg).to_string()); - } - } - }); let sgr_mouse_mode_any_button = String::from("\u{1b}[?1000;1005h"); // normal event tracking (1000) with sgr encoding (1006) tab.handle_pty_bytes(1, sgr_mouse_mode_any_button.as_bytes().to_vec()); tab.handle_left_click(&Position::new(5, 71), client_id); @@ -2030,9 +2029,11 @@ fn pane_in_utf8_normal_event_tracking_mouse_mode() { tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); tab.handle_scrollwheel_up(&Position::new(5, 71), 1, client_id); tab.handle_scrollwheel_down(&Position::new(5, 71), 1, client_id); - std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + + pty_instruction_bus.exit(); + assert_eq!( - *messages_to_pty_writer.lock().unwrap(), + pty_instruction_bus.clone_output(), vec![ "\u{1b}[M g%".to_string(), // utf8 left click // no hold event here, as hold events are not reported in normal mode @@ -2058,43 +2059,23 @@ fn pane_bracketed_paste_ignored_when_not_in_bracketed_paste_mode() { }; let client_id: u16 = 1; - let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); - let (to_pty_writer, pty_writer_receiver): ChannelWithContext = - channels::unbounded(); - let to_pty_writer = SenderWithContext::new(to_pty_writer); - let mut tab = - create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer.clone()); + let mut pty_instruction_bus = MockPtyInstructionBus::new(); + let mut tab = create_new_tab_with_mock_pty_writer( + size, + ModeInfo::default(), + pty_instruction_bus.pty_write_sender(), + ); + pty_instruction_bus.start(); - let _pty_writer_thread = std::thread::Builder::new() - .name("pty_writer".to_string()) - .spawn({ - let messages_to_pty_writer = messages_to_pty_writer.clone(); - move || loop { - let (event, _err_ctx) = pty_writer_receiver - .recv() - .expect("failed to receive event on channel"); - match event { - PtyWriteInstruction::Write(msg, _) => messages_to_pty_writer - .lock() - .unwrap() - .push(String::from_utf8_lossy(&msg).to_string()), - PtyWriteInstruction::Exit => break, - } - } - }); let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~ let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201 tab.write_to_active_terminal(bracketed_paste_start, client_id); tab.write_to_active_terminal("test".as_bytes().to_vec(), client_id); tab.write_to_active_terminal(bracketed_paste_end, client_id); - to_pty_writer.send(PtyWriteInstruction::Exit).unwrap(); + pty_instruction_bus.exit(); - std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive - assert_eq!( - *messages_to_pty_writer.lock().unwrap(), - vec!["", "test", ""] - ); + assert_eq!(pty_instruction_bus.clone_output(), vec!["", "test", ""]); } #[test] @@ -2106,30 +2087,13 @@ fn pane_faux_scrolling_in_alternate_mode() { let client_id: u16 = 1; let lines_to_scroll = 3; - let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); - let (to_pty_writer, pty_writer_receiver): ChannelWithContext = - channels::unbounded(); - let to_pty_writer = SenderWithContext::new(to_pty_writer); - let mut tab = - create_new_tab_with_mock_pty_writer(size, ModeInfo::default(), to_pty_writer.clone()); - - let _pty_writer_thread = std::thread::Builder::new() - .name("pty_writer".to_string()) - .spawn({ - let messages_to_pty_writer = messages_to_pty_writer.clone(); - move || loop { - let (event, _err_ctx) = pty_writer_receiver - .recv() - .expect("failed to receive event on channel"); - match event { - PtyWriteInstruction::Write(msg, _) => messages_to_pty_writer - .lock() - .unwrap() - .push(String::from_utf8_lossy(&msg).to_string()), - PtyWriteInstruction::Exit => break, - } - } - }); + let mut pty_instruction_bus = MockPtyInstructionBus::new(); + let mut tab = create_new_tab_with_mock_pty_writer( + size, + ModeInfo::default(), + pty_instruction_bus.pty_write_sender(), + ); + pty_instruction_bus.start(); let enable_alternate_screen = String::from("\u{1b}[?1049h"); // CSI ? 1049 h -> switch to the Alternate Screen Buffer let set_application_mode = String::from("\u{1b}[?1h"); @@ -2148,9 +2112,7 @@ fn pane_faux_scrolling_in_alternate_mode() { tab.handle_scrollwheel_up(&Position::new(1, 1), lines_to_scroll, client_id); tab.handle_scrollwheel_down(&Position::new(1, 1), lines_to_scroll, client_id); - to_pty_writer.send(PtyWriteInstruction::Exit).unwrap(); - - std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + pty_instruction_bus.exit(); let mut expected: Vec<&str> = Vec::new(); expected.append(&mut vec!["\u{1b}[A"; lines_to_scroll]); @@ -2158,5 +2120,5 @@ fn pane_faux_scrolling_in_alternate_mode() { expected.append(&mut vec!["\u{1b}OA"; lines_to_scroll]); expected.append(&mut vec!["\u{1b}OB"; lines_to_scroll]); - assert_eq!(*messages_to_pty_writer.lock().unwrap(), expected); + assert_eq!(pty_instruction_bus.clone_output(), expected); }