summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2021-06-21 10:45:18 +0200
committerGitHub <noreply@github.com>2021-06-21 10:45:18 +0200
commit3313634fe969a69925a5d32445ba14a5f79e5d87 (patch)
tree778f1badfeed4a26d58e3413180f41629134d35a
parent07ad2f54eaf146e3e4ff88c943ba4cca236bbc9b (diff)
Add e2e tests (#582)
* feature(tests): e2e tests * chore(build): github action * chore(build): fix workflow * chore(build): fix workflow * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * work * working * working * working * bring back the proper errors * make e2e flow run properly * style(fmt): make rustfmt happy * style(fmt): make rustfmt happy * run on everything just to test the workflow * bring back running behaviour on workflow
-rw-r--r--.github/workflows/e2e.yml49
-rw-r--r--CONTRIBUTING.md12
-rw-r--r--Cargo.lock132
-rw-r--r--Cargo.toml4
-rw-r--r--Makefile.toml17
-rw-r--r--docker-compose.yml21
-rw-r--r--src/tests/e2e/cases.rs716
-rw-r--r--src/tests/e2e/mod.rs2
-rw-r--r--src/tests/e2e/remote_runner.rs284
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__cannot_split_terminals_vertically_when_active_terminal_is_too_small.snap25
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_pane.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__close_tab.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__detach_and_attach_session.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__lock_mode.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__open_new_tab.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_pane.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__resize_terminal_window.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__split_terminals_vertically.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__starts_with_one_terminal.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__toggle_pane_fullscreen.snap29
-rw-r--r--src/tests/mod.rs1
-rw-r--r--src/tests/utils.rs4
23 files changed, 1576 insertions, 10 deletions
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
new file mode 100644
index 000000000..a81d4ae61
--- /dev/null
+++ b/.github/workflows/e2e.yml
@@ -0,0 +1,49 @@
+name: End to End tests
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+env:
+ CARGO_TERM_COLOR: always
+
+jobs:
+ test:
+ name: Bulild generic binary and run tests on it
+ runs-on: ubuntu-latest
+
+ services:
+ ssh:
+ image: ghcr.io/linuxserver/openssh-server
+ env:
+ PUID: 1000
+ PGID: 1000
+ TZ: Europe/Vienna
+ PASSWORD_ACCESS: true
+ USER_PASSWORD: test
+ USER_NAME: test
+ ports:
+ - 2222:2222
+ options: -v ${{ github.workspace }}/target:/usr/src/zellij --name ssh
+ steps:
+ - uses: actions/checkout@v2
+ - name: Add WASM target
+ run: rustup target add wasm32-wasi
+ - name: Install musl-tools
+ run: sudo apt-get install -y --no-install-recommends musl-tools
+ - name: Add musl target
+ run: rustup target add x86_64-unknown-linux-musl
+ - name: Install cargo-make
+ run: cargo install --debug cargo-make
+ - name: Build asset
+ run: cargo make build-e2e
+ - name: Restart ssh container
+ # we need to do this because otherwise the volume will not be mounted
+ # on the docker container, since it was created before the folder existed
+ uses: docker://docker
+ with:
+ args: docker restart ssh
+ - name: Test
+ run: cargo make e2e-test
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 95ac05ec4..d34367f33 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -44,6 +44,18 @@ cargo make manpage
To run `install` or `publish`, you'll need the package `binaryen` in the
version `wasm-opt --version` > 97, for it's command `wasm-opt`.
+## Running the end-to-end tests
+Zellij includes some end to end tests which test the whole application as a black-box from the outside.
+These tests work by running a docker container which contains the Zellij binary, connecting to it via ssh, sending some commands and comparing the output received against predefined snapshots.
+
+To run these tests locally, you'll need to have both `docker` and `docker-compose` installed.
+Once you do, in the repository root:
+1. `docker-compose up -d` will start up the docker container
+2. `cargo make build-e2e` will build the generic linux executable of Zellij in the target folder, which is shared with the container
+3. `cargo make e2e-test` will run the tests
+
+To re-run the tests after you've changed something in the code base, be sure to repeat steps 2 and 3.
+
## Looking for something to work on?
If you are new contributor to `Zellij` going through
diff --git a/Cargo.lock b/Cargo.lock
index 2996a8182..a2c525d65 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -313,6 +313,15 @@ dependencies = [
]
[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -504,7 +513,7 @@ dependencies = [
"lazy_static",
"libc",
"mio",
- "parking_lot",
+ "parking_lot 0.11.1",
"signal-hook 0.1.17",
"winapi",
]
@@ -902,6 +911,7 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1b21a2971cea49ca4613c0e9fe8225ecaf5de64090fddc6002284726e9244"
dependencies = [
+ "backtrace",
"console",
"lazy_static",
"serde",
@@ -1018,6 +1028,32 @@ dependencies = [
]
[[package]]
+name = "libssh2-sys"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1025,6 +1061,15 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "lock_api"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
@@ -1185,6 +1230,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
[[package]]
+name = "openssl-sys"
+version = "0.9.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6b0d6fb7d80f877617dfcb014e605e2b5ab2fb0afdf27935219bb6bd984cb98"
+dependencies = [
+ "autocfg",
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1192,13 +1250,37 @@ checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "parking_lot"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
+dependencies = [
+ "lock_api 0.3.4",
+ "parking_lot_core 0.7.2",
+]
+
+[[package]]
+name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
- "lock_api",
- "parking_lot_core",
+ "lock_api 0.4.4",
+ "parking_lot_core 0.8.3",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
+dependencies = [
+ "cfg-if 0.1.10",
+ "cloudabi",
+ "libc",
+ "redox_syscall 0.1.57",
+ "smallvec",
+ "winapi",
]
[[package]]
@@ -1210,7 +1292,7 @@ dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
- "redox_syscall",
+ "redox_syscall 0.2.8",
"smallvec",
"winapi",
]
@@ -1228,6 +1310,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "pkg-config"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
+
+[[package]]
name = "polling"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1424,6 +1512,12 @@ dependencies = [
[[package]]
name = "redox_syscall"
+version = "0.1.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
+
+[[package]]
+name = "redox_syscall"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
@@ -1437,7 +1531,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
dependencies = [
- "redox_syscall",
+ "redox_syscall 0.2.8",
]
[[package]]
@@ -1447,7 +1541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
"getrandom",
- "redox_syscall",
+ "redox_syscall 0.2.8",
]
[[package]]
@@ -1622,7 +1716,19 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b"
dependencies = [
- "lock_api",
+ "lock_api 0.4.4",
+]
+
+[[package]]
+name = "ssh2"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d876d4d57f6bbf2245d43f7ec53759461f801a446d3693704aa6d27b257844d7"
+dependencies = [
+ "bitflags",
+ "libc",
+ "libssh2-sys",
+ "parking_lot 0.10.2",
]
[[package]]
@@ -1749,7 +1855,7 @@ dependencies = [
"cfg-if 1.0.0",
"libc",
"rand 0.8.3",
- "redox_syscall",
+ "redox_syscall 0.2.8",
"remove_dir_all",
"winapi",
]
@@ -1783,7 +1889,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
dependencies = [
"libc",
"numtoa",
- "redox_syscall",
+ "redox_syscall 0.2.8",
"redox_termios",
]
@@ -1930,6 +2036,12 @@ dependencies = [
]
[[package]]
+name = "vcpkg"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa"
+
+[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2333,6 +2445,8 @@ version = "0.14.0"
dependencies = [
"insta",
"names",
+ "rand 0.8.3",
+ "ssh2",
"zellij-client",
"zellij-server",
"zellij-utils",
diff --git a/Cargo.toml b/Cargo.toml
index 7e483940b..165254525 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,7 +19,9 @@ zellij-server = { path = "zellij-server/", version = "0.14.0" }
zellij-utils = { path = "zellij-utils/", version = "0.14.0" }
[dev-dependencies]
-insta = "1.6.0"
+insta = { version = "1.6.0", features = ["backtrace"] }
+ssh2 = "0.9.1"
+rand = "0.8.0"
zellij-utils = { path = "zellij-utils/", version = "0.14.0", features = ["test"] }
zellij-client = { path = "zellij-client/", version = "0.14.0", features = ["test"] }
zellij-server = { path = "zellij-server/", version = "0.14.0", features = ["test"] }
diff --git a/Makefile.toml b/Makefile.toml
index 16b7695ba..e0043ab71 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -84,6 +84,10 @@ end
env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = ["default-plugins/status-bar", "default-plugins/strider", "default-plugins/tab-bar"] }
run_task = { name = "build-release", fork = true }
+[tasks.build-plugins]
+env = { "CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS" = ["default-plugins/status-bar", "default-plugins/strider", "default-plugins/tab-bar"] }
+run_task = { name = "build", fork = true }
+
[tasks.wasm-opt-plugins]
script_runner = "@duckscript"
script = '''
@@ -120,6 +124,19 @@ dependencies = ["setup-cross-compilation", "build-plugins-release", "wasm-opt-pl
command = "cross"
args = ["build", "--verbose", "--release", "--target", "${CARGO_MAKE_TASK_ARGS}"]
+# Build e2e asset
+[tasks.build-e2e]
+workspace = false
+dependencies = ["build-plugins"]
+command = "cargo"
+args = ["build", "--verbose", "--target", "x86_64-unknown-linux-musl"]
+
+# Run e2e tests - we mark the e2e tests as "ignored" so they will not be run with the normal ones
+[tasks.e2e-test]
+workspace = false
+command = "cargo"
+args = ["test", "--", "--ignored", "--nocapture", "--test-threads", "1", "@@split(CARGO_MAKE_TASK_ARGS,;)"]
+
[tasks.setup-cross-compilation]
command = "cargo"
args = ["install", "cross"]
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 000000000..ec52486ee
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,21 @@
+---
+version: "2.1"
+services:
+ zellij-e2e:
+ image: ghcr.io/linuxserver/openssh-server
+ container_name: zellij-e2e
+ hostname: zellij-e2e
+ environment:
+ - PUID=1000
+ - PGID=1000
+ - TZ=Europe/Vienna
+ - PASSWORD_ACCESS=true
+ - USER_PASSWORD=test
+ - USER_NAME=test
+ volumes:
+ - type: bind
+ source: ./target
+ target: /usr/src/zellij
+ ports:
+ - 2222:2222
+ restart: unless-stopped
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
new file mode 100644
index 000000000..5af67074c
--- /dev/null
+++ b/src/tests/e2e/cases.rs
@@ -0,0 +1,716 @@
+use ::insta::assert_snapshot;
+use zellij_utils::pane_size::PositionAndSize;
+
+use rand::Rng;
+
+use super::remote_runner::{RemoteRunner, RemoteTerminal, Step};
+use crate::tests::utils::commands::{
+ CLOSE_PANE_IN_PANE_MODE, DETACH_IN_SESSION_MODE, ENTER, LOCK_MODE, NEW_TAB_IN_TAB_MODE,
+ PANE_MODE, QUIT, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, SCROLL_MODE,
+ SCROLL_UP_IN_SCROLL_MODE, SESSION_MODE, SPLIT_RIGHT_IN_PANE_MODE, TAB_MODE,
+ TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
+};
+
+// All the E2E tests are marked as "ignored" so that they can be run separately from the normal
+// tests
+
+#[test]
+#[ignore]
+pub fn starts_with_one_terminal() {
+ let fake_win_size = PositionAndSize {
+ cols: 120,
+ rows: 24,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let last_snapshot = RemoteRunner::new("starts_with_one_terminal", fake_win_size, None)
+ .add_step(Step {
+ name: "Wait for app to load",
+ instruction: |remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
+ {
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .run_all_steps();
+ assert_snapshot!(last_snapshot);
+}
+
+#[test]
+#[ignore]
+pub fn split_terminals_vertically() {
+ let fake_win_size = PositionAndSize {
+ cols: 120,
+ rows: 24,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+
+ let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size, None)
+ .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(2, 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 appear",
+ 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
+ },
+ })
+ .run_all_steps();
+ assert_snapshot!(last_snapshot);
+}
+
+#[test]
+#[ignore]
+pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() {
+ let fake_win_size = PositionAndSize {
+ cols: 8,
+ rows: 20,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let last_snapshot = RemoteRunner::new(
+ "cannot_split_terminals_vertically_when_active_terminal_is_too_small",
+ fake_win_size,
+ None,
+ )
+ .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(2, 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: "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",
+ instruction: |remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.cursor_position_is(5, 2) && remote_terminal.snapshot_contains("Hi!")
+ {
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .run_all_steps();
+ assert_snapshot!(last_snapshot);
+}
+
+#[test]
+#[ignore]
+pub fn scrolling_inside_a_pane() {
+ let fake_win_size = PositionAndSize {
+ cols: 120,
+ rows: 24,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let last_snapshot = RemoteRunner::new("scrolling_inside_a_pane", fake_win_size, None)
+ .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(2, 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: "Fill terminal with text",
+ instruction: |mut 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
+ remote_terminal.send_key(&format!("{:0<57}", "line1 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line2 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line3 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line4 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line5 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line6 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line7 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line8 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line9 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line10 ").as_bytes());