use super::plugin_thread_main; use crate::screen::ScreenInstruction; use crate::{channels::SenderWithContext, thread_bus::Bus, ServerInstruction}; use insta::assert_snapshot; use lazy_static::lazy_static; use std::collections::BTreeMap; use std::path::PathBuf; use tempfile::tempdir; use wasmi::Engine; use zellij_utils::data::{ BareKey, Event, InputMode, KeyWithModifier, ModeInfo, PermissionStatus, PermissionType, }; use zellij_utils::errors::ErrorContext; use zellij_utils::input::actions::Action; use zellij_utils::input::keybinds::Keybinds; use zellij_utils::input::layout::{ Layout, PluginAlias, PluginUserConfiguration, RunPlugin, RunPluginLocation, RunPluginOrAlias, }; use zellij_utils::input::permission::PermissionCache; use zellij_utils::input::plugins::PluginAliases; 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() }; } macro_rules! log_actions_in_thread_struct { ( $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() }; } macro_rules! grant_permissions_and_log_actions_in_thread { ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => { std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let log = $arc_mutex_log.clone(); let mut exit_event_count = 0; let cache_path = $cache_path.clone(); let plugin_thread_sender = $plugin_thread_sender.clone(); 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; } }, ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => { if plugin_permission.permissions.contains($permission_type) { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Granted, Some(cache_path.clone()), ), ); } else { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Denied, Some(cache_path.clone()), ), ); } }, _ => { log.lock().unwrap().push(event); }, } } }) .unwrap() }; } macro_rules! deny_permissions_and_log_actions_in_thread { ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => { std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let log = $arc_mutex_log.clone(); let mut exit_event_count = 0; let cache_path = $cache_path.clone(); let plugin_thread_sender = $plugin_thread_sender.clone(); 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; } }, ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Denied, Some(cache_path.clone()), ), ); break; }, _ => { log.lock().unwrap().push(event); }, } } }) .unwrap() }; } macro_rules! grant_permissions_and_log_actions_in_thread_naked_variant { ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => { std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let log = $arc_mutex_log.clone(); let mut exit_event_count = 0; let cache_path = $cache_path.clone(); let plugin_thread_sender = $plugin_thread_sender.clone(); 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; } }, ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => { if plugin_permission.permissions.contains($permission_type) { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Granted, Some(cache_path.clone()), ), ); } else { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Denied, Some(cache_path.clone()), ), ); } }, _ => { log.lock().unwrap().push(event); }, } } }) .unwrap() }; } macro_rules! grant_permissions_and_log_actions_in_thread_struct_variant { ( $arc_mutex_log:expr, $exit_event:path, $receiver:expr, $exit_after_count:expr, $permission_type:expr, $cache_path:expr, $plugin_thread_sender:expr, $client_id:expr ) => { std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let log = $arc_mutex_log.clone(); let mut exit_event_count = 0; let cache_path = $cache_path.clone(); let plugin_thread_sender = $plugin_thread_sender.clone(); 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; } }, ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => { if plugin_permission.permissions.contains($permission_type) { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Granted, Some(cache_path.clone()), ), ); } else { let _ = plugin_thread_sender.send( PluginInstruction::PermissionRequestResult( 0, Some($client_id), plugin_permission.permissions, PermissionStatus::Denied, Some(cache_path.clone()), ), ); } }, _ => { log.lock().unwrap().push(event); }, } } }) .unwrap() }; } fn create_plugin_thread( zellij_cwd: Option, session_env_vars: Option>, ) -> ( SenderWithContext, Receiver<(ScreenInstruction, ErrorContext)>, Box, ) { let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from(".")); let session_env_vars = session_env_vars.unwrap_or_else(|| std::env::vars().collect()); let initiating_client_id = 1; let (to_server, _server_receiver): ChannelWithContext = channels::bounded(50); let to_server = SenderWithContext::new(to_server); let (to_screen, screen_receiver): ChannelWithContext = channels::unbounded(); let to_screen = SenderWithContext::new(to_screen); let (to_plugin, plugin_receiver): ChannelWithContext = channels::unbounded(); let to_plugin = SenderWithContext::new(to_plugin); let (to_pty, _pty_receiver): ChannelWithContext = channels::unbounded(); let to_pty = SenderWithContext::new(to_pty); let (to_pty_writer, _pty_writer_receiver): ChannelWithContext = channels::unbounded(); let to_pty_writer = SenderWithContext::new(to_pty_writer); let (to_background_jobs, _background_jobs_receiver): ChannelWithContext = 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 mut config = wasmi::Config::default(); config.set_max_stack_height(1024 * 1024); config.set_max_recursion_depth(1000); let engine = Engine::new(&config); let data_dir = PathBuf::from(tempdir().unwrap().path()); let layout_dir = PathBuf::from(tempdir().unwrap().path()); let default_shell = PathBuf::from("."); let default_shell_action = None; // TODO: change me let mut plugin_aliases = PluginAliases::default(); plugin_aliases.aliases.insert( "fixture_plugin_for_tests".to_owned(), RunPlugin::from_url(&format!( "file:{}/../target/e2e-data/plugins/fixture-plugin-for-tests.wasm", std::env::var_os("CARGO_MANIFEST_DIR") .unwrap() .to_string_lossy() )) .unwrap(), ); 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, engine, data_dir, Box::new(Layout::default()), Some(layout_dir), vec![], vec![], default_shell, zellij_cwd, session_env_vars, default_shell_action, plugin_aliases, InputMode::Normal, Keybinds::default(), Default::default(), initiating_client_id, ) .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); let _ = plugin_thread.join(); } }; (to_plugin, screen_receiver, Box::new(teardown)) } fn create_plugin_thread_with_server_receiver( zellij_cwd: Option, session_env_vars: Option>, ) -> ( SenderWithContext, Receiver<(ServerInstruction, ErrorContext)>, Receiver<(ScreenInstruction, ErrorContext)>, Box, ) { let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from(".")); let session_env_vars = session_env_vars.unwrap_or_else(|| std::env::vars().collect()); let (to_server, server_receiver): ChannelWithContext = channels::bounded(50); let to_server = SenderWithContext::new(to_server); let (to_screen, screen_receiver): ChannelWithContext = channels::unbounded(); let to_screen = SenderWithContext::new(to_screen); let (to_plugin, plugin_receiver): ChannelWithContext = channels::unbounded(); let to_plugin = SenderWithContext::new(to_plugin); let (to_pty, _pty_receiver): ChannelWithContext = channels::unbounded(); let to_pty = SenderWithContext::new(to_pty); let (to_pty_writer, _pty_writer_receiver): ChannelWithContext = channels::unbounded(); let to_pty_writer = SenderWithContext::new(to_pty_writer); let (to_background_jobs, _background_jobs_receiver): ChannelWithContext = 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 mut config = wasmi::Config::default(); config.set_max_stack_height(1024 * 1024); config.set_max_recursion_depth(1000); let engine = Engine::new(&config); let data_dir = PathBuf::from(tempdir().unwrap().path()); let default_shell = PathBuf::from("."); let default_shell_action = None; // TODO: change me let initiating_client_id = 1; 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, engine, data_dir, Box::new(Layout::default()), None, vec![], vec![], default_shell, zellij_cwd, session_env_vars, default_shell_action, PluginAliases::default(), InputMode::Normal, Keybinds::default(), Default::default(), initiating_client_id, ) .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); let _ = plugin_thread.join(); } }; ( to_plugin, server_receiver, screen_receiver, Box::new(teardown), ) } fn create_plugin_thread_with_pty_receiver( zellij_cwd: Option, layout_dir: Option, session_env_vars: Option>, ) -> ( SenderWithContext, Receiver<(PtyInstruction, ErrorContext)>, Receiver<(ScreenInstruction, ErrorContext)>, Box, ) { let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from(".")); let session_env_vars = session_env_vars.unwrap_or_else(|| std::env::vars().collect()); let (to_server, _server_receiver): ChannelWithContext = channels::bounded(50); let to_server = SenderWithContext::new(to_server); let (to_screen, screen_receiver): ChannelWithContext = channels::unbounded(); let to_screen = SenderWithContext::new(to_screen); let (to_plugin, plugin_receiver): ChannelWithContext = channels::unbounded(); let to_plugin = SenderWithContext::new(to_plugin); let (to_pty, pty_receiver): ChannelWithContext = channels::unbounded(); let to_pty = SenderWithContext::new(to_pty); let (to_pty_writer, _pty_writer_receiver): ChannelWithContext = channels::unbounded(); let to_pty_writer = SenderWithContext::new(to_pty_writer); let (to_background_jobs, _background_jobs_receiver): ChannelWithContext = 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 mut config = wasmi::Config::default(); config.set_max_stack_height(1024 * 1024); config.set_max_recursion_depth(1000); let engine = Engine::new(&config); let data_dir = PathBuf::from(tempdir().unwrap().path()); let layout_dir = layout_dir.unwrap_or_else(|| PathBuf::from(tempdir().unwrap().path())); let default_shell = PathBuf::from("."); let default_shell_action = None; // TODO: change me let initiating_client_id = 1; 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, engine, data_dir, Box::new(Layout::default()), Some(layout_dir), vec![], vec![], default_shell, zellij_cwd, session_env_vars, default_shell_action, PluginAliases::default(), InputMode::Normal, Keybinds::default(), Default::default(), initiating_client_id, ) .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); let _ = plugin_thread.join(); } }; (to_plugin, pty_receiver, screen_receiver, Box::new(teardown)) } fn create_plugin_thread_with_background_jobs_receiver( zellij_cwd: Option, session_env_vars: Option>, ) -> ( SenderWithContext, Receiver<(BackgroundJob, ErrorContext)>, Receiver<(ScreenInstruction, ErrorContext)>, Box, ) { let zellij_cwd = zellij_cwd.unwrap_or_else(|| PathBuf::from(".")); let session_env_vars = session_env_vars.unwrap_or_else(|| std::env::vars().collect()); let (to_server, _server_receiver): ChannelWithContext = channels::bounded(50); let to_server = SenderWithContext::new(to_server); let (to_screen, screen_receiver): ChannelWithContext = channels::unbounded(); let to_screen = SenderWithContext::new(to_screen); let (to_plugin, plugin_receiver): ChannelWithContext = channels::unbounded(); let to_plugin = SenderWithContext::new(to_plugin); let (to_pty, _pty_receiver): ChannelWithContext = channels::unbounded(); let to_pty = SenderWithContext::new(to_pty); let (to_pty_writer, _pty_writer_receiver): ChannelWithContext = channels::unbounded(); let to_pty_writer = SenderWithContext::new(to_pty_writer); let (to_background_jobs, background_jobs_receiver): ChannelWithContext = 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 mut config = wasmi::Config::default(); config.set_max_stack_height(1024 * 1024); config.set_max_recursion_depth(1000); let engine = Engine::new(&config); let data_dir = PathBuf::from(tempdir().unwrap().path()); let default_shell = PathBuf::from("."); let default_shell_action = None; // TODO: change me let initiating_client_id = 1; 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, engine, data_dir, Box::new(Layout::default()), None, vec![], vec![], default_shell, zellij_cwd, session_env_vars, default_shell_action, PluginAliases::default(), InputMode::Normal, Keybinds::default(), Default::default(), initiating_client_id, ) .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); let _ = to_background_jobs.send(BackgroundJob::Exit); let _ = plugin_thread.join(); } }; ( to_plugin, background_jobs_receiver, screen_receiver, Box::new(teardown), ) } lazy_static! { static ref PLUGIN_FIXTURE: String = format!( // to populate this file, make sure to run the build-e2e CI job // (or compile the fixture plugin and copy the resulting .wasm blob to the below location) "{}/../target/e2e-data/plugins/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 temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); 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_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_id = plugin_render_asset.plugin_id; let client_id = plugin_render_asset.client_id; let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).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 load_new_plugin_with_plugin_alias() { // 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 temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::Alias(PluginAlias { name: "fixture_plugin_for_tests".to_owned(), configuration: Default::default(), run_plugin: None, ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); 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_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_id = plugin_render_asset.plugin_id; let client_id = plugin_render_asset.client_id; let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).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 temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 3, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // 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 std::thread::sleep(std::time::Duration::from_millis(500)); 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_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_id = plugin_render_asset.plugin_id; let client_id = plugin_render_asset.client_id; let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Payload from worker") { return Some((plugin_id, client_id, plugin_bytes)); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn plugin_workers_persist_state() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 5, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // 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 // we do this a second time so that the worker will log the first message on its own state and // then send us the "received 2 messages" indication we check for below, letting us know it // managed to persist its own state and act upon it //std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::SystemClipboardFailure, )])); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::SystemClipboardFailure, )])); 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_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_id = plugin_render_asset.plugin_id; let client_id = plugin_render_asset.client_id; let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("received 2 messages") { return Some((plugin_id, client_id, plugin_bytes)); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn can_subscribe_to_hd_events() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); // extra long time because we only start the fs watcher on plugin load std::thread::sleep(std::time::Duration::from_millis(5000)); std::fs::OpenOptions::new() .create(true) .write(true) .open(PathBuf::from(temp_folder.path()).join("test1")) .unwrap(); 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_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_id = plugin_render_asset.plugin_id; let client_id = plugin_render_asset.client_id; let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("FileSystemCreate") { return Some((plugin_id, client_id, plugin_bytes)); } } } None }); assert!(plugin_bytes_event.is_some()); } #[test] #[ignore] pub fn switch_to_mode_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_server_instructions = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instructions, ServerInstruction::ChangeMode, server_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('a'))), // this triggers a SwitchToMode(Tab) command in the fixture // plugin )])); server_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let switch_to_mode_event = received_server_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ServerInstruction::ChangeMode(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", switch_to_mode_event)); } #[test] #[ignore] pub fn switch_to_mode_plugin_command_permission_denied() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = deny_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ChangeMode, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('a'))), // this triggers a SwitchToMode(Tab) command in the fixture // plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let switch_to_mode_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ChangeMode(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", switch_to_mode_event)); } #[test] #[ignore] pub fn new_tabs_with_layout_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::NewTab, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('b'))), // this triggers a new_tabs_with_layout command in the fixture // plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let first_new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::NewTab(..) = i { Some(i.clone()) } else { None } }) .clone(); let second_new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ScreenInstruction::NewTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", first_new_tab_event)); assert_snapshot!(format!("{:#?}", second_new_tab_event)); } #[test] #[ignore] pub fn new_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::NewTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('c'))), // this triggers a new_tab command in the fixture // plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::NewTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn go_to_next_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::SwitchTabNext, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('d'))), // this triggers the event in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::SwitchTabNext(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn go_to_previous_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::SwitchTabPrev, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('e'))), // this triggers the event in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::SwitchTabPrev(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn resize_focused_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::Resize, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('f'))), // this triggers the event in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::Resize(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn resize_focused_pane_with_direction_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::Resize, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('g'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::Resize(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn focus_next_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusNextPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('h'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusNextPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn focus_previous_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusPreviousPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('i'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusPreviousPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn focus_last_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusLastPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('l')) .with_alt_modifier() .with_ctrl_modifier(), ), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusLastPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn move_focus_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MoveFocusLeft, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('j'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MoveFocusLeft(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn move_focus_or_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MoveFocusLeftOrPreviousTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('k'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MoveFocusLeftOrPreviousTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn edit_scrollback_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::EditScrollback, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('m'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::EditScrollback(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn write_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::WriteCharacter, screen_receiver, 1, &PermissionType::WriteToStdin, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('n'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::WriteCharacter(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn write_chars_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::WriteCharacter, screen_receiver, 1, &PermissionType::WriteToStdin, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('o'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::WriteCharacter(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn toggle_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ToggleTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('p'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ToggleTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn move_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MovePane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('q'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MovePane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn move_pane_with_direction_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MovePaneLeft, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('r'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MovePaneLeft(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn clear_screen_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ClearScreen, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('s'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ClearScreen(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn scroll_up_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollUp, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('t'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollUp(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn scroll_down_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollDown, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('u'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollDown(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn scroll_to_top_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollToTop, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('v'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollToTop(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn scroll_to_bottom_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollToBottom, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('w'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollToBottom(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn page_scroll_up_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PageScrollUp, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('x'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PageScrollUp(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn page_scroll_down_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PageScrollDown, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('y'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PageScrollDown(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn toggle_focus_fullscreen_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ToggleActiveTerminalFullscreen, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('z'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ToggleActiveTerminalFullscreen(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn toggle_pane_frames_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::TogglePaneFrames, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('1'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::TogglePaneFrames(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn toggle_pane_embed_or_eject_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::TogglePaneEmbedOrFloating, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('2'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::TogglePaneEmbedOrFloating(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn new_pane_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('9'))), )])); pty_thread.join().unwrap(); teardown(); let new_pane_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_pane_event)); } #[test] #[ignore] pub fn toggle_floating_panes_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ToggleFloatingPanes, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('0'))), )])); screen_thread.join().unwrap(); teardown(); let toggle_floating_panes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ToggleFloatingPanes(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", toggle_floating_panes_event)); } #[test] #[ignore] pub fn toggle_floating_panes_with_tab_id_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ToggleFloatingPanesWithTabId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('i')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let toggle_floating_panes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ToggleFloatingPanesWithTabId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", toggle_floating_panes_event)); } #[test] #[ignore] pub fn undo_rename_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::UndoRenamePane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('3'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::UndoRenamePane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn close_focus_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::CloseFocusedPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('4'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::CloseFocusedPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn toggle_active_tab_sync_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ToggleActiveSyncTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('5'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ToggleActiveSyncTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn close_focused_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::CloseTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('6'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::CloseTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn undo_rename_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::UndoRenameTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('7'))), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::UndoRenameTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn previous_swap_layout_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PreviousSwapLayout, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PreviousSwapLayout(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn next_swap_layout_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::NextSwapLayout, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('b')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::NextSwapLayout(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn go_to_tab_name_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::GoToTabName, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::GoToTabName(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn focus_or_create_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::GoToTabName, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::GoToTabName(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn go_to_tab() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::GoToTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('e')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::GoToTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn start_or_reload_plugin() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::StartOrReloadPluginPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('f')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::StartOrReloadPluginPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn quit_zellij_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::ClientExit, server_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('8'))), // this triggers the enent in the fixture plugin )])); server_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_server_instruction .lock() .unwrap() .iter() .find_map(|i| { if let ServerInstruction::ClientExit(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn detach_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::DetachSession, server_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('l'))), // this triggers the enent in the fixture plugin )])); server_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_server_instruction .lock() .unwrap() .iter() .find_map(|i| { if let ServerInstruction::DetachSession(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn open_file_floating_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('h')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!( format!("{:#?}", new_tab_event).replace(&format!("{:?}", temp_folder.path()), "\"CWD\"") ); } #[test] #[ignore] pub fn open_file_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('g')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!( format!("{:#?}", new_tab_event).replace(&format!("{:?}", temp_folder.path()), "\"CWD\"") ); } #[test] #[ignore] pub fn open_file_with_line_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('i')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!( format!("{:#?}", new_tab_event).replace(&format!("{:?}", temp_folder.path()), "\"CWD\"") ); } #[test] #[ignore] pub fn open_file_with_line_floating_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('j')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!( format!("{:#?}", new_tab_event).replace(&format!("{:?}", temp_folder.path()), "\"CWD\"") ); } #[test] #[ignore] pub fn open_terminal_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('k')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn open_terminal_floating_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('l')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn open_command_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('m')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn open_command_pane_floating_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('n')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn switch_to_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::GoToTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('o')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::GoToTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn hide_self_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::SuppressPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('p')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::SuppressPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn show_self_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusPaneWithId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('q')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusPaneWithId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn close_terminal_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ClosePane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('r')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ClosePane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn close_plugin_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ClosePane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('s')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ClosePane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn focus_terminal_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusPaneWithId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('t')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusPaneWithId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn focus_plugin_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusPaneWithId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('u')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusPaneWithId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn rename_terminal_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::RenamePane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('v')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::RenamePane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn rename_plugin_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::RenamePane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('w')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::RenamePane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn rename_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::RenameTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('x')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::RenameTab(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn send_configuration_to_plugins() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let mut configuration = BTreeMap::new(); configuration.insert( "fake_config_key_1".to_owned(), "fake_config_value_1".to_owned(), ); configuration.insert( "fake_config_key_2".to_owned(), "fake_config_value_2".to_owned(), ); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(configuration), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::GoToTabName, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('z')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); // here we make sure we received a rename_tab event with the title being the stringified // (Debug) configuration we sent to the fixture plugin to make sure it got there properly let go_to_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::GoToTabName(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", go_to_tab_event)); } #[test] #[ignore] pub fn request_plugin_permissions() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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::RequestPluginPermissions, screen_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::RequestPluginPermissions(_, plugin_permission) = i { Some(plugin_permission.permissions.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn granted_permission_request_result() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; // here we create a fake screen thread that will send a PermissionStatus::Granted // message for every permission request it gets let screen_thread = std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let cache_path = cache_path.clone(); let plugin_thread_sender = plugin_thread_sender.clone(); move || loop { let (event, _err_ctx) = screen_receiver .recv() .expect("failed to receive event on channel"); match event { ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => { let _ = plugin_thread_sender.send(PluginInstruction::PermissionRequestResult( 0, Some(client_id), plugin_permission.permissions, PermissionStatus::Granted, Some(cache_path.clone()), )); break; }, ScreenInstruction::Exit => { break; }, _ => {}, } } }) .unwrap(); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin.clone(), Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); teardown(); let permission_cache = PermissionCache::from_path_or_default(Some(cache_path)); let mut permissions = permission_cache .get_permissions(PathBuf::from(&*PLUGIN_FIXTURE).display().to_string()) .clone(); let permissions = permissions.as_mut().map(|p| { let mut permissions = p.clone(); permissions.sort_unstable(); permissions }); assert_snapshot!(format!("{:#?}", permissions)); } #[test] #[ignore] pub fn denied_permission_request_result() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; // here we create a fake screen thread that will send a PermissionStatus::Granted // message for every permission request it gets let screen_thread = std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let cache_path = cache_path.clone(); let plugin_thread_sender = plugin_thread_sender.clone(); move || loop { let (event, _err_ctx) = screen_receiver .recv() .expect("failed to receive event on channel"); match event { ScreenInstruction::RequestPluginPermissions(_, plugin_permission) => { let _ = plugin_thread_sender.send(PluginInstruction::PermissionRequestResult( 0, Some(client_id), plugin_permission.permissions, PermissionStatus::Denied, Some(cache_path.clone()), )); break; }, ScreenInstruction::Exit => { break; }, _ => {}, } } }) .unwrap(); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin.clone(), Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('1')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); teardown(); let permission_cache = PermissionCache::from_path_or_default(Some(cache_path)); let permissions = permission_cache.get_permissions(PathBuf::from(&*PLUGIN_FIXTURE).display().to_string()); assert_snapshot!(format!("{:#?}", permissions)); } #[test] #[ignore] pub fn run_command_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, background_jobs_receiver, screen_receiver, teardown) = create_plugin_thread_with_background_jobs_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_background_jobs_instructions = Arc::new(Mutex::new(vec![])); let background_jobs_thread = log_actions_in_thread!( received_background_jobs_instructions, BackgroundJob::RunCommand, background_jobs_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('2')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_background_job = received_background_jobs_instructions .lock() .unwrap() .iter() .find_map(|i| { if let BackgroundJob::RunCommand(..) = i { Some(i.clone()) } else { None } }) .clone(); assert!(format!("{:#?}", new_background_job).contains("user_value_1")); } #[test] #[ignore] pub fn run_command_with_env_vars_and_cwd_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, background_jobs_receiver, screen_receiver, teardown) = create_plugin_thread_with_background_jobs_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_background_jobs_instructions = Arc::new(Mutex::new(vec![])); let background_jobs_thread = log_actions_in_thread!( received_background_jobs_instructions, BackgroundJob::RunCommand, background_jobs_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('3')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_background_jobs_instructions .lock() .unwrap() .iter() .find_map(|i| { if let BackgroundJob::RunCommand(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn web_request_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, background_jobs_receiver, screen_receiver, teardown) = create_plugin_thread_with_background_jobs_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_background_jobs_instructions = Arc::new(Mutex::new(vec![])); let background_jobs_thread = log_actions_in_thread!( received_background_jobs_instructions, BackgroundJob::WebRequest, background_jobs_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::WebAccess, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('4')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); background_jobs_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_background_jobs_instructions .lock() .unwrap() .iter() .find_map(|i| { if let BackgroundJob::WebRequest(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", new_tab_event)); } #[test] #[ignore] pub fn unblock_input_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadCliPipes, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::CliPipe { pipe_id: "input_pipe_id".to_owned(), name: "message_name".to_owned(), payload: Some("message_payload".to_owned()), plugin: None, // broadcast args: None, configuration: None, floating: None, pane_id_to_replace: None, pane_title: None, cwd: None, skip_cache: false, cli_client_id: client_id, }); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let plugin_bytes_events = received_screen_instructions .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ScreenInstruction::PluginBytes(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", plugin_bytes_events)); } #[test] #[ignore] pub fn block_input_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadCliPipes, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); // extra long time because we only start the fs watcher on plugin load std::thread::sleep(std::time::Duration::from_millis(5000)); let _ = plugin_thread_sender.send(PluginInstruction::CliPipe { pipe_id: "input_pipe_id".to_owned(), name: "message_name_block".to_owned(), payload: Some("message_payload".to_owned()), plugin: None, // broadcast args: None, configuration: None, floating: None, pane_id_to_replace: None, pane_title: None, cwd: None, skip_cache: false, cli_client_id: client_id, }); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let plugin_bytes_events = received_screen_instructions .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ScreenInstruction::PluginBytes(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", plugin_bytes_events)); } #[test] #[ignore] pub fn pipe_output_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::CliPipeOutput, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::CliPipe { pipe_id: "input_pipe_id".to_owned(), name: "pipe_output".to_owned(), payload: Some("message_payload".to_owned()), plugin: None, // broadcast args: None, configuration: None, floating: None, pane_id_to_replace: None, pane_title: None, cwd: None, skip_cache: false, cli_client_id: client_id, }); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let plugin_bytes_events = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::CliPipeOutput(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", plugin_bytes_events)); } #[test] #[ignore] pub fn pipe_message_to_plugin_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 3, &PermissionType::ReadCliPipes, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::CliPipe { pipe_id: "input_pipe_id".to_owned(), name: "pipe_message_to_plugin".to_owned(), payload: Some("payload_sent_to_self".to_owned()), plugin: None, // broadcast args: None, configuration: None, floating: None, pane_id_to_replace: None, pane_title: None, cwd: None, skip_cache: false, cli_client_id: client_id, }); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); screen_thread.join().unwrap(); // this might take a while if the cache is cold let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_id = plugin_render_asset.plugin_id; let client_id = plugin_render_asset.client_id; let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Payload from self:") { return Some((plugin_id, client_id, plugin_bytes)); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn switch_session_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::SwitchSession, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('5')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let switch_session_event = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::SwitchSession(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!(format!("{:#?}", switch_session_event) .replace(&format!("{:?}", temp_folder.path()), "\"CWD\"")); } #[test] #[ignore] pub fn switch_session_with_layout_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::SwitchSession, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('7')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let switch_session_event = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::SwitchSession(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!(format!("{:#?}", switch_session_event) .replace(&format!("{:?}", temp_folder.path()), "\"CWD\"")); } #[test] #[ignore] pub fn switch_session_with_layout_and_cwd_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::SwitchSession, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('9')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let switch_session_event = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::SwitchSession(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", switch_session_event)); } #[test] #[ignore] pub fn disconnect_other_clients_plugins_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::DisconnectAllClientsExcept, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('6')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let switch_session_event = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::DisconnectAllClientsExcept(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", switch_session_event)); } #[test] #[ignore] pub fn reconfigure_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread_struct!( received_server_instruction, ServerInstruction::Reconfigure, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('0')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let reconfigure_event = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::Reconfigure { .. } = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", reconfigure_event)); } #[test] #[ignore] pub fn run_plugin_in_specific_cwd() { // note that this test might sometimes fail when run alone without the rest of the suite due to // timing issues let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder.clone()), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let plugin_initial_cwd = plugin_host_folder.join("custom_plugin_cwd"); let _ = std::fs::create_dir_all(&plugin_initial_cwd); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), initial_cwd: Some(plugin_initial_cwd.clone()), }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread!( received_server_instruction, ServerInstruction::ClientExit, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('8')).with_ctrl_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('8'))), // this sends this quit command so tha the test exits cleanly )])); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold assert!( std::fs::read_dir(plugin_initial_cwd) .unwrap() .map(|d| d.unwrap().path().to_string_lossy().to_string()) .collect::>() .join(",") .contains("hi-from-plugin.txt"), "File written into plugin initial cwd" ); } #[test] #[ignore] pub fn hide_pane_with_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::SuppressPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let suppress_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::SuppressPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", suppress_pane_event)); } #[test] #[ignore] pub fn show_pane_with_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::FocusPaneWithId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('b')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let focus_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::FocusPaneWithId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", focus_pane_event)); } #[test] #[ignore] pub fn open_command_pane_background_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnTerminal, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); pty_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let new_tab_event = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnTerminal(..) = i { Some(i.clone()) } else { None } }) .clone(); // we do the replace below to avoid the randomness of the temporary folder in the snapshot // while still testing it assert_snapshot!( format!("{:#?}", new_tab_event).replace(&format!("{:?}", temp_folder.path()), "\"CWD\"") ); } #[test] #[ignore] pub fn rerun_command_pane_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::RerunCommandPane, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let rerun_command_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::RerunCommandPane(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", rerun_command_pane_event)); } #[test] #[ignore] pub fn resize_pane_with_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ResizePaneWithId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('e')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let rerun_command_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ResizePaneWithId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", rerun_command_pane_event)); } #[test] #[ignore] pub fn edit_scrollback_for_pane_with_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::EditScrollbackForPaneWithId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('f')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let rerun_command_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::EditScrollbackForPaneWithId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", rerun_command_pane_event)); } #[test] #[ignore] pub fn write_to_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::WriteToPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('g')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let rerun_command_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::WriteToPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", rerun_command_pane_event)); } #[test] #[ignore] pub fn write_chars_to_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::WriteToPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('h')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let rerun_command_pane_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::WriteToPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", rerun_command_pane_event)); } #[test] #[ignore] pub fn move_pane_with_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MovePaneWithPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('i')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MovePaneWithPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn move_pane_with_pane_id_in_direction_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MovePaneWithPaneIdInDirection, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('j')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MovePaneWithPaneIdInDirection(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn clear_screen_for_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ClearScreenForPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('k')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ClearScreenForPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn scroll_up_in_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollUpInPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('l')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollUpInPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn scroll_down_in_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollDownInPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('m')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollDownInPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn scroll_to_top_in_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollToTopInPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('n')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollToTopInPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn scroll_to_bottom_in_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ScrollToBottomInPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('o')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ScrollToBottomInPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn page_scroll_up_in_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PageScrollUpInPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('p')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PageScrollUpInPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn page_scroll_down_in_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PageScrollDownInPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('q')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PageScrollDownInPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn toggle_pane_id_fullscreen_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::TogglePaneIdFullscreen, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('r')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::TogglePaneIdFullscreen(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn toggle_pane_embed_or_eject_for_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::TogglePaneEmbedOrEjectForPaneId, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('s')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::TogglePaneEmbedOrEjectForPaneId(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn close_tab_with_index_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::CloseTabWithIndex, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('t')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::CloseTabWithIndex(..) = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn break_panes_to_new_tab_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_struct_variant!( received_screen_instructions, ScreenInstruction::BreakPanesToNewTab, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('u')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::BreakPanesToNewTab { .. } = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn break_panes_to_tab_with_index_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_struct_variant!( received_screen_instructions, ScreenInstruction::BreakPanesToTabWithIndex, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('v')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let screen_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::BreakPanesToTabWithIndex { .. } = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", screen_instruction)); } #[test] #[ignore] pub fn reload_plugin_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::RequestStateUpdateForPlugins, // happens on successful plugin (re)load screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('w')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let request_state_update_requests = received_screen_instructions .lock() .unwrap() .iter() .filter(|i| { if let ScreenInstruction::RequestStateUpdateForPlugins = i { true } else { false } }) .count(); assert_eq!(request_state_update_requests, 2); } #[test] #[ignore] pub fn load_new_plugin_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::RequestStateUpdateForPlugins, // happens on successful plugin (re)load screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('x')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let request_state_update_requests = received_screen_instructions .lock() .unwrap() .iter() .filter(|i| { if let ScreenInstruction::RequestStateUpdateForPlugins = i { true } else { false } }) .count(); assert_eq!(request_state_update_requests, 2); } #[test] #[ignore] pub fn rebind_keys_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, server_receiver, screen_receiver, teardown) = create_plugin_thread_with_server_receiver(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let received_server_instruction = Arc::new(Mutex::new(vec![])); let server_thread = log_actions_in_thread_struct!( received_server_instruction, ServerInstruction::RebindKeys, server_receiver, 1 ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('y')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); server_thread.join().unwrap(); // this might take a while if the cache is cold let rebind_event = received_server_instruction .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ServerInstruction::RebindKeys { .. } = i { Some(i.clone()) } else { None } }) .clone(); assert_snapshot!(format!("{:#?}", rebind_event)); } #[test] #[ignore] pub fn list_clients_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ListClientsToPlugin, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('z')).with_alt_modifier()), // this triggers the enent in the fixture plugin )])); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let list_clients_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ListClientsToPlugin(..) = i { Some(i.clone()) } else { None } }) .unwrap(); assert_snapshot!(format!("{:#?}", list_clients_instruction)); } #[test] #[ignore] pub fn before_close_plugin_event() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::HighlightAndUnhighlightPanes, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(5000)); // here we send an unload to plugin id 0 (the first plugin id, presumably this plugin) // so that its BeforeClose Event will be triggered and it will send a // HighlightAndUnhighlightPanes // instruction which we can assert below let _ = plugin_thread_sender.send(PluginInstruction::Unload(0)); screen_thread.join().unwrap(); // this might take a while if the cache is cold teardown(); let sent_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::HighlightAndUnhighlightPanes(..) = i { Some(i.clone()) } else { None } }) .unwrap(); assert_snapshot!(format!("{:#?}", sent_instruction)); } #[test] #[ignore] pub fn show_cursor_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ShowPluginCursor, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('a')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let show_cursor_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ShowPluginCursor(plugin_id, client_id, cursor_position) = i { Some((*plugin_id, *client_id, *cursor_position)) } else { None } }); assert_snapshot!(format!("{:#?}", show_cursor_instruction)); } #[test] #[ignore] pub fn hide_cursor_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::ShowPluginCursor, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('b')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let hide_cursor_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ShowPluginCursor(plugin_id, client_id, cursor_position) = i { Some((*plugin_id, *client_id, *cursor_position)) } else { None } }); assert_snapshot!(format!("{:#?}", hide_cursor_instruction)); } #[test] #[ignore] pub fn copy_to_clipboard_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::CopyTextToClipboard, screen_receiver, 1, &PermissionType::WriteToClipboard, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let copy_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::CopyTextToClipboard(text, plugin_id) = i { Some((text.clone(), *plugin_id)) } else { None } }); assert_snapshot!(format!("{:#?}", copy_instruction)); } #[test] #[ignore] pub fn run_action_plugin_command() { let temp_folder = tempdir().unwrap(); // placed explicitly in the test scope because its // destructor removes the directory let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MoveFocusLeft, screen_receiver, 1, &PermissionType::RunActionsAsUser, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let move_focus_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MoveFocusLeft(client_id, notification_end) = i { Some((*client_id, notification_end.clone())) } else { None } }); assert_snapshot!(format!("{:#?}", move_focus_instruction)); } #[test] #[ignore] pub fn send_sigint_to_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let client_id = 1; let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SendSigintToPaneId, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('e')).with_super_modifier()), )])); pty_thread.join().unwrap(); teardown(); let sigint_instruction = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SendSigintToPaneId(pane_id) = i { Some(*pane_id) } else { None } }); assert_snapshot!(format!("{:#?}", sigint_instruction)); } #[test] #[ignore] pub fn send_sigkill_to_pane_id_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver(Some(plugin_host_folder), None, None); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let client_id = 1; let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SendSigkillToPaneId, pty_receiver, 1 ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let _screen_thread = grant_permissions_and_log_actions_in_thread_naked_variant!( received_screen_instructions, ScreenInstruction::Exit, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('f')).with_super_modifier()), )])); pty_thread.join().unwrap(); teardown(); let sigkill_instruction = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SendSigkillToPaneId(pane_id) = i { Some(*pane_id) } else { None } }); assert_snapshot!(format!("{:#?}", sigkill_instruction)); } #[test] #[ignore] pub fn copy_to_clipboard_without_permission() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let client_id = 1; // This test denies the permission and expects no CopyTextToClipboard instruction let screen_thread = deny_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::CopyTextToClipboard, screen_receiver, 1, &PermissionType::WriteToClipboard, cache_path, plugin_thread_sender, client_id ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('c')).with_super_modifier()), )])); // Give it time to potentially (incorrectly) send the instruction std::thread::sleep(std::time::Duration::from_millis(1000)); let _ = plugin_thread_sender.send(PluginInstruction::Exit); screen_thread.join().unwrap(); teardown(); let copy_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::CopyTextToClipboard(text, plugin_id) = i { Some((text.clone(), *plugin_id)) } else { None } }); // Should be None because permission was denied assert_snapshot!(format!("{:#?}", copy_instruction)); } #[test] #[ignore] pub fn run_action_without_permission() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let client_id = 1; let screen_thread = deny_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::MoveFocusLeft, screen_receiver, 1, &PermissionType::RunActionsAsUser, cache_path, plugin_thread_sender, client_id ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('d')).with_super_modifier()), )])); std::thread::sleep(std::time::Duration::from_millis(1000)); let _ = plugin_thread_sender.send(PluginInstruction::Exit); screen_thread.join().unwrap(); teardown(); let move_focus_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::MoveFocusLeft(client_id, notification_end) = i { Some((*client_id, notification_end.clone())) } else { None } }); // Should be None because permission was denied assert_snapshot!(format!("{:#?}", move_focus_instruction)); } #[test] #[ignore] pub fn generate_random_name_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+A to trigger generate_random_name() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Generated name") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn dump_layout_success_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+B to trigger dump_layout("default") let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('b')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Layout dump") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn dump_layout_not_found_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+C to trigger dump_layout("nonexistent_layout_xyz") let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('c')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Layout dump error") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn get_layout_dir_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+D to trigger get_layout_dir() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('d')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Got layout folder") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn get_focused_pane_info_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread_struct_variant!( received_screen_instructions, ScreenInstruction::GetFocusedPaneInfo, screen_receiver, 1, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+E to trigger get_focused_pane_info() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('e')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let focused_pane_info_query = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::GetFocusedPaneInfo { .. } = i { return Some(i.clone()); } else { None } }); assert_snapshot!(format!("{:#?}", focused_pane_info_query)); } #[test] #[ignore] pub fn dump_session_layout_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+F to trigger dump_session_layout() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('f')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let dump_layout_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::DumpLayoutToPlugin { .. } = i { return Some(i.clone()); } None }); assert_snapshot!(format!("{:#?}", dump_layout_instruction)); } #[test] #[ignore] pub fn dump_session_layout_for_tab_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+G to trigger dump_session_layout_for_tab(0) let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('g')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let dump_layout_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::DumpLayoutToPlugin { .. } = i { return Some(i.clone()); } None }); assert_snapshot!(format!("{:#?}", dump_layout_instruction)); } #[test] #[ignore] pub fn parse_layout_success_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+H to trigger parse_layout() with valid KDL let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('h')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Parse success") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn parse_layout_error_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+I to trigger parse_layout() with invalid KDL let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('i')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Parse error:") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn save_layout_success_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+A to trigger save_layout() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Save layout") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn save_layout_already_exists_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 3, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+A to save the layout first time let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); std::thread::sleep(std::time::Duration::from_millis(200)); // Send Ctrl+Alt+B to try saving without overwrite let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('b')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("error") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn save_layout_with_overwrite_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+C to save with overwrite=true let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('c')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Save layout") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn save_layout_invalid_kdl_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+D to save invalid KDL let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('d')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("invalid layout error") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn rename_layout_success_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 3, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+A to save layout first let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); std::thread::sleep(std::time::Duration::from_millis(200)); // Send Ctrl+Alt+E to rename layout let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('e')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Rename layout") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn rename_layout_not_found_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+F to rename nonexistent layout let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('f')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Rename") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn delete_layout_success_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 3, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+A to save layout first let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); std::thread::sleep(std::time::Duration::from_millis(200)); // Send Ctrl+Alt+E to rename layout let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('e')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); std::thread::sleep(std::time::Duration::from_millis(200)); // Send Ctrl+Alt+G to delete renamed layout let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('g')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .rev() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Delete layout") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn delete_layout_not_found_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+H to delete nonexistent layout let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('h')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Delete") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn save_layout_path_traversal_blocked_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+I to test path traversal blocking let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('i')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Path traversal") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn edit_layout_plugin_command() { let temp_host_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_host_folder.path()); let temp_layout_folder = tempdir().unwrap(); let layout_folder_path = PathBuf::from(temp_layout_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, pty_receiver, screen_receiver, teardown) = create_plugin_thread_with_pty_receiver( Some(plugin_host_folder), Some(layout_folder_path), None, ); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let received_pty_instructions = Arc::new(Mutex::new(vec![])); let pty_thread = log_actions_in_thread!( received_pty_instructions, PtyInstruction::SpawnInPlaceTerminal, pty_receiver, 1 ); let _screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 3, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+J to trigger edit_layout() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('j')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); pty_thread.join().unwrap(); teardown(); // this is the editor pane for the layout being spawned let spawn_terminal_instruction = received_pty_instructions .lock() .unwrap() .iter() .find_map(|i| { if let PtyInstruction::SpawnInPlaceTerminal(..) = i { Some(i.clone()) } else { None } }); assert_snapshot!(format!("{:#?}", spawn_terminal_instruction).replace( &format!("{}", temp_layout_folder.path().display().to_string()), "LAYOUT_FOLDER_PATH" )) } #[test] #[ignore] pub fn override_layout_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::OverrideLayout, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+K to trigger override_layout() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('k')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let override_layout_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::OverrideLayout(..) = i { Some(i.clone()) } else { None } }); assert_snapshot!(format!("{:#?}", override_layout_instruction)); } #[test] #[ignore] pub fn generate_random_name_permission_denied() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let client_id = 1; let screen_thread = deny_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, client_id ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Shift+A to trigger generate_random_name() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); // Give it time to potentially (incorrectly) send the output std::thread::sleep(std::time::Duration::from_millis(1000)); let _ = plugin_thread_sender.send(PluginInstruction::Exit); screen_thread.join().unwrap(); teardown(); let plugin_bytes_with_name = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Generated name:") { return Some(plugin_bytes); } } } None }); // Should be None because permission was denied assert_snapshot!(format!("{:#?}", plugin_bytes_with_name)); } #[test] #[ignore] pub fn save_layout_permission_denied() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), None); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let client_id = 1; let screen_thread = deny_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Ctrl+Alt+A to trigger save_layout() let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('a')) .with_ctrl_modifier() .with_alt_modifier(), ), )])); // Give it time to potentially (incorrectly) send the output std::thread::sleep(std::time::Duration::from_millis(1000)); let _ = plugin_thread_sender.send(PluginInstruction::Exit); screen_thread.join().unwrap(); teardown(); let plugin_bytes_with_save = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Save layout") { return Some(plugin_bytes); } } } None }); // Should be None because permission was denied assert_snapshot!(format!("{:#?}", plugin_bytes_with_save)); } #[test] #[ignore] pub fn plugin_receives_config_change_event() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let mut initial_config = BTreeMap::new(); initial_config.insert("theme".to_owned(), "dark".to_owned()); initial_config.insert("size".to_owned(), "large".to_owned()); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(initial_config.clone()), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin.clone(), Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let mut new_config = BTreeMap::new(); new_config.insert("theme".to_owned(), "light".to_owned()); // Changed new_config.insert("size".to_owned(), "small".to_owned()); // Changed let mut plugin_aliases = PluginAliases::default(); plugin_aliases.aliases.insert( "test_plugin".to_owned(), RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(new_config.clone()), ..Default::default() }, ); let _ = plugin_thread_sender.send(PluginInstruction::DetectPluginConfigChanges(plugin_aliases)); screen_thread.join().unwrap(); teardown(); let plugin_renders = received_screen_instructions .lock() .unwrap() .iter() .filter_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_output = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); return Some(plugin_output); } } None }) .collect::>(); assert!( plugin_renders .iter() .any(|r| r.contains("PluginConfigurationChanged")), "Plugin should receive PluginConfigurationChanged event" ); let last_render = plugin_renders .last() .expect("Should have at least one render"); assert!( last_render.contains("theme") && last_render.contains("light"), "Plugin should render with new theme=light config" ); assert!( last_render.contains("size") && last_render.contains("small"), "Plugin should render with new size=small config" ); assert_snapshot!(format!("{:#?}", last_render)); } #[test] #[ignore] pub fn plugin_does_not_receive_event_when_config_unchanged() { // Test that plugin does NOT receive event when config hasn't changed let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); // Initial plugin configuration let mut initial_config = BTreeMap::new(); initial_config.insert("theme".to_owned(), "dark".to_owned()); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(initial_config.clone()), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, // Only expect initial load and one update, no config change event &PermissionType::ReadApplicationState, cache_path, plugin_thread_sender, client_id ); // Load plugin let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin.clone(), Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send SAME configuration (unchanged) let mut plugin_aliases = PluginAliases::default(); plugin_aliases.aliases.insert( "test_plugin".to_owned(), RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(initial_config.clone()), // SAME config ..Default::default() }, ); let _ = plugin_thread_sender.send(PluginInstruction::DetectPluginConfigChanges(plugin_aliases)); // Send an unrelated event to trigger a render let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::InputReceived, )])); screen_thread.join().unwrap(); teardown(); // Verify plugin did NOT receive PluginConfigurationChanged event let plugin_renders = received_screen_instructions .lock() .unwrap() .iter() .filter_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_output = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); return Some(plugin_output); } } None }) .collect::>(); assert!( !plugin_renders .iter() .any(|r| r.contains("PluginConfigurationChanged")), "Plugin should NOT receive PluginConfigurationChanged when config unchanged" ); } #[test] #[ignore] pub fn get_session_environment_variables_plugin_command() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let mut test_env_vars = std::collections::BTreeMap::new(); test_env_vars.insert("TEST_ENV_VAR_1".to_string(), "test_value_123".to_string()); test_env_vars.insert( "TEST_ENV_VAR_2".to_string(), "another_test_value".to_string(), ); test_env_vars.insert("PATH".to_string(), "/usr/bin:/bin".to_string()); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder.clone()), Some(test_env_vars)); let received_screen_instructions = Arc::new(Mutex::new(vec![])); let screen_thread = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ReadSessionEnvironmentVariables, cache_path, plugin_thread_sender, 1 ); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key( KeyWithModifier::new(BareKey::Char('j')) .with_ctrl_modifier() .with_shift_modifier(), ), )])); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("Got") && plugin_bytes.contains("environment variables") { return Some(plugin_bytes); } } } None }); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } // ===================================================================== // Plugin Highlight API Integration Tests // ===================================================================== use crate::panes::PaneId; #[test] #[ignore] pub fn set_pane_regex_highlights_via_plugin() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_struct_variant!( received_screen_instructions, ScreenInstruction::SetPluginRegexHighlights, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Super+g key event to trigger set_pane_regex_highlights in the fixture plugin let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('g')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let highlight_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::SetPluginRegexHighlights { pane_id, plugin_id, highlights, } = i { Some((*pane_id, *plugin_id, highlights.clone())) } else { None } }); assert!( highlight_instruction.is_some(), "Expected SetPluginRegexHighlights instruction" ); let (pane_id, _plugin_id, highlights) = highlight_instruction.unwrap(); assert_eq!(pane_id, PaneId::Terminal(1)); assert_eq!(highlights.len(), 1); assert_eq!(highlights[0].pattern, "test_pattern"); assert!(highlights[0].italic); assert!(highlights[0].underline); } #[test] #[ignore] pub fn clear_pane_highlights_via_plugin() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread_struct_variant!( received_screen_instructions, ScreenInstruction::ClearPluginHighlights, screen_receiver, 1, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send Super+h key event to trigger clear_pane_highlights in the fixture plugin let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::Key(KeyWithModifier::new(BareKey::Char('h')).with_super_modifier()), )])); screen_thread.join().unwrap(); teardown(); let clear_instruction = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::ClearPluginHighlights { pane_id, plugin_id } = i { Some((*pane_id, *plugin_id)) } else { None } }); assert!( clear_instruction.is_some(), "Expected ClearPluginHighlights instruction" ); let (pane_id, _plugin_id) = clear_instruction.unwrap(); assert_eq!(pane_id, PaneId::Terminal(1)); } #[test] #[ignore] pub fn highlight_clicked_event_delivered_to_plugin() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(None, None); let plugin_should_float = Some(false); let plugin_title = Some("test_plugin".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: Default::default(), ..Default::default() }); 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 = grant_permissions_and_log_actions_in_thread!( received_screen_instructions, ScreenInstruction::PluginBytes, screen_receiver, 2, &PermissionType::ChangeApplicationState, cache_path, plugin_thread_sender, client_id ); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, // close_replaced_pane plugin_title, run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Send a HighlightClicked event directly to the plugin let _ = plugin_thread_sender.send(PluginInstruction::HighlightClicked { plugin_id: 0, client_id, pane_id: PaneId::Terminal(1), pattern: "test".into(), matched_string: "test_match".into(), context: BTreeMap::new(), }); screen_thread.join().unwrap(); teardown(); let plugin_bytes_event = received_screen_instructions .lock() .unwrap() .iter() .find_map(|i| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = i { for plugin_render_asset in plugin_render_assets { let plugin_bytes = plugin_render_asset.bytes.clone(); let plugin_bytes = String::from_utf8_lossy(plugin_bytes.as_slice()).to_string(); if plugin_bytes.contains("HighlightClicked") { return Some(plugin_bytes); } } } None }); assert!( plugin_bytes_event.is_some(), "Expected PluginBytes containing 'HighlightClicked'" ); assert_snapshot!(format!("{:#?}", plugin_bytes_event)); } #[test] #[ignore] pub fn mode_update_payload_is_lightweight_for_opted_in_plugins() { // Plugin A subscribes to ModeUpdate only (legacy behavior — receives full keybinds) // Plugin B subscribes to InitialKeybinds + ModeUpdate (receives stripped keybinds) let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; // Plugin A: subscribes to ModeUpdate only (legacy) let mut config_a = BTreeMap::new(); config_a.insert("subscribe_mode_update".to_owned(), "true".to_owned()); let run_plugin_a = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(config_a), ..Default::default() }); // Plugin B: subscribes to InitialKeybinds + ModeUpdate (lightweight) let mut config_b = BTreeMap::new(); config_b.insert("subscribe_initial_keybinds".to_owned(), "true".to_owned()); let run_plugin_b = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(config_b), ..Default::default() }); let received_screen_instructions = Arc::new(Mutex::new(vec![])); // Spawn a screen thread that grants permissions and collects all instructions until Exit let screen_thread = std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let log = received_screen_instructions.clone(); let cache_path = cache_path.clone(); let plugin_thread_sender = plugin_thread_sender.clone(); move || loop { let (event, _err_ctx) = screen_receiver .recv() .expect("failed to receive event on channel"); match event { ScreenInstruction::RequestPluginPermissions(plugin_id, plugin_permission) => { let _ = plugin_thread_sender.send(PluginInstruction::PermissionRequestResult( plugin_id, Some(client_id), plugin_permission.permissions, PermissionStatus::Granted, Some(cache_path.clone()), )); }, ScreenInstruction::Exit => { break; }, _ => { log.lock().unwrap().push(event); }, } } }) .unwrap(); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); // Load plugin A let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, Some("plugin_a".to_owned()), run_plugin_a, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Load plugin B let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, Some("plugin_b".to_owned()), run_plugin_b, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Set keybinds on all plugins via Reconfigure so that Plugin A (legacy) // will have non-empty keybinds in its internal state let mut keybind_map = std::collections::HashMap::new(); let mut mode_map = std::collections::HashMap::new(); mode_map.insert(KeyWithModifier::new(BareKey::Char('q')), vec![Action::Quit]); keybind_map.insert(InputMode::Normal, mode_map); let test_keybinds = Keybinds(keybind_map); let _ = plugin_thread_sender.send(PluginInstruction::Reconfigure { client_id, keybinds: Some(test_keybinds), default_mode: None, default_shell: None, layout_dir: None, was_written_to_disk: false, }); std::thread::sleep(std::time::Duration::from_millis(500)); // Send ModeUpdate to all plugins (None = broadcast) // The keybinds field here doesn't matter — apply_event_to_plugin replaces it let mode_info = ModeInfo { mode: InputMode::Normal, ..Default::default() }; let _ = plugin_thread_sender.send(PluginInstruction::Update(vec![( None, Some(client_id), Event::ModeUpdate(mode_info), )])); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); screen_thread.join().unwrap(); let instructions = received_screen_instructions.lock().unwrap(); let mut plugin_a_has_keybinds = false; let mut plugin_b_has_empty_keybinds = false; for instruction in instructions.iter() { if let ScreenInstruction::PluginBytes(plugin_render_assets) = instruction { for asset in plugin_render_assets { let bytes = String::from_utf8_lossy(&asset.bytes).to_string(); if bytes.contains("ModeUpdate") { // Plugin A (plugin_id 0) should have keybinds with 'q' -> Quit // Plugin B (plugin_id 1) should have empty keybinds if bytes.contains("Quit") { plugin_a_has_keybinds = true; } else if bytes.contains("ModeUpdate") && !bytes.contains("Quit") { plugin_b_has_empty_keybinds = true; } } } } } assert!( plugin_a_has_keybinds, "Plugin A (legacy) should receive full ModeUpdate with keybinds" ); assert!( plugin_b_has_empty_keybinds, "Plugin B (InitialKeybinds subscriber) should receive ModeUpdate with empty keybinds" ); } #[test] #[ignore] pub fn reconfiguration_resends_keybinds_to_opted_in_plugins() { let temp_folder = tempdir().unwrap(); let plugin_host_folder = PathBuf::from(temp_folder.path()); let cache_path = plugin_host_folder.join("permissions_test.kdl"); let (plugin_thread_sender, screen_receiver, teardown) = create_plugin_thread(Some(plugin_host_folder), None); let plugin_should_float = Some(false); let tab_index = 1; let client_id = 1; let size = Size { cols: 121, rows: 20, }; // Plugin subscribes to InitialKeybinds + ModeUpdate let mut config = BTreeMap::new(); config.insert("subscribe_initial_keybinds".to_owned(), "true".to_owned()); let run_plugin = RunPluginOrAlias::RunPlugin(RunPlugin { _allow_exec_host_cmd: false, location: RunPluginLocation::File(PathBuf::from(&*PLUGIN_FIXTURE)), configuration: PluginUserConfiguration::new(config), ..Default::default() }); let received_screen_instructions = Arc::new(Mutex::new(vec![])); // Spawn a screen thread that grants permissions and collects all instructions until Exit let screen_thread = std::thread::Builder::new() .name("fake_screen_thread".to_string()) .spawn({ let log = received_screen_instructions.clone(); let cache_path = cache_path.clone(); let plugin_thread_sender = plugin_thread_sender.clone(); move || loop { let (event, _err_ctx) = screen_receiver .recv() .expect("failed to receive event on channel"); match event { ScreenInstruction::RequestPluginPermissions(plugin_id, plugin_permission) => { let _ = plugin_thread_sender.send(PluginInstruction::PermissionRequestResult( plugin_id, Some(client_id), plugin_permission.permissions, PermissionStatus::Granted, Some(cache_path.clone()), )); }, ScreenInstruction::Exit => { break; }, _ => { log.lock().unwrap().push(event); }, } } }) .unwrap(); let _ = plugin_thread_sender.send(PluginInstruction::AddClient(client_id)); let _ = plugin_thread_sender.send(PluginInstruction::Load( plugin_should_float, false, false, Some("test_plugin".to_owned()), run_plugin, Some(tab_index), None, client_id, size, None, None, false, None, None, None, )); std::thread::sleep(std::time::Duration::from_millis(500)); // Build new keybinds for reconfiguration let mut keybind_map = std::collections::HashMap::new(); let mut mode_map = std::collections::HashMap::new(); mode_map.insert(KeyWithModifier::new(BareKey::Char('x')), vec![Action::Quit]); keybind_map.insert(InputMode::Normal, mode_map); let new_keybinds = Keybinds(keybind_map); // Send Reconfigure let _ = plugin_thread_sender.send(PluginInstruction::Reconfigure { client_id, keybinds: Some(new_keybinds), default_mode: None, default_shell: None, layout_dir: None, was_written_to_disk: false, }); std::thread::sleep(std::time::Duration::from_millis(500)); teardown(); screen_thread.join().unwrap(); let instructions = received_screen_instructions.lock().unwrap(); let initial_keybinds_event = instructions.iter().find_map(|instruction| { if let ScreenInstruction::PluginBytes(plugin_render_assets) = instruction { for asset in plugin_render_assets { let bytes = String::from_utf8_lossy(&asset.bytes).to_string(); if bytes.contains("InitialKeybinds") { return Some(bytes); } } } None }); assert!( initial_keybinds_event.is_some(), "Plugin should receive InitialKeybinds event after reconfiguration" ); // Note: the keybinds content in InitialKeybinds may be empty due to a race condition // between the executor thread updating keybinds and send_initial_keybinds_to_plugin // reading them synchronously. The important thing is that the event IS delivered. }