diff options
Diffstat (limited to 'zellij-server/src/tab/unit/tab_integration_tests.rs')
-rw-r--r-- | zellij-server/src/tab/unit/tab_integration_tests.rs | 336 |
1 files changed, 330 insertions, 6 deletions
diff --git a/zellij-server/src/tab/unit/tab_integration_tests.rs b/zellij-server/src/tab/unit/tab_integration_tests.rs index bb6dbf653..2496f89d0 100644 --- a/zellij-server/src/tab/unit/tab_integration_tests.rs +++ b/zellij-server/src/tab/unit/tab_integration_tests.rs @@ -17,6 +17,9 @@ use zellij_utils::ipc::IpcReceiverWithContext; use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::position::Position; +use crate::pty_writer::PtyWriteInstruction; +use zellij_utils::channels::{self, ChannelWithContext, SenderWithContext}; + use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::os::unix::io::RawFd; @@ -150,6 +153,63 @@ fn create_new_tab(size: Size, default_mode: ModeInfo) -> Tab { tab } +fn create_new_tab_with_mock_pty_writer( + size: Size, + default_mode: ModeInfo, + mock_pty_writer: SenderWithContext<PtyWriteInstruction>, +) -> Tab { + set_session_name("test".into()); + let index = 0; + let position = 0; + let name = String::new(); + let os_api = Box::new(FakeInputOutput { + file_dumps: Arc::new(Mutex::new(HashMap::new())), + }); + let mut senders = ThreadSenders::default().silently_fail_on_send(); + senders.replace_to_pty_writer(mock_pty_writer); + let max_panes = None; + let mode_info = default_mode; + let style = Style::default(); + let draw_pane_frames = true; + let client_id = 1; + let session_is_mirrored = true; + let mut connected_clients = HashSet::new(); + connected_clients.insert(client_id); + let connected_clients = Rc::new(RefCell::new(connected_clients)); + let character_cell_info = Rc::new(RefCell::new(None)); + let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default())); + let copy_options = CopyOptions::default(); + let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new())); + let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default())); + let mut tab = Tab::new( + index, + position, + name, + size, + character_cell_info, + sixel_image_store, + os_api, + senders, + max_panes, + style, + mode_info, + draw_pane_frames, + connected_clients, + session_is_mirrored, + client_id, + copy_options, + terminal_emulator_colors, + terminal_emulator_color_codes, + ); + tab.apply_layout( + LayoutTemplate::default().try_into().unwrap(), + vec![1], + index, + client_id, + ); + tab +} + fn create_new_tab_with_sixel_support( size: Size, sixel_image_store: Rc<RefCell<SixelImageStore>>, @@ -846,7 +906,7 @@ fn move_floating_pane_focus_with_mouse() { tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); tab.handle_left_click(&Position::new(9, 71), client_id); - tab.handle_mouse_release(&Position::new(9, 71), client_id); + tab.handle_left_mouse_release(&Position::new(9, 71), client_id); tab.render(&mut output, None); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), @@ -892,7 +952,7 @@ fn move_pane_focus_with_mouse_to_non_floating_pane() { tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); tab.handle_left_click(&Position::new(4, 71), client_id); - tab.handle_mouse_release(&Position::new(4, 71), client_id); + tab.handle_left_mouse_release(&Position::new(4, 71), client_id); tab.render(&mut output, None); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), @@ -938,7 +998,7 @@ fn drag_pane_with_mouse() { tab.handle_pty_bytes(5, Vec::from("\u{1b}#8".as_bytes())); tab.handle_pty_bytes(6, Vec::from("\u{1b}#8".as_bytes())); tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_release(&Position::new(7, 75), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); tab.render(&mut output, None); let (snapshot, cursor_coordinates) = take_snapshot_and_cursor_position( output.serialize().get(&client_id).unwrap(), @@ -988,7 +1048,7 @@ fn mark_text_inside_floating_pane() { tab.selecting_with_mouse, "started selecting with mouse on click" ); - tab.handle_mouse_release(&Position::new(8, 50), client_id); + tab.handle_left_mouse_release(&Position::new(8, 50), client_id); assert!( !tab.selecting_with_mouse, "stopped selecting with mouse on release" @@ -1357,7 +1417,7 @@ fn move_floating_pane_with_sixel_image() { let fixture = read_fixture("sixel-image-500px.six"); tab.handle_pty_bytes(2, fixture); tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_release(&Position::new(7, 75), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); tab.render(&mut output, None); let snapshot = take_snapshot_with_sixel( @@ -1392,7 +1452,7 @@ fn floating_pane_above_sixel_image() { let fixture = read_fixture("sixel-image-500px.six"); tab.handle_pty_bytes(1, fixture); tab.handle_left_click(&Position::new(5, 71), client_id); - tab.handle_mouse_release(&Position::new(7, 75), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); tab.render(&mut output, None); let snapshot = take_snapshot_with_sixel( @@ -1720,3 +1780,267 @@ fn enter_search_floating_pane() { ); assert_snapshot!("search_floating_tab_highlight_fring", snapshot); } + +#[test] +fn pane_in_sgr_button_event_tracking_mouse_mode() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + + let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); + let (to_pty_writer, pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> = + 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); + + // 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); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); + tab.handle_right_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id); + tab.handle_middle_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); + tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id); + tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + assert_eq!( + *messages_to_pty_writer.lock().unwrap(), + vec![ + "\u{1b}[<0;71;5M".to_string(), // SGR left click + "\u{1b}[<32;72;9M".to_string(), // SGR left click (hold) + "\u{1b}[<0;75;7m".to_string(), // SGR left button release + "\u{1b}[<2;71;5M".to_string(), // SGR right click + "\u{1b}[<34;72;9M".to_string(), // SGR right click (hold) + "\u{1b}[<2;75;7m".to_string(), // SGR right button release + "\u{1b}[<1;71;5M".to_string(), // SGR middle click + "\u{1b}[<33;72;9M".to_string(), // SGR middle click (hold) + "\u{1b}[<1;75;7m".to_string(), // SGR middle button release + "\u{1b}[<64;71;5M".to_string(), // SGR scroll up + "\u{1b}[<65;71;5M".to_string(), // SGR scroll down + ] + ); +} + +#[test] +fn pane_in_sgr_normal_event_tracking_mouse_mode() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + + let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); + let (to_pty_writer, pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> = + 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); + + // 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); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); + tab.handle_right_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id); + tab.handle_middle_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); + tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id); + tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + assert_eq!( + *messages_to_pty_writer.lock().unwrap(), + vec![ + "\u{1b}[<0;71;5M".to_string(), // SGR left click + // no hold event here, as hold events are not reported in normal mode + "\u{1b}[<0;75;7m".to_string(), // SGR left button release + "\u{1b}[<2;71;5M".to_string(), // SGR right click + // no hold event here, as hold events are not reported in normal mode + "\u{1b}[<2;75;7m".to_string(), // SGR right button release + "\u{1b}[<1;71;5M".to_string(), // SGR middle click + // no hold event here, as hold events are not reported in normal mode + "\u{1b}[<1;75;7m".to_string(), // SGR middle button release + "\u{1b}[<64;71;5M".to_string(), // SGR scroll up + "\u{1b}[<65;71;5M".to_string(), // SGR scroll down + ] + ); +} + +#[test] +fn pane_in_utf8_button_event_tracking_mouse_mode() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + + let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); + let (to_pty_writer, pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> = + 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); + + // 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); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); + tab.handle_right_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id); + tab.handle_middle_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); + tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id); + tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + assert_eq!( + *messages_to_pty_writer.lock().unwrap(), + vec![ + "\u{1b}[M g%".to_string(), // utf8 left click + "\u{1b}[M@h)".to_string(), // utf8 left click (hold) + "\u{1b}[M#k'".to_string(), // utf8 left button release + "\u{1b}[M\"g%".to_string(), // utf8 right click + "\u{1b}[MBh)".to_string(), // utf8 right click (hold) + "\u{1b}[M#k'".to_string(), // utf8 right button release + "\u{1b}[M!g%".to_string(), // utf8 middle click + "\u{1b}[MAh)".to_string(), // utf8 middle click (hold) + "\u{1b}[M#k'".to_string(), // utf8 middle click release + "\u{1b}[M`g%".to_string(), // utf8 scroll up + "\u{1b}[Mag%".to_string(), // utf8 scroll down + ] + ); +} + +#[test] +fn pane_in_utf8_normal_event_tracking_mouse_mode() { + let size = Size { + cols: 121, + rows: 20, + }; + let client_id = 1; + + let messages_to_pty_writer = Arc::new(Mutex::new(vec![])); + let (to_pty_writer, pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> = + 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); + + // 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); + tab.handle_mouse_hold_left(&Position::new(9, 72), client_id); + tab.handle_left_mouse_release(&Position::new(7, 75), client_id); + tab.handle_right_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_right(&Position::new(9, 72), client_id); + tab.handle_right_mouse_release(&Position::new(7, 75), client_id); + tab.handle_middle_click(&Position::new(5, 71), client_id); + tab.handle_mouse_hold_middle(&Position::new(9, 72), client_id); + tab.handle_middle_mouse_release(&Position::new(7, 75), client_id); + tab.scroll_terminal_up(&Position::new(5, 71), 1, client_id); + tab.scroll_terminal_down(&Position::new(5, 71), 1, client_id); + std::thread::sleep(std::time::Duration::from_millis(100)); // give time for messages to arrive + assert_eq!( + *messages_to_pty_writer.lock().unwrap(), + vec![ + "\u{1b}[M g%".to_string(), // utf8 left click + // no hold event here, as hold events are not reported in normal mode + "\u{1b}[M#k'".to_string(), // utf8 left button release + "\u{1b}[M\"g%".to_string(), // utf8 right click + // no hold event here, as hold events are not reported in normal mode + "\u{1b}[M#k'".to_string(), // utf8 right button release + "\u{1b}[M!g%".to_string(), // utf8 middle click + // no hold event here, as hold events are not reported in normal mode + "\u{1b}[M#k'".to_string(), // utf8 middle click release + "\u{1b}[M`g%".to_string(), // utf8 scroll up + "\u{1b}[Mag%".to_string(), // utf8 scroll down + ] + ); +} |