diff options
| author | Aram Drevekenin <aram@poor.dev> | 2021-09-27 11:29:13 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-09-27 11:29:13 +0200 |
| commit | 5c54bf18c21e60d82ef063b62337f6b545d914d3 (patch) | |
| tree | 4af736bfd7b05ae2402930f8f41e3e9f669d3a6f /src/tests | |
| parent | chore(docs): update changelog (diff) | |
| download | sl0ppij-5c54bf18c21e60d82ef063b62337f6b545d914d3.tar.gz sl0ppij-5c54bf18c21e60d82ef063b62337f6b545d914d3.tar.xz sl0ppij-5c54bf18c21e60d82ef063b62337f6b545d914d3.zip | |
feat(sessions): mirrored sessions (#740)
* feat(sessions): mirrored sessions
* fix(tests): input units
* style(fmt): make rustfmt happy
* fix(tests): make mirrored sessions e2e test more robust
* refactor(sessions): remove force attach
* style(fmt): rustfmtify
* docs(changelog): update change
* fix(e2e): retry on all errors
Diffstat (limited to 'src/tests')
4 files changed, 252 insertions, 55 deletions
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs index 307986c1..77892463 100644 --- a/src/tests/e2e/cases.rs +++ b/src/tests/e2e/cases.rs @@ -153,20 +153,12 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { }, }) .add_step(Step { - name: "Send text to terminal", - instruction: |mut remote_terminal: RemoteTerminal| -> bool { - // this is just normal input that should be sent into the one terminal so that we can make - // sure we silently failed to split in the previous step - remote_terminal.send_key("Hi!".as_bytes()); - true - }, - }) - .add_step(Step { - name: "Wait for text to appear", + name: "Make sure only one pane appears", instruction: |remote_terminal: RemoteTerminal| -> bool { let mut step_is_complete = false; - if remote_terminal.cursor_position_is(6, 2) && remote_terminal.snapshot_contains("Hi!") + if remote_terminal.cursor_position_is(3, 2) && remote_terminal.snapshot_contains("...") { + // ... is the truncated tip line step_is_complete = true; } step_is_complete @@ -917,3 +909,85 @@ pub fn start_without_pane_frames() { .run_all_steps(); assert_snapshot!(last_snapshot); } + +#[test] +#[ignore] +pub fn mirrored_sessions() { + let fake_win_size = Size { + cols: 120, + rows: 24, + }; + let mut test_attempts = 10; + let session_name = "mirrored_sessions"; + let mut last_snapshot = None; + loop { + // we run this test in a loop because there are some edge cases (especially in the CI) + // where the second runner times out and then we also need to restart the first runner + // if no test timed out, we break the loop and assert the snapshot + let mut first_runner = + RemoteRunner::new_with_session_name("mirrored_sessions", fake_win_size, session_name) + .add_step(Step { + name: "Split pane to the right", + instruction: |mut remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.status_bar_appears() + && remote_terminal.cursor_position_is(3, 2) + { + remote_terminal.send_key(&PANE_MODE); + remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE); + // back to normal mode after split + remote_terminal.send_key(&ENTER); + step_is_complete = true; + } + step_is_complete + }, + }) + .add_step(Step { + name: "Wait for new pane to open", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) + && remote_terminal.tip_appears() + { + // cursor is in the newly opened second pane + step_is_complete = true; + } + step_is_complete + }, + }); + first_runner.run_all_steps(); + + let mut second_runner = + RemoteRunner::new_existing_session("mirrored_sessions", fake_win_size, session_name) + .add_step(Step { + name: "Make sure session appears correctly", + instruction: |remote_terminal: RemoteTerminal| -> bool { + let mut step_is_complete = false; + if remote_terminal.cursor_position_is(63, 2) + && remote_terminal.tip_appears() + { + // cursor is in the newly opened second pane + step_is_complete = true; + } + step_is_complete + }, + }); + let last_test_snapshot = second_runner.run_all_steps(); + + if (first_runner.test_timed_out || second_runner.test_timed_out) && test_attempts >= 0 { + test_attempts -= 1; + continue; + } else { + last_snapshot = Some(last_test_snapshot); + break; + } + } + match last_snapshot { + Some(last_snapshot) => { + assert_snapshot!(last_snapshot); + } + None => { + panic!("test timed out before completing"); + } + } +} diff --git a/src/tests/e2e/remote_runner.rs b/src/tests/e2e/remote_runner.rs index 7c4587f3..924babf6 100644 --- a/src/tests/e2e/remote_runner.rs +++ b/src/tests/e2e/remote_runner.rs @@ -10,7 +10,7 @@ use std::net::TcpStream; use std::path::Path; -const ZELLIJ_EXECUTABLE_LOCATION: &str = "/usr/src/zellij/x86_64-unknown-linux-musl/debug/zellij"; +const ZELLIJ_EXECUTABLE_LOCATION: &str = "/usr/src/zellij/x86_64-unknown-linux-musl/release/zellij"; const ZELLIJ_LAYOUT_PATH: &str = "/usr/src/zellij/fixtures/layouts"; const CONNECTION_STRING: &str = "127.0.0.1:2222"; const CONNECTION_USERNAME: &str = "test"; @@ -24,7 +24,7 @@ fn ssh_connect() -> ssh2::Session { sess.handshake().unwrap(); sess.userauth_password(CONNECTION_USERNAME, CONNECTION_PASSWORD) .unwrap(); - sess.set_timeout(20000); + sess.set_timeout(3000); sess } @@ -58,6 +58,27 @@ fn start_zellij(channel: &mut ssh2::Channel) { channel.flush().unwrap(); } +fn start_zellij_in_session(channel: &mut ssh2::Channel, session_name: &str) { + stop_zellij(channel); + channel + .write_all( + format!( + "{} --session {}\n", + ZELLIJ_EXECUTABLE_LOCATION, session_name + ) + .as_bytes(), + ) + .unwrap(); + channel.flush().unwrap(); +} + +fn attach_to_existing_session(channel: &mut ssh2::Channel, session_name: &str) { + channel + .write_all(format!("{} attach {}\n", ZELLIJ_EXECUTABLE_LOCATION, session_name).as_bytes()) + .unwrap(); + channel.flush().unwrap(); +} + fn start_zellij_without_frames(channel: &mut ssh2::Channel) { stop_zellij(channel); channel @@ -188,6 +209,9 @@ pub struct RemoteRunner { win_size: Size, layout_file_name: Option<&'static str>, without_frames: bool, + session_name: Option<String>, + attach_to_existing: bool, + pub test_timed_out: bool, } impl RemoteRunner { @@ -216,10 +240,89 @@ impl RemoteRunner { test_name, currently_running_step: None, current_step_index: 0, - retries_left: 3, + retries_left: 10, + win_size, + layout_file_name: None, + without_frames: false, + session_name: None, + attach_to_existing: false, + test_timed_out: false, + } + } + pub fn new_with_session_name( + test_name: &'static str, + win_size: Size, + session_name: &str, + ) -> Self { + let sess = ssh_connect(); + let mut channel = sess.channel_session().unwrap(); + let vte_parser = vte::Parser::new(); + let mut rows = Dimension::fixed(win_size.rows); + let mut cols = Dimension::fixed(win_size.cols); + rows.set_inner(win_size.rows); + cols.set_inner(win_size.cols); + let pane_geom = PaneGeom { + x: 0, + y: 0, + rows, + cols, + }; + let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index + setup_remote_environment(&mut channel, win_size); + start_zellij_in_session(&mut channel, &session_name); + RemoteRunner { + steps: vec![], + channel, + terminal_output, + vte_parser, + test_name, + currently_running_step: None, + current_step_index: 0, + retries_left: 10, + win_size, + layout_file_name: None, + without_frames: false, + session_name: Some(String::from(session_name)), + attach_to_existing: false, + test_timed_out: false, + } + } + pub fn new_existing_session( + test_name: &'static str, + win_size: Size, + session_name: &str, + ) -> Self { + let sess = ssh_connect(); + let mut channel = sess.channel_session().unwrap(); + let vte_parser = vte::Parser::new(); + let mut rows = Dimension::fixed(win_size.rows); + let mut cols = Dimension::fixed(win_size.cols); + rows.set_inner(win_size.rows); + cols.set_inner(win_size.cols); + let pane_geom = PaneGeom { + x: 0, + y: 0, + rows, + cols, + }; + let terminal_output = TerminalPane::new(0, pane_geom, Palette::default(), 0); // 0 is the pane index + setup_remote_environment(&mut channel, win_size); + attach_to_existing_session(&mut channel, &session_name); + RemoteRunner { + steps: vec![], + channel, + terminal_output, + vte_parser, + test_name, + currently_running_step: None, + current_step_index: 0, + retries_left: 10, win_size, layout_file_name: None, without_frames: false, + session_name: Some(String::from(session_name)), + attach_to_existing: true, + test_timed_out: false, } } pub fn new_without_frames(test_name: &'static str, win_size: Size) -> Self { @@ -247,10 +350,13 @@ impl RemoteRunner { test_name, currently_running_step: None, current_step_index: 0, - retries_left: 3, + retries_left: 10, win_size, layout_file_name: None, without_frames: true, + session_name: None, + attach_to_existing: false, + test_timed_out: false, } } pub fn new_with_layout( @@ -283,10 +389,13 @@ impl RemoteRunner { test_name, currently_running_step: None, current_step_index: 0, - retries_left: 3, + retries_left: 10, win_size, layout_file_name: Some(layout_file_name), without_frames: false, + session_name: None, + attach_to_existing: false, + test_timed_out: false, } } pub fn add_step(mut self, step: Step) -> Self { @@ -296,16 +405,6 @@ impl RemoteRunner { pub fn replace_steps(&mut self, steps: Vec<Step>) { self.steps = steps; } - fn current_remote_terminal_state(&mut self) -> RemoteTerminal { - let current_snapshot = self.get_current_snapshot(); - let (cursor_x, cursor_y) = self.terminal_output.cursor_coordinates().unwrap_or((0, 0)); - RemoteTerminal { - cursor_x, - cursor_y, - current_snapshot, - channel: &mut self.channel, - } - } pub fn run_next_step(&mut self) { if let Some(next_step) = self.steps.get(self.current_step_index) { let current_snapshot = take_snapshot(&mut self.terminal_output); @@ -341,6 +440,24 @@ impl RemoteRunner { new_runner.replace_steps(self.steps.clone()); drop(std::mem::replace(self, new_runner)); self.run_all_steps() + } else if self.session_name.is_some() { + let mut new_runner = if self.attach_to_existing { + RemoteRunner::new_existing_session( + self.test_name, + self.win_size, + &self.session_name.as_ref().unwrap(), + ) + } else { + RemoteRunner::new_with_session_name( + self.test_name, + self.win_size, + &self.session_name.as_ref().unwrap(), + ) + }; + new_runner.retries_left = self.retries_left - 1; + new_runner.replace_steps(self.steps.clone()); + drop(std::mem::replace(self, new_runner)); + self.run_all_steps() } else { let mut new_runner = RemoteRunner::new(self.test_name, self.win_size); new_runner.retries_left = self.retries_left - 1; @@ -349,22 +466,6 @@ impl RemoteRunner { self.run_all_steps() } } - fn display_informative_error(&mut self) { - let test_name = self.test_name; - let current_step_name = self.currently_running_step.as_ref().cloned(); - match current_step_name { - Some(current_step) => { - let remote_terminal = self.current_remote_terminal_state(); - eprintln!("Timed out waiting for data on the SSH channel for test {}. Was waiting for step: {}", test_name, current_step); - eprintln!("{:?}", remote_terminal); - } - None => { - let remote_terminal = self.current_remote_terminal_state(); - eprintln!("Timed out waiting for data on the SSH channel for test {}. Haven't begun running steps yet.", test_name); - eprintln!("{:?}", remote_terminal); - } - } - } pub fn run_all_steps(&mut self) -> String { // returns the last snapshot loop { @@ -381,23 +482,16 @@ impl RemoteRunner { break; } } - Err(e) => { - if e.kind() == std::io::ErrorKind::TimedOut { - if self.retries_left > 0 { - return self.restart_test(); - } - self.display_informative_error(); - panic!("Timed out waiting for test"); + Err(_e) => { + if self.retries_left > 0 { + return self.restart_test(); } - panic!("Error while reading remote session: {}", e); + self.test_timed_out = true; } } } take_snapshot(&mut self.terminal_output) } - pub fn get_current_snapshot(&mut self) -> String { - take_snapshot(&mut self.terminal_output) - } } impl Drop for RemoteRunner { diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap index d37f23e3..796a596b 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap @@ -5,7 +5,7 @@ expression: last_snapshot --- Zellij ┌──────┐ -│$ Hi!█│ +│$ █ │ │ │ │ │ │ │ @@ -22,4 +22,4 @@ expression: last_snapshot │ │ └──────┘ Ctrl + - + ... diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap new file mode 100644 index 00000000..3223680c --- /dev/null +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__mirrored_sessions.snap @@ -0,0 +1,29 @@ +--- +source: src/tests/e2e/cases.rs +expression: last_snapshot + +--- + Zellij (mirrored_sessions) Tab #1 +┌ Pane #1 ─────────────────────────────────────────────────┐┌ Pane #2 ─────────────────────────────────────────────────┐ +│$ ││$ █ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +│ ││ │ +└──────────────────────────────────────────────────────────┘└──────────────────────────────────────────────────────────┘ + Ctrl + <g> LOCK <p> PANE <t> TAB <n> RESIZE <s> SCROLL <o> SESSION <q> QUIT + Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes. |
