summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2023-05-15 10:43:58 +0200
committerAram Drevekenin <aram@poor.dev>2023-05-15 10:43:58 +0200
commitc8dd7379e35cfab03b8e33245ae9daebcaaf24b6 (patch)
tree2854c4d497e26ec18cc769003ee561be5fcf23e8
parentb59dca24d15326ace4fac7d68101b723cbd902e0 (diff)
tests and more refactoring
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml1
-rw-r--r--default-plugins/fixture-plugin-for-tests/.cargo/config.toml2
-rw-r--r--default-plugins/fixture-plugin-for-tests/Cargo.toml17
l---------default-plugins/fixture-plugin-for-tests/LICENSE.md1
-rw-r--r--default-plugins/fixture-plugin-for-tests/src/main.rs67
-rw-r--r--default-plugins/strider/src/main.rs6
-rw-r--r--default-plugins/strider/src/state.rs1
-rw-r--r--xtask/src/ci.rs20
-rw-r--r--xtask/src/main.rs1
-rw-r--r--zellij-server/Cargo.toml2
-rw-r--r--zellij-server/src/plugins/mod.rs4
-rw-r--r--zellij-server/src/plugins/unit/plugin_tests.rs226
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__load_new_plugin_from_hd.snap12
-rw-r--r--zellij-server/src/plugins/unit/snapshots/zellij_server__plugins__plugin_tests__plugin_workers.snap12
-rw-r--r--zellij-tile/src/lib.rs1
-rwxr-xr-xzellij-utils/assets/plugins/fixture-plugin-for-tests.wasmbin0 -> 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
new file mode 100755
index 000000000..8e9b4dfaa
--- /dev/null
+++ b/zellij-utils/assets/plugins/fixture-plugin-for-tests.wasm
Binary files differ