mirror of
https://github.com/alexpasmantier/television.git
synced 2025-07-28 13:51:41 +00:00
test(e2e): more end to end tests
This commit is contained in:
parent
8d822cd2fc
commit
3b57710006
113
Cargo.lock
generated
113
Cargo.lock
generated
@ -161,9 +161,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@ -231,6 +231,12 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.41"
|
||||
@ -347,12 +353,6 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "comma"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.8.1"
|
||||
@ -585,6 +585,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@ -1031,15 +1037,6 @@ version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@ -1069,16 +1066,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags 1.2.1",
|
||||
"bitflags 2.9.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"memoffset",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1295,6 +1290,27 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-pty"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 1.3.2",
|
||||
"downcast-rs",
|
||||
"filedescriptor",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"nix",
|
||||
"serial2",
|
||||
"shared_library",
|
||||
"shell-words",
|
||||
"winapi",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
@ -1431,19 +1447,6 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rexpect"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ff60778f96fb5a48adbe421d21bf6578ed58c0872d712e7e08593c195adff8"
|
||||
dependencies = [
|
||||
"comma",
|
||||
"nix",
|
||||
"regex",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
@ -1614,6 +1617,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial2"
|
||||
version = "0.2.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7d1d08630509d69f90eff4afcd02c3bd974d979225cbd815ff5942351b14375"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
@ -1634,6 +1648,22 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shared_library"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shell-words"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@ -1789,8 +1819,8 @@ dependencies = [
|
||||
"lazy-regex",
|
||||
"nucleo",
|
||||
"parking_lot",
|
||||
"portable-pty",
|
||||
"ratatui",
|
||||
"rexpect",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -2421,6 +2451,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
|
@ -75,7 +75,7 @@ clipboard-win = "5.4.0"
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
tempfile = "3.16.0"
|
||||
rexpect = "0.5"
|
||||
portable-pty = "0.9.0"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
BIN
output.txt
Normal file
BIN
output.txt
Normal file
Binary file not shown.
341
tests/e2e.rs
341
tests/e2e.rs
@ -1,97 +1,260 @@
|
||||
use std::path::Path;
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
use rexpect::error::Error;
|
||||
use rexpect::process::wait::WaitStatus;
|
||||
use rexpect::spawn;
|
||||
use television::channels::prototypes::ChannelPrototype;
|
||||
use tempfile::TempDir;
|
||||
use portable_pty::{CommandBuilder, PtySize, native_pty_system};
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn setup_config(content: &str) -> TempDir {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let config_path = temp_dir.path().join("config.toml");
|
||||
std::fs::write(&config_path, content).unwrap();
|
||||
temp_dir
|
||||
const DEFAULT_CONFIG_FILE: &str = "./.config/config.toml";
|
||||
#[cfg(unix)]
|
||||
const DEFAULT_CABLE_DIR: &str = "./cable/unix";
|
||||
#[cfg(windows)]
|
||||
const DEFAULT_CABLE_DIR: &str = "./cable/windows";
|
||||
|
||||
const DEFAULT_PTY_SIZE: PtySize = PtySize {
|
||||
rows: 20,
|
||||
cols: 60,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
};
|
||||
|
||||
const DEFAULT_TIMEOUT: Duration = Duration::from_millis(400);
|
||||
|
||||
fn assert_exit_ok(
|
||||
child: &mut Box<dyn portable_pty::Child + Send + Sync + 'static>,
|
||||
timeout: Duration,
|
||||
) {
|
||||
let now = std::time::Instant::now();
|
||||
while now.elapsed() < timeout {
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
assert!(
|
||||
status.success(),
|
||||
"Process exited with non-zero status: {:?}",
|
||||
status
|
||||
);
|
||||
return;
|
||||
}
|
||||
Ok(None) => {
|
||||
// Process is still running, continue waiting
|
||||
sleep(Duration::from_millis(50));
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Error waiting for process: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("Process did not exit in time");
|
||||
}
|
||||
|
||||
fn setup_cable_channels<D>(channels: Vec<&str>, cable_dir: D)
|
||||
where
|
||||
D: AsRef<Path>,
|
||||
{
|
||||
let cable_dir = cable_dir.as_ref();
|
||||
std::fs::create_dir_all(cable_dir).unwrap();
|
||||
for channel in channels {
|
||||
let name = toml::from_str::<ChannelPrototype>(channel)
|
||||
.unwrap()
|
||||
.metadata
|
||||
.name;
|
||||
let channel_path = cable_dir.join(format!("{}.toml", name));
|
||||
std::fs::write(&channel_path, channel).unwrap();
|
||||
#[test]
|
||||
fn tv_version() {
|
||||
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(["--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);
|
||||
}
|
||||
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_list_channels() {
|
||||
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,
|
||||
"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);
|
||||
}
|
||||
|
||||
#[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,
|
||||
]);
|
||||
cmd
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_ctrl_c() {
|
||||
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-C to the process
|
||||
let mut writer = pair.master.take_writer().unwrap();
|
||||
writeln!(writer, "\x03").unwrap(); // 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);
|
||||
}
|
||||
|
||||
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 child = pair.slave.spawn_command(cmd).unwrap();
|
||||
sleep(Duration::from_millis(300));
|
||||
|
||||
// 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);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_version() -> Result<(), Error> {
|
||||
let mut p = spawn("./target/debug/tv --version", Some(500))?;
|
||||
p.exp_regex("television [0-9]+\\.[0-9]+\\.[0-9]+")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_help() -> Result<(), Error> {
|
||||
let mut p = spawn("./target/debug/tv --help", Some(500))?;
|
||||
p.exp_regex("A cross-platform")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const BASIC_FILE_CHANNEL: &str = r#"
|
||||
[metadata]
|
||||
name = "files"
|
||||
|
||||
[source]
|
||||
command = "fd -t f"
|
||||
"#;
|
||||
|
||||
const BASIC_DIR_CHANNEL: &str = r#"
|
||||
[metadata]
|
||||
name = "dirs"
|
||||
|
||||
[source]
|
||||
command = "fd -t d"
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn tv_list_channels() -> Result<(), Error> {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
setup_cable_channels(
|
||||
vec![BASIC_FILE_CHANNEL, BASIC_DIR_CHANNEL],
|
||||
&temp_dir,
|
||||
);
|
||||
|
||||
let mut p = spawn(
|
||||
&format!(
|
||||
"./target/debug/tv --cable-dir {} list-channels ",
|
||||
temp_dir.path().display()
|
||||
),
|
||||
Some(500),
|
||||
)?;
|
||||
p.exp_regex("files")?;
|
||||
p.exp_regex("dirs")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tv_init_zsh() -> Result<(), Error> {
|
||||
let p = spawn("./target/debug/tv init zsh", Some(500))?;
|
||||
// check that the process exits successfully
|
||||
if let Ok(w) = p.process.wait() {
|
||||
assert_eq!(w, WaitStatus::Exited(w.pid().unwrap(), 0));
|
||||
} else {
|
||||
panic!("Failed to wait for process");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
test_channel! {
|
||||
test_channel_files: "files",
|
||||
test_channel_dirs: "dirs",
|
||||
test_channel_env: "env",
|
||||
test_channel_git_log: "git-log",
|
||||
test_channel_git_reflog: "git-reflog",
|
||||
test_channel_git_branch: "git-branch",
|
||||
test_channel_text: "text",
|
||||
test_channel_diff: "git-diff",
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user