mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-29 14:21:43 +00:00
test(e2e): add proper e2e tests and pty-testing utils
This commit is contained in:
parent
14804f50a2
commit
b780fa1ba5
42
Cargo.lock
generated
42
Cargo.lock
generated
@ -122,6 +122,12 @@ version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
@ -1866,7 +1872,7 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025"
|
||||
dependencies = [
|
||||
"vte",
|
||||
"vte 0.14.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1949,6 +1955,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"unicode-width 0.2.0",
|
||||
"ureq",
|
||||
"vt100",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
@ -2297,6 +2304,29 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vt100"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"log",
|
||||
"unicode-width 0.1.14",
|
||||
"vte 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.14.1"
|
||||
@ -2306,6 +2336,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
|
@ -77,6 +77,7 @@ clipboard-win = "5.4.0"
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
tempfile = "3.16.0"
|
||||
portable-pty = "0.9.0"
|
||||
vt100 = "0.15"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
548
tests/e2e.rs
548
tests/e2e.rs
@ -1,5 +1,5 @@
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
use std::{io::Write, thread::sleep};
|
||||
|
||||
use portable_pty::{CommandBuilder, PtySize, native_pty_system};
|
||||
|
||||
@ -10,16 +10,88 @@ const DEFAULT_CABLE_DIR: &str = "./cable/unix";
|
||||
const DEFAULT_CABLE_DIR: &str = "./cable/windows";
|
||||
|
||||
const DEFAULT_PTY_SIZE: PtySize = PtySize {
|
||||
rows: 20,
|
||||
cols: 60,
|
||||
rows: 30,
|
||||
cols: 120,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
};
|
||||
|
||||
const DEFAULT_TIMEOUT: Duration = Duration::from_millis(400);
|
||||
const DEFAULT_DELAY: Duration = Duration::from_millis(200);
|
||||
|
||||
fn assert_exit_ok(
|
||||
child: &mut Box<dyn portable_pty::Child + Send + Sync + 'static>,
|
||||
struct PtyTester {
|
||||
pair: portable_pty::PtyPair,
|
||||
cwd: std::path::PathBuf,
|
||||
delay: Duration,
|
||||
pub reader: Box<dyn std::io::Read + Send>,
|
||||
pub writer: Box<dyn std::io::Write + Send + 'static>,
|
||||
void_buffer: Vec<u8>,
|
||||
parser: vt100::Parser,
|
||||
}
|
||||
|
||||
impl PtyTester {
|
||||
fn new() -> Self {
|
||||
let pty_system = native_pty_system();
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
let reader = pair.master.try_clone_reader().unwrap();
|
||||
let writer = pair.master.take_writer().unwrap();
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let delay = DEFAULT_DELAY;
|
||||
let size = pair.master.get_size().unwrap();
|
||||
let parser = vt100::Parser::new(size.rows, size.cols, 0);
|
||||
PtyTester {
|
||||
pair,
|
||||
cwd,
|
||||
delay,
|
||||
reader: Box::new(reader),
|
||||
writer: Box::new(writer),
|
||||
void_buffer: vec![0; 2usize.pow(20)], // 1 MiB buffer
|
||||
parser,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn with_delay(mut self, delay: Duration) -> Self {
|
||||
self.delay = delay;
|
||||
self
|
||||
}
|
||||
|
||||
/// Spawns a command in the pty, returning a boxed child process.
|
||||
pub fn spawn_command(
|
||||
&mut self,
|
||||
mut cmd: CommandBuilder,
|
||||
) -> Box<dyn portable_pty::Child + Send + Sync> {
|
||||
cmd.cwd(&self.cwd);
|
||||
let child = self.pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(self.delay);
|
||||
child
|
||||
}
|
||||
|
||||
fn read_raw_output(&mut self) -> String {
|
||||
self.void_buffer.fill(0);
|
||||
let bytes_read = self.reader.read(&mut self.void_buffer).unwrap();
|
||||
String::from_utf8_lossy(&self.void_buffer[..bytes_read]).to_string()
|
||||
}
|
||||
|
||||
/// Reads the output from the child process's pty.
|
||||
/// This method processes the output using a vt100 parser to handle terminal escape
|
||||
/// sequences.
|
||||
fn read_tui_output(&mut self) -> String {
|
||||
self.void_buffer.fill(0);
|
||||
let bytes_read = self.reader.read(&mut self.void_buffer).unwrap();
|
||||
self.parser.process(&self.void_buffer[..bytes_read]);
|
||||
self.parser.screen().contents()
|
||||
}
|
||||
|
||||
/// Writes input to the child process's stdin.
|
||||
pub fn write_input(&mut self, input: &str) {
|
||||
write!(self.writer, "{}", input).unwrap();
|
||||
self.writer.flush().unwrap();
|
||||
sleep(self.delay);
|
||||
}
|
||||
|
||||
/// Waits for the child process to exit, asserting that it exits with a success status.
|
||||
pub fn assert_exit_ok(
|
||||
child: &mut Box<dyn portable_pty::Child + Send + Sync>,
|
||||
timeout: Duration,
|
||||
) {
|
||||
let now = std::time::Instant::now();
|
||||
@ -45,232 +117,242 @@ fn assert_exit_ok(
|
||||
panic!("Process did not exit in time");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_version() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
const FRAME_STABILITY_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut cmd = CommandBuilder::new("./target/debug/tv");
|
||||
cmd.cwd(std::env::current_dir().unwrap());
|
||||
cmd.args(["--version"]);
|
||||
let mut child: Box<dyn portable_pty::Child + Send + Sync + 'static> =
|
||||
pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
// Read the output from the pty
|
||||
let mut buf = [0; 512];
|
||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||
let _ = reader.read(&mut buf).unwrap();
|
||||
let output = String::from_utf8_lossy(&buf);
|
||||
assert!(output.contains("television"));
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
pub fn get_tui_frame(&mut self) -> String {
|
||||
// wait for the UI to stabilize with a timeout
|
||||
let mut frame = String::new();
|
||||
let start_time = std::time::Instant::now();
|
||||
loop {
|
||||
let new_frame = self.read_tui_output();
|
||||
if new_frame == frame {
|
||||
break;
|
||||
}
|
||||
frame = new_frame;
|
||||
assert!(
|
||||
start_time.elapsed() < Self::FRAME_STABILITY_TIMEOUT,
|
||||
"UI did not stabilize within {:?}. Last frame:\n{}",
|
||||
Self::FRAME_STABILITY_TIMEOUT,
|
||||
frame
|
||||
);
|
||||
}
|
||||
frame
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_help() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut cmd = CommandBuilder::new("./target/debug/tv");
|
||||
cmd.cwd(std::env::current_dir().unwrap());
|
||||
cmd.args(["--help"]);
|
||||
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
// Read the output from the pty
|
||||
let mut buf = [0; 512];
|
||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||
let _ = reader.read(&mut buf).unwrap();
|
||||
let output = String::from_utf8_lossy(&buf);
|
||||
assert!(output.contains("A cross-platform"));
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
/// Asserts that the output contains the expected string.
|
||||
pub fn assert_tui_output_contains(&mut self, expected: &str) {
|
||||
let frame = self.get_tui_frame();
|
||||
assert!(
|
||||
frame.contains(expected),
|
||||
"Expected output to contain\n'{}'\nbut got:\n{}",
|
||||
expected,
|
||||
frame
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_list_channels() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
pub fn assert_raw_output_contains(&mut self, expected: &str) {
|
||||
let output = self.read_raw_output();
|
||||
assert!(
|
||||
output.contains(expected),
|
||||
"Expected output to contain '{}', but got:\n{}",
|
||||
expected,
|
||||
output
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut cmd = CommandBuilder::new("./target/debug/tv");
|
||||
cmd.cwd(std::env::current_dir().unwrap());
|
||||
cmd.args([
|
||||
fn ctrl(c: char) -> String {
|
||||
((c as u8 & 0x1F) as char).to_string()
|
||||
}
|
||||
|
||||
const ENTER: &str = "\r";
|
||||
|
||||
const TV_BIN_PATH: &str = "./target/debug/tv";
|
||||
const LOCAL_CONFIG_AND_CABLE: &[&str] = &[
|
||||
"--cable-dir",
|
||||
DEFAULT_CABLE_DIR,
|
||||
"--config-file",
|
||||
DEFAULT_CONFIG_FILE,
|
||||
"list-channels",
|
||||
]);
|
||||
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
];
|
||||
|
||||
// Read the output from the pty
|
||||
let mut buf = [0; 512];
|
||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||
let _ = reader.read(&mut buf).unwrap();
|
||||
let output = String::from_utf8_lossy(&buf);
|
||||
assert!(output.contains("files"), "Output: {}", output);
|
||||
assert!(output.contains("dirs"), "Output: {}", output);
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
fn tv() -> CommandBuilder {
|
||||
CommandBuilder::new(TV_BIN_PATH)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_init_zsh() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut cmd = CommandBuilder::new("./target/debug/tv");
|
||||
cmd.cwd(std::env::current_dir().unwrap());
|
||||
cmd.args([
|
||||
"--cable-dir",
|
||||
DEFAULT_CABLE_DIR,
|
||||
"--config-file",
|
||||
DEFAULT_CONFIG_FILE,
|
||||
"init",
|
||||
"zsh",
|
||||
]);
|
||||
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/// Creates a command to run `tv` with the repo's config and cable directory
|
||||
/// using the cable directory as cwd.
|
||||
fn tv_command(channel: &str) -> CommandBuilder {
|
||||
let mut cmd = CommandBuilder::new("./target/debug/tv");
|
||||
cmd.cwd(std::env::current_dir().unwrap());
|
||||
cmd.args([
|
||||
"--cable-dir",
|
||||
DEFAULT_CABLE_DIR,
|
||||
"--config-file",
|
||||
DEFAULT_CONFIG_FILE,
|
||||
channel,
|
||||
]);
|
||||
fn tv_with_args(args: &[&str]) -> CommandBuilder {
|
||||
let mut cmd = tv();
|
||||
cmd.args(args);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn tv_local_config_and_cable_with_args(args: &[&str]) -> CommandBuilder {
|
||||
let mut cmd = tv();
|
||||
cmd.args(LOCAL_CONFIG_AND_CABLE);
|
||||
cmd.args(args);
|
||||
cmd
|
||||
}
|
||||
|
||||
mod e2e {
|
||||
use super::*;
|
||||
mod tv_subcommands {
|
||||
use super::*;
|
||||
|
||||
/// Really just a sanity check
|
||||
#[test]
|
||||
fn tv_version() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child = tester.spawn_command(tv_with_args(&["--version"]));
|
||||
|
||||
tester.assert_raw_output_contains("television");
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
/// Really just a sanity check
|
||||
#[test]
|
||||
fn tv_help() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child = tester.spawn_command(tv_with_args(&["--help"]));
|
||||
|
||||
tester.assert_raw_output_contains("A cross-platform");
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
/// Tests the `tv list-channels` command.
|
||||
///
|
||||
/// We expect this to list all available channels in the cable directory.
|
||||
#[test]
|
||||
fn tv_list_channels() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child =
|
||||
tester.spawn_command(tv_local_config_and_cable_with_args(&[
|
||||
"list-channels",
|
||||
]));
|
||||
|
||||
// Check what's in the cable directory
|
||||
let cable_dir_filenames = std::fs::read_dir(DEFAULT_CABLE_DIR)
|
||||
.expect("Failed to read cable directory")
|
||||
.filter_map(Result::ok)
|
||||
.filter_map(|entry| {
|
||||
// this is pretty lazy and can be improved later on
|
||||
entry.path().extension().and_then(|ext| {
|
||||
if ext == "toml" {
|
||||
entry.path().file_stem().and_then(|stem| {
|
||||
stem.to_str().map(String::from)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Check if the output contains all channel names
|
||||
let output = tester.read_raw_output();
|
||||
for channel in cable_dir_filenames {
|
||||
assert!(
|
||||
output.contains(&channel),
|
||||
"Channel '{}' not found in output: {}",
|
||||
channel,
|
||||
output
|
||||
);
|
||||
}
|
||||
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// This simply tests that the command exits successfully.
|
||||
fn tv_init_zsh() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child =
|
||||
tester.spawn_command(tv_with_args(&["init", "zsh"]));
|
||||
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
mod general_ui {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn toggle_help() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child =
|
||||
tester.spawn_command(tv_local_config_and_cable_with_args(&[]));
|
||||
|
||||
tester.write_input(&ctrl('g'));
|
||||
|
||||
tester.assert_tui_output_contains("current mode:");
|
||||
|
||||
// Exit the application
|
||||
tester.write_input(&ctrl('c'));
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// FIXME: was lazy, this should be more robust
|
||||
fn toggle_preview() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child =
|
||||
tester.spawn_command(tv_local_config_and_cable_with_args(&[]));
|
||||
|
||||
let with_preview =
|
||||
"╭───────────────────────── files ──────────────────────────╮";
|
||||
tester.assert_tui_output_contains(with_preview);
|
||||
|
||||
// Toggle preview
|
||||
tester.write_input(&ctrl('o'));
|
||||
|
||||
let without_preview = "╭─────────────────────────────────────────────────────── files ────────────────────────────────────────────────────────╮";
|
||||
tester.assert_tui_output_contains(without_preview);
|
||||
|
||||
// Toggle preview
|
||||
tester.write_input(&ctrl('o'));
|
||||
|
||||
tester.assert_tui_output_contains(with_preview);
|
||||
|
||||
// Exit the application
|
||||
tester.write_input(&ctrl('c'));
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
mod channels {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tv_ctrl_c() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child =
|
||||
tester.spawn_command(tv_local_config_and_cable_with_args(&[
|
||||
"files",
|
||||
]));
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut child = pair.slave.spawn_command(tv_command("files")).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
// Send Ctrl-C to the process
|
||||
let mut writer = pair.master.take_writer().unwrap();
|
||||
writeln!(writer, "\x03").unwrap(); // Ctrl-C
|
||||
tester.write_input(&ctrl('c'));
|
||||
|
||||
// Check if the child process exited with a timeout
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_remote_control() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut child = pair.slave.spawn_command(tv_command("files")).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
// Send Ctrl-T to the process (open remote control mode)
|
||||
let mut writer = pair.master.take_writer().unwrap();
|
||||
writeln!(writer, "\x14").unwrap(); // Ctrl-T
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
let mut buf = [0; 5096]; // Buffer size for reading output
|
||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||
let _ = reader.read(&mut buf).unwrap();
|
||||
|
||||
let s = String::from_utf8_lossy(&buf);
|
||||
|
||||
assert!(s.contains("Remote Control"));
|
||||
|
||||
// Send Ctrl-c to exit remote control mode
|
||||
writeln!(writer, "\x03").unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
// resend Ctrl-c to finally exit
|
||||
writeln!(writer, "\x03").unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_custom_input_header_and_preview_size() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
|
||||
// Spawn a tv process in the pty
|
||||
let mut cmd = tv_command("files");
|
||||
cmd.args(["--input-header", "bagels"]);
|
||||
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
// Read the output from the pty
|
||||
let mut buf = [0; 5096];
|
||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||
let _ = reader.read(&mut buf).unwrap();
|
||||
let output = String::from_utf8_lossy(&buf);
|
||||
|
||||
assert!(output.contains("bagels"));
|
||||
|
||||
// Send Ctrl-C to the process
|
||||
let mut writer = pair.master.take_writer().unwrap();
|
||||
writeln!(writer, "\x03").unwrap(); // Ctrl-C
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
/// Test that the various channels open correctly, spawn a UI that contains the
|
||||
/// expected channel name, and exit cleanly when Ctrl-C is pressed.
|
||||
macro_rules! test_channel {
|
||||
($($name:ident: $channel_name:expr,)*) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
let pty_system = native_pty_system();
|
||||
// Create a new pty
|
||||
let pair = pty_system.openpty(DEFAULT_PTY_SIZE).unwrap();
|
||||
let cmd = tv_command(&$channel_name);
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child = tester.spawn_command(tv_local_config_and_cable_with_args(&[
|
||||
$channel_name,
|
||||
]));
|
||||
|
||||
let mut child = pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(300));
|
||||
tester.assert_tui_output_contains(&format!(
|
||||
"── {} ──",
|
||||
$channel_name
|
||||
));
|
||||
|
||||
// Read the output from the pty
|
||||
let mut buf = [0; 5096];
|
||||
let mut reader = pair.master.try_clone_reader().unwrap();
|
||||
let _ = reader.read(&mut buf).unwrap();
|
||||
let output = String::from_utf8_lossy(&buf);
|
||||
|
||||
assert!(
|
||||
output.contains(&$channel_name),
|
||||
"Unable to find channel name (\"{}\") in output: {}",
|
||||
&$channel_name,
|
||||
output
|
||||
);
|
||||
|
||||
// Send Ctrl-C to the process
|
||||
let mut writer = pair.master.take_writer().unwrap();
|
||||
writeln!(writer, "\x03").unwrap(); // Ctrl-C
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
assert_exit_ok(&mut child, DEFAULT_TIMEOUT);
|
||||
tester.write_input(&ctrl('c'));
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
)*
|
||||
}
|
||||
@ -286,3 +368,65 @@ test_channel! {
|
||||
test_channel_text: "text",
|
||||
test_channel_diff: "git-diff",
|
||||
}
|
||||
}
|
||||
|
||||
mod remote_control {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tv_remote_control_shows() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child = tester
|
||||
.spawn_command(tv_local_config_and_cable_with_args(&["dirs"]));
|
||||
|
||||
// open remote control mode
|
||||
tester.write_input(&ctrl('t'));
|
||||
|
||||
tester.assert_tui_output_contains("──Remote Control──");
|
||||
|
||||
// exit remote then app
|
||||
tester.write_input(&ctrl('c'));
|
||||
tester.write_input(&ctrl('c'));
|
||||
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_remote_control_zaps() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut child = tester
|
||||
.spawn_command(tv_local_config_and_cable_with_args(&["dirs"]));
|
||||
|
||||
// open remote control mode
|
||||
tester.write_input(&ctrl('t'));
|
||||
tester.write_input("files");
|
||||
tester.write_input(ENTER);
|
||||
|
||||
tester.assert_tui_output_contains("── files ──");
|
||||
|
||||
// exit remote then app
|
||||
tester.write_input(&ctrl('c'));
|
||||
tester.write_input(&ctrl('c'));
|
||||
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
mod cli {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tv_custom_input_header_and_preview_size() {
|
||||
let mut tester = PtyTester::new();
|
||||
let mut cmd = tv_local_config_and_cable_with_args(&["files"]);
|
||||
cmd.args(["--input-header", "toasted bagels"]);
|
||||
let mut child = tester.spawn_command(cmd);
|
||||
|
||||
tester.assert_tui_output_contains("── toasted bagels ──");
|
||||
|
||||
tester.write_input(&ctrl('c'));
|
||||
|
||||
PtyTester::assert_exit_ok(&mut child, DEFAULT_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user