diff options
author | Aram Drevekenin <aram@poor.dev> | 2023-05-15 10:43:58 +0200 |
---|---|---|
committer | Aram Drevekenin <aram@poor.dev> | 2023-05-15 10:43:58 +0200 |
commit | c8dd7379e35cfab03b8e33245ae9daebcaaf24b6 (patch) | |
tree | 2854c4d497e26ec18cc769003ee561be5fcf23e8 | |
parent | b59dca24d15326ace4fac7d68101b723cbd902e0 (diff) |
tests and more refactoring
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | default-plugins/fixture-plugin-for-tests/.cargo/config.toml | 2 | ||||
-rw-r--r-- | default-plugins/fixture-plugin-for-tests/Cargo.toml | 17 | ||||
l--------- | default-plugins/fixture-plugin-for-tests/LICENSE.md | 1 | ||||
-rw-r--r-- | default-plugins/fixture-plugin-for-tests/src/main.rs | 67 | ||||
-rw-r--r-- | default-plugins/strider/src/main.rs | 6 | ||||
-rw-r--r-- | default-plugins/strider/src/state.rs | 1 | ||||
-rw-r--r-- | xtask/src/ci.rs | 20 | ||||
-rw-r--r-- | xtask/src/main.rs | 1 | ||||
-rw-r--r-- | zellij-server/Cargo.toml | 2 | ||||
-rw-r--r-- | zellij-server/src/plugins/mod.rs | 4 | ||||
-rw-r--r-- | zellij-server/src/plugins/unit/plugin_tests.rs | 226 | ||||
-rw-r--r-- | zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__load_new_plugin_from_hd.snap | 12 | ||||
-rw-r--r-- | zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__plugin_workers.snap | 12 | ||||
-rw-r--r-- | zellij-tile/src/lib.rs | 1 | ||||
-rwxr-xr-x | zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm | bin | 0 -> 729883 bytes |
17 files changed, 373 insertions, 10 deletions
diff --git a/Cargo.lock b/Cargo.lock index 7f2555423..de9f100f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -990,6 +990,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] +name = "fixture-plugin-for-tests" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "zellij-tile", +] + +[[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4063,6 +4072,7 @@ dependencies = [ "sixel-image", "sixel-tokenizer", "sysinfo", + "tempfile", "typetag", "unicode-width", "url", diff --git a/Cargo.toml b/Cargo.toml index 484bacf2c..c9233ca19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "default-plugins/status-bar", "default-plugins/strider", "default-plugins/tab-bar", + "default-plugins/fixture-plugin-for-tests", "zellij-client", "zellij-server", "zellij-utils", diff --git a/default-plugins/fixture-plugin-for-tests/.cargo/config.toml b/default-plugins/fixture-plugin-for-tests/.cargo/config.toml new file mode 100644 index 000000000..bc255e30b --- /dev/null +++ b/default-plugins/fixture-plugin-for-tests/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasi"
\ No newline at end of file diff --git a/default-plugins/fixture-plugin-for-tests/Cargo.toml b/default-plugins/fixture-plugin-for-tests/Cargo.toml new file mode 100644 index 000000000..6e7b9308a --- /dev/null +++ b/default-plugins/fixture-plugin-for-tests/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "fixture-plugin-for-tests" +version = "0.1.0" +authors = ["Aram Drevekenin <aram@poor.dev>"] +edition = "2021" +license = "MIT" + +[dependencies] +# colored = "2" +# ansi_term = "0.12" +# lazy_static = "1.4.0" +# rand = "0.8.4" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +# thiserror = "1.0.30" +zellij-tile = { path = "../../zellij-tile" } +# zellij-tile-utils = { path = "../../zellij-tile-utils" } diff --git a/default-plugins/fixture-plugin-for-tests/LICENSE.md b/default-plugins/fixture-plugin-for-tests/LICENSE.md new file mode 120000 index 000000000..f0608a63a --- /dev/null +++ b/default-plugins/fixture-plugin-for-tests/LICENSE.md @@ -0,0 +1 @@ +../../LICENSE.md
\ No newline at end of file diff --git a/default-plugins/fixture-plugin-for-tests/src/main.rs b/default-plugins/fixture-plugin-for-tests/src/main.rs new file mode 100644 index 000000000..4ef19adfc --- /dev/null +++ b/default-plugins/fixture-plugin-for-tests/src/main.rs @@ -0,0 +1,67 @@ +use zellij_tile::prelude::*; +use serde::{Serialize, Deserialize}; + +// This is a fixture plugin used only for tests in Zellij +// it is not (and should not!) be included in the mainline executable +// it's included here for convenience so that it will be built by the CI + +#[derive(Default)] +struct State { + received_events: Vec<Event>, + received_payload: Option<String>, +} + +#[derive(Default, Serialize, Deserialize)] +struct TestWorker {} + +impl <'de> ZellijWorker<'de> for TestWorker { + fn on_message(&mut self, message: String, payload: String) { + if message == "ping" { + post_message_to_plugin("pong".into(), payload); + } + } +} + +register_plugin!(State); +register_worker!(TestWorker, test_worker); + +impl ZellijPlugin for State { + fn load(&mut self) { + subscribe(&[ + EventType::InputReceived, + EventType::SystemClipboardFailure, + EventType::CustomMessage, + ]); + } + + fn update(&mut self, event: Event) -> bool { + match &event { + Event::CustomMessage(message, payload)=> { + if message == "pong" { + self.received_payload = Some(payload.clone()); + } + + } + Event::SystemClipboardFailure => { + // this is just to trigger the worker message + post_message_to( + "test", + "ping".to_owned(), + "gimme_back_my_payload".to_owned() + ); + }, + _ => {} + } + let should_render = true; + self.received_events.push(event); + should_render + } + + fn render(&mut self, rows: usize, cols: usize) { + if let Some(payload) = self.received_payload.as_ref() { + println!("Payload from worker: {:?}", payload); + } else { + println!("Rows: {:?}, Cols: {:?}, Received events: {:?}", rows, cols, self.received_events); + } + } +} diff --git a/default-plugins/strider/src/main.rs b/default-plugins/strider/src/main.rs index cb8e07f46..1e4ea555e 100644 --- a/default-plugins/strider/src/main.rs +++ b/default-plugins/strider/src/main.rs @@ -1,8 +1,3 @@ -// TODO: -// 1. worker to different file - DONE -// 2. separate search rendering to different rendering function - DONE -// 3. make hd scanning happen on startup and show loading indication - DONE -// 4. make selection and opening files work - TODO: CONTINUE HERE (04/05) mod state; mod search; @@ -40,7 +35,6 @@ impl ZellijPlugin for State { self.ev_history.push_back((event.clone(), Instant::now())); match event { Event::Timer(_elapsed) => { - // eprintln!("got timer event"); should_render = true; if self.loading { set_timeout(0.5); diff --git a/default-plugins/strider/src/state.rs b/default-plugins/strider/src/state.rs index 3ebe9dd03..617e7488d 100644 --- a/default-plugins/strider/src/state.rs +++ b/default-plugins/strider/src/state.rs @@ -9,7 +9,6 @@ use std::{ use zellij_tile::prelude::*; pub const ROOT: &str = "/host"; -// pub const ROOT: &str = "/tmp"; // TODO: NO!! pub const CURRENT_SEARCH_TERM: &str = "/data/current_search_term"; #[derive(Default)] diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs index bff00792b..54456735f 100644 --- a/xtask/src/ci.rs +++ b/xtask/src/ci.rs @@ -4,7 +4,7 @@ use crate::{ flags::{self, CiCmd, Cross, E2e}, }; use anyhow::Context; -use std::{ffi::OsString, path::PathBuf}; +use std::{ffi::OsString, path::{Path, PathBuf}}; use xshell::{cmd, Shell}; pub fn main(sh: &Shell, flags: flags::Ci) -> anyhow::Result<()> { @@ -117,10 +117,24 @@ fn e2e_test(sh: &Shell, args: Vec<OsString>) -> anyhow::Result<()> { crate::cargo() .and_then(|cargo| { + // e2e tests cmd!(sh, "{cargo} test -- --ignored --nocapture --test-threads 1") - .args(args) + .args(args.clone()) .run() - .map_err(anyhow::Error::new) + .map_err(anyhow::Error::new)?; + + // plugin system tests are run here because they're medium-slow + let _pd = sh.push_dir(Path::new("zellij-server")); + println!(""); + let msg = format!(">> Testing Plugin System"); + crate::status(&msg); + println!("{}", msg); + + cmd!(sh, "{cargo} test -- --ignored --nocapture --test-threads 1") + .args(args.clone()) + .run() + .with_context(|| format!("Failed to run tests for the Plugin System"))?; + Ok(()) }) .context(err_context) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 209caa5e7..9f439e8da 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -29,6 +29,7 @@ lazy_static::lazy_static! { "default-plugins/status-bar", "default-plugins/strider", "default-plugins/tab-bar", + "default-plugins/fixture-plugin-for-tests", "zellij-utils", "zellij-tile-utils", "zellij-tile", diff --git a/zellij-server/Cargo.toml b/zellij-server/Cargo.toml index 0fa9fe1e3..725b36fce 100644 --- a/zellij-server/Cargo.toml +++ b/zellij-server/Cargo.toml @@ -35,6 +35,8 @@ semver = "0.11.0" [dev-dependencies] insta = "1.6.0" +tempfile = "3.2.0" +wasmer = { version = "2.3.0", features = [ "singlepass" ] } [features] singlepass = ["wasmer/singlepass"] diff --git a/zellij-server/src/plugins/mod.rs b/zellij-server/src/plugins/mod.rs index 67a8c96c2..832c49088 100644 --- a/zellij-server/src/plugins/mod.rs +++ b/zellij-server/src/plugins/mod.rs @@ -253,3 +253,7 @@ pub(crate) fn plugin_thread_main( }) .context("failed to cleanup plugin data directory") } + +#[path = "./unit/plugin_tests.rs"] +#[cfg(test)] +mod plugin_tests; diff --git a/zellij-server/src/plugins/unit/plugin_tests.rs b/zellij-server/src/plugins/unit/plugin_tests.rs new file mode 100644 index 000000000..759d2ddf5 --- /dev/null +++ b/zellij-server/src/plugins/unit/plugin_tests.rs @@ -0,0 +1,226 @@ +use wasmer::Store; +use zellij_utils::lazy_static::lazy_static; +use tempfile::tempdir; +use super::{plugin_thread_main}; +use crate::screen::ScreenInstruction; +use crate::{ + channels::SenderWithContext, + thread_bus::Bus, + ServerInstruction, +}; +use insta::assert_snapshot; +use std::path::PathBuf; +use zellij_utils::data::Event; +use zellij_utils::errors::ErrorContext; +use zellij_utils::input::plugins::PluginsConfig; +use zellij_utils::input::layout::{Layout, RunPluginLocation, RunPlugin}; +use zellij_utils::pane_size::Size; + +use crate::background_jobs::BackgroundJob; +use crate::pty_writer::PtyWriteInstruction; +use std::env::set_var; +use std::sync::{Arc, Mutex}; + +use crate::{plugins::PluginInstruction, pty::PtyInstruction}; + +use zellij_utils::{ + channels::{self, ChannelWithContext, Receiver}, +}; + +macro_rules! log_actions_in_thread { + ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr ) => { + std::thread::Builder::new() + .name("logger thread".to_string()) + .spawn({ + let log = $arc_mutex_log.clone(); + let mut exit_event_count = 0; + move || loop { + let (event, _err_ctx) = $receiver + .recv() + .expect("failed to receive event on channel"); + match event { + $exit_event(..) => { + exit_event_count += 1; + log.lock().unwrap().push(event); + if exit_event_count == $exit_after_count { + break; + } + }, + _ => { + log.lock().unwrap().push(event); + }, + } + } + }) + .unwrap() + }; +} + +fn create_plugin_thread() -> (SenderWithContext<PluginInstruction>, Receiver<(ScreenInstruction, ErrorContext)>, Box<dyn FnMut()>) { + let (to_server, _server_receiver): ChannelWithContext<ServerInstruction> = + channels::bounded(50); + let to_server = SenderWithContext::new(to_server); + + let (to_screen, screen_receiver): ChannelWithContext<ScreenInstruction> = + channels::unbounded(); + let to_screen = SenderWithContext::new(to_screen); + + let (to_plugin, plugin_receiver): ChannelWithContext<PluginInstruction> = + channels::unbounded(); + let to_plugin = SenderWithContext::new(to_plugin); + let (to_pty, _pty_receiver): ChannelWithContext<PtyInstruction> = channels::unbounded(); + let to_pty = SenderWithContext::new(to_pty); + + let (to_pty_writer, _pty_writer_receiver): ChannelWithContext<PtyWriteInstruction> = + channels::unbounded(); + let to_pty_writer = SenderWithContext::new(to_pty_writer); + + let (to_background_jobs, _background_jobs_receiver): ChannelWithContext<BackgroundJob> = + channels::unbounded(); + let to_background_jobs = SenderWithContext::new(to_background_jobs); + + let plugin_bus = Bus::new( + vec![plugin_receiver], + Some(&to_screen), + Some(&to_pty), + Some(&to_plugin), + Some(&to_server), + Some(&to_pty_writer), + Some(&to_background_jobs), + None, + ) + .should_silently_fail(); + let store = Store::new(&wasmer::Universal::new(wasmer::Singlepass::default()).engine()); + let data_dir = PathBuf::from(tempdir().unwrap().path()); + let _plugin_thread = std::thread::Builder::new() + .name("plugin_thread".to_string()) + .spawn(move || { + set_var("ZELLIJ_SESSION_NAME", "zellij-test"); + plugin_thread_main( + plugin_bus, + store, + data_dir, + PluginsConfig::default(), + Box::new(Layout::default()), + ) + .expect("TEST") + }) + .unwrap(); + let teardown = { + let to_plugin = to_plugin.clone(); + move || { + let _ = to_pty.send(PtyInstruction::Exit); + let _ = to_pty_writer.send(PtyWriteInstruction::Exit); + let _ = to_screen.send(ScreenInstruction::Exit); + let _ = to_server.send(ServerInstruction::KillSession); + let _ = to_plugin.send(PluginInstruction::Exit); + } + }; + (to_plugin, screen_receiver, Box::new(teardown)) +} + +lazy_static! { + static ref PLUGIN_FIXTURE: String = format!("{}/../target/wasm32-wasi/debug/fixture-plugin-for-tests.wasm", std::env::var_os("CARGO_MANIFEST_DIR").unwrap().to_string_lossy()); +} + +#[test] +#[ignore] +pub fn load_new_plugin_from_hd() { + // here we load our fixture plugin into the plugin thread, and then send it an update message + // expecting tha thte plugin will log the received event and render it later after the update + // message (this is what the fixture plugin does) + // we then listen on our mock screen receiver to make sure we got a PluginBytes instruction + // that contains said render, and assert against it + let (plugin_thread_sender, screen_receiver, mut teardown) = create_plugin_thread(); + let plugin_should_float = Some(false); + let plugin_title = Some("test_plugin".to_owned()); + let run_plugin = RunPlugin { + _allow_exec_host_cmd: false, + location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), + }; + let tab_index = 1; + let client_id = 1; + let size = Size { + cols: 121, + rows: 20, + }; + let received_screen_instructions = Arc::new(Mutex::new(vec![])); + let screen_thread = log_actions_in_thread!( + received_screen_instructions, + ScreenInstruction::PluginBytes, + screen_receiver, + 2 + ); + + let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); + let _ = plugin_thread_sender.send(PluginInstruction::Load(plugin_should_float, plugin_title, run_plugin, tab_index, client_id, size)); + let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(None, Some(client_id), Event::InputReceived)])); // will be cached and sent to the plugin once it's loaded + screen_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let plugin_bytes_event = received_screen_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let ScreenInstruction::PluginBytes(plugin_bytes) = i { + for (plugin_id, client_id, plugin_bytes) in plugin_bytes { + let plugin_bytes = String::from_utf8_lossy(plugin_bytes).to_string(); + if plugin_bytes.contains("InputReceived") { + return Some((*plugin_id, *client_id, plugin_bytes)); + } + } + } + None + }); + assert_snapshot!(format!("{:#?}", plugin_bytes_event)); +} + +#[test] +#[ignore] +pub fn plugin_workers() { + let (plugin_thread_sender, screen_receiver, mut teardown) = create_plugin_thread(); + let plugin_should_float = Some(false); + let plugin_title = Some("test_plugin".to_owned()); + let run_plugin = RunPlugin { + _allow_exec_host_cmd: false, + location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), + }; + let tab_index = 1; + let client_id = 1; + let size = Size { + cols: 121, + rows: 20, + }; + let received_screen_instructions = Arc::new(Mutex::new(vec![])); + let screen_thread = log_actions_in_thread!( + received_screen_instructions, + ScreenInstruction::PluginBytes, + screen_receiver, + 3 + ); + + let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); + let _ = plugin_thread_sender.send(PluginInstruction::Load(plugin_should_float, plugin_title, run_plugin, tab_index, client_id, size)); + // we send a SystemClipboardFailure to trigger the custom handler in the fixture plugin that + // will send a message to the worker and in turn back to the plugin to be rendered, so we know + // that this cycle is working + let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![(None, Some(client_id), Event::SystemClipboardFailure)])); // will be cached and sent to the plugin once it's loaded + screen_thread.join().unwrap(); // this might take a while if the cache is cold + teardown(); + let plugin_bytes_event = received_screen_instructions + .lock() + .unwrap() + .iter() + .find_map(|i| { + if let ScreenInstruction::PluginBytes(plugin_bytes) = i { + for (plugin_id, client_id, plugin_bytes) in plugin_bytes { + let plugin_bytes = String::from_utf8_lossy(plugin_bytes).to_string(); + if plugin_bytes.contains("Payload from worker") { + return Some((*plugin_id, *client_id, plugin_bytes)); + } + } + } + None + }); + assert_snapshot!(format!("{:#?}", plugin_bytes_event)); +} diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__load_new_plugin_from_hd.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__load_new_plugin_from_hd.snap new file mode 100644 index 000000000..18c7e537f --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__load_new_plugin_from_hd.snap @@ -0,0 +1,12 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +assertion_line: 744 +expression: "format!(\"{:#?}\", plugin_bytes_event)" +--- +Some( + ( + 0, + 1, + "Rows: 20, Cols: 121, Received events: [InputReceived]\n\r", + ), +) diff --git a/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__plugin_workers.snap b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__plugin_workers.snap new file mode 100644 index 000000000..4aeccc31d --- /dev/null +++ b/zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__plugin_workers.snap @@ -0,0 +1,12 @@ +--- +source: zellij-server/src/plugins/./unit/plugin_tests.rs +assertion_line: 782 +expression: "format!(\"{:#?}\", plugin_bytes_event)" +--- +Some( + ( + 0, + 1, + "Payload from worker: \"gimme_back_my_payload\"\n\r", + ), +) diff --git a/zellij-tile/src/lib.rs b/zellij-tile/src/lib.rs index 3c8807538..3db09ff36 100644 --- a/zellij-tile/src/lib.rs +++ b/zellij-tile/src/lib.rs @@ -78,6 +78,7 @@ macro_rules! register_worker { ($worker:ty, $worker_name:ident) => { #[no_mangle] pub fn $worker_name() { + use serde_json::*; let worker_display_name = std::stringify!($worker_name); // read message from STDIN diff --git a/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm Binary files differnew file mode 100755 index 000000000..8e9b4dfaa --- /dev/null +++ b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm |