diff options
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/common/input/config.rs | 135 | ||||
-rw-r--r-- | src/common/mod.rs | 11 | ||||
-rw-r--r-- | src/main.rs | 11 | ||||
-rw-r--r-- | src/tests/integration/basic.rs | 71 | ||||
-rw-r--r-- | src/tests/integration/close_pane.rs | 79 | ||||
-rw-r--r-- | src/tests/integration/compatibility.rs | 121 | ||||
-rw-r--r-- | src/tests/integration/layouts.rs | 3 | ||||
-rw-r--r-- | src/tests/integration/move_focus_down.rs | 13 | ||||
-rw-r--r-- | src/tests/integration/move_focus_left.rs | 13 | ||||
-rw-r--r-- | src/tests/integration/move_focus_right.rs | 13 | ||||
-rw-r--r-- | src/tests/integration/move_focus_up.rs | 13 | ||||
-rw-r--r-- | src/tests/integration/resize_down.rs | 79 | ||||
-rw-r--r-- | src/tests/integration/resize_left.rs | 79 | ||||
-rw-r--r-- | src/tests/integration/resize_right.rs | 79 | ||||
-rw-r--r-- | src/tests/integration/resize_up.rs | 79 | ||||
-rw-r--r-- | src/tests/integration/tabs.rs | 49 | ||||
-rw-r--r-- | src/tests/integration/terminal_window_resize.rs | 9 | ||||
-rw-r--r-- | src/tests/integration/toggle_fullscreen.rs | 13 |
21 files changed, 688 insertions, 185 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 76dd7497f..20878035f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] * Fix the tab '(Sync)' suffix in named tabs (https://github.com/zellij-org/zellij/pull/410) * Improve performance when multiple panes are open (https://github.com/zellij-org/zellij/pull/318) +* Improve error reporting and tests of configuration (https://github.com/zellij-org/zellij/pull/423) ## [0.6.0] - 2021-04-29 * Doesn't quit anymore on single `q` press while in tab mode (https://github.com/zellij-org/zellij/pull/342) diff --git a/Cargo.lock b/Cargo.lock index 0b18bd6bb..d5a0de39d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2239,6 +2239,7 @@ dependencies = [ "strip-ansi-escapes", "structopt", "strum", + "tempfile", "termion", "termios", "unicode-truncate", diff --git a/Cargo.toml b/Cargo.toml index 1988dffa3..bfdfa8843 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ features = ["unstable"] [dev-dependencies] insta = "1.6.0" +tempfile = "3.2.0" [build-dependencies] structopt = "0.3" diff --git a/src/common/input/config.rs b/src/common/input/config.rs index 62ec3dfe1..daa52b03b 100644 --- a/src/common/input/config.rs +++ b/src/common/input/config.rs @@ -6,14 +6,17 @@ use std::io::{self, Read}; use std::path::{Path, PathBuf}; use super::keybinds::{Keybinds, KeybindsFromYaml}; -use crate::cli::ConfigCli; +use crate::cli::{CliArgs, ConfigCli}; use crate::common::install; use serde::Deserialize; +use std::convert::TryFrom; + +const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml"; type ConfigResult = Result<Config, ConfigError>; -/// Intermediate deserialisation config struct +/// Intermediate deserialization config struct #[derive(Debug, Deserialize)] pub struct ConfigFromYaml { pub keybinds: Option<KeybindsFromYaml>, @@ -27,13 +30,13 @@ pub struct Config { #[derive(Debug)] pub enum ConfigError { - // Deserialisation error + // Deserialization error Serde(serde_yaml::Error), // Io error Io(io::Error), // Io error with path context IoPath(io::Error, PathBuf), - // Internal Deserialisation Error + // Internal Deserialization Error FromUtf8(std::string::FromUtf8Error), } @@ -44,6 +47,35 @@ impl Default for Config { } } +impl TryFrom<&CliArgs> for Config { + type Error = ConfigError; + + fn try_from(opts: &CliArgs) -> ConfigResult { + if let Some(ref path) = opts.config { + return Config::new(&path); + } + + if let Some(ConfigCli::Config { clean, .. }) = opts.option { + if clean { + return Config::from_default_assets(); + } + } + + let config_dir = opts.config_dir.clone().or_else(install::default_config_dir); + + if let Some(ref config) = config_dir { + let path = config.join(DEFAULT_CONFIG_FILE_NAME); + if path.exists() { + Config::new(&path) + } else { + Config::from_default_assets() + } + } else { + Config::from_default_assets() + } + } +} + impl Config { /// Uses defaults, but lets config override them. pub fn from_yaml(yaml_config: &str) -> ConfigResult { @@ -71,45 +103,6 @@ impl Config { pub fn from_default_assets() -> ConfigResult { Self::from_yaml(String::from_utf8(install::DEFAULT_CONFIG.to_vec())?.as_str()) } - - /// Entry point of the configuration - #[cfg(not(test))] - pub fn from_cli_config( - location: Option<PathBuf>, - cli_config: Option<ConfigCli>, - config_dir: Option<PathBuf>, - ) -> ConfigResult { - if let Some(path) = location { - return Config::new(&path); - } - - if let Some(ConfigCli::Config { clean, .. }) = cli_config { - if clean { - return Config::from_default_assets(); - } - } - - if let Some(config) = config_dir { - let path = config.join("config.yaml"); - if path.exists() { - Config::new(&path) - } else { - Config::from_default_assets() - } - } else { - Config::from_default_assets() - } - } - - /// In order not to mess up tests from changing configurations - #[cfg(test)] - pub fn from_cli_config( - _: Option<PathBuf>, - _: Option<ConfigCli>, - _: Option<PathBuf>, - ) -> ConfigResult { - Ok(Config::default()) - } } impl Display for ConfigError { @@ -119,7 +112,7 @@ impl Display for ConfigError { ConfigError::IoPath(ref err, ref path) => { write!(formatter, "IoError: {}, File: {}", err, path.display(),) } - ConfigError::Serde(ref err) => write!(formatter, "Deserialisation error: {}", err), + ConfigError::Serde(ref err) => write!(formatter, "Deserialization error: {}", err), ConfigError::FromUtf8(ref err) => write!(formatter, "FromUtf8Error: {}", err), } } @@ -157,20 +150,56 @@ impl From<std::string::FromUtf8Error> for ConfigError { // The unit test location. #[cfg(test)] mod config_test { + use std::io::Write; + + use tempfile::tempdir; + use super::*; #[test] - fn clean_option_equals_default_config() { - let cli_config = ConfigCli::Config { clean: true }; - let config = Config::from_cli_config(None, Some(cli_config), None).unwrap(); - let default = Config::default(); - assert_eq!(config, default); + fn try_from_cli_args_with_config() { + let arbitrary_config = PathBuf::from("nonexistent.yaml"); + let mut opts = CliArgs::default(); + opts.config = Some(arbitrary_config); + println!("OPTS= {:?}", opts); + let result = Config::try_from(&opts); + assert!(result.is_err()); + } + + #[test] + fn try_from_cli_args_with_option_clean() { + let mut opts = CliArgs::default(); + opts.option = Some(ConfigCli::Config { clean: true }); + let result = Config::try_from(&opts); + assert!(result.is_ok()); + } + + #[test] + fn try_from_cli_args_with_config_dir() { + let mut opts = CliArgs::default(); + let tmp = tempdir().unwrap(); + File::create(tmp.path().join(DEFAULT_CONFIG_FILE_NAME)) + .unwrap() + .write_all(b"keybinds: invalid\n") + .unwrap(); + opts.config_dir = Some(tmp.path().to_path_buf()); + let result = Config::try_from(&opts); + assert!(result.is_err()); + } + + #[test] + fn try_from_cli_args_with_config_dir_without_config() { + let mut opts = CliArgs::default(); + let tmp = tempdir().unwrap(); + opts.config_dir = Some(tmp.path().to_path_buf()); + let result = Config::try_from(&opts); + assert_eq!(result.unwrap(), Config::default()); } #[test] - fn no_config_option_file_equals_default_config() { - let config = Config::from_cli_config(None, None, None).unwrap(); - let default = Config::default(); - assert_eq!(config, default); + fn try_from_cli_args_default() { + let opts = CliArgs::default(); + let result = Config::try_from(&opts); + assert_eq!(result.unwrap(), Config::default()); } } diff --git a/src/common/mod.rs b/src/common/mod.rs index c9b8e0e26..b17fce34e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -120,7 +120,7 @@ pub enum AppInstruction { /// Start Zellij with the specified [`OsApi`] and command-line arguments. // FIXME this should definitely be modularized and split into different functions. -pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) { +pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs, config: Config) { let take_snapshot = "\u{1b}[?1049h"; os_input.unset_raw_mode(0); let _ = os_input @@ -130,15 +130,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) { env::set_var(&"ZELLIJ", "0"); - let config_dir = opts.config_dir.or_else(install::default_config_dir); - - let config = Config::from_cli_config(opts.config, opts.option, config_dir) - .map_err(|e| { - eprintln!("There was an error in the config file:\n{}", e); - std::process::exit(1); - }) - .unwrap(); - let command_is_executing = CommandIsExecuting::new(); let full_screen_ws = os_input.get_terminal_size_using_fd(0); diff --git a/src/main.rs b/src/main.rs index 12269ae91..e95471fb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod client; use crate::cli::CliArgs; use crate::command_is_executing::CommandIsExecuting; +use crate::common::input::config::Config; use crate::os_input_output::get_os_input; use crate::utils::{ consts::{ZELLIJ_IPC_PIPE, ZELLIJ_TMP_DIR, ZELLIJ_TMP_LOG_DIR}, @@ -17,12 +18,20 @@ use common::{ command_is_executing, errors, install, os_input_output, pty_bus, screen, start, utils, wasm_vm, ApiCommand, }; +use std::convert::TryFrom; use std::io::Write; use std::os::unix::net::UnixStream; use structopt::StructOpt; pub fn main() { let opts = CliArgs::from_args(); + let config = match Config::try_from(&opts) { + Ok(config) => config, + Err(e) => { + eprintln!("There was an error in the config file:\n{}", e); + std::process::exit(1); + } + }; if let Some(split_dir) = opts.split { match split_dir { 'h' => { @@ -66,6 +75,6 @@ pub fn main() { let os_input = get_os_input(); atomic_create_dir(ZELLIJ_TMP_DIR).unwrap(); atomic_create_dir(ZELLIJ_TMP_LOG_DIR).unwrap(); - start(Box::new(os_input), opts); + start(Box::new(os_input), opts, config); } } diff --git a/src/tests/integration/basic.rs b/src/tests/integration/basic.rs index 9b1b2bdd7..f785666aa 100644 --- a/src/tests/integration/basic.rs +++ b/src/tests/integration/basic.rs @@ -1,6 +1,7 @@ use crate::panes::PositionAndSize; use ::insta::assert_snapshot; +use crate::common::input::config::Config; use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::commands::{ PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE, SCROLL_MODE, SCROLL_PAGE_DOWN_IN_SCROLL_MODE, @@ -26,7 +27,11 @@ pub fn starts_with_one_terminal() { }; let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&QUIT]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -49,7 +54,11 @@ pub fn split_terminals_vertically() { }; let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&PANE_MODE, &SPLIT_RIGHT_IN_PANE_MODE, &QUIT]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -72,7 +81,11 @@ pub fn split_terminals_horizontally() { }; let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&PANE_MODE, &SPLIT_DOWN_IN_PANE_MODE, &QUIT]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -102,7 +115,11 @@ pub fn split_largest_terminal() { &SPAWN_TERMINAL_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -125,7 +142,11 @@ pub fn cannot_split_terminals_vertically_when_active_terminal_is_too_small() { }; let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&PANE_MODE, &SPLIT_RIGHT_IN_PANE_MODE, &QUIT]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -148,7 +169,11 @@ pub fn cannot_split_terminals_horizontally_when_active_terminal_is_too_small() { }; let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&PANE_MODE, &SPLIT_DOWN_IN_PANE_MODE, &QUIT]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -171,7 +196,11 @@ pub fn cannot_split_largest_terminal_when_there_is_no_room() { }; let mut fake_input_output = get_fake_os_input(&fake_win_size); fake_input_output.add_terminal_input(&[&PANE_MODE, &SPAWN_TERMINAL_IN_PANE_MODE, &QUIT]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -202,7 +231,11 @@ pub fn scrolling_up_inside_a_pane() { &SCROLL_UP_IN_SCROLL_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -235,7 +268,11 @@ pub fn scrolling_down_inside_a_pane() { &SCROLL_DOWN_IN_SCROLL_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -265,7 +302,11 @@ pub fn scrolling_page_up_inside_a_pane() { &SCROLL_PAGE_UP_IN_SCROLL_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -298,7 +339,11 @@ pub fn scrolling_page_down_inside_a_pane() { &SCROLL_PAGE_DOWN_IN_SCROLL_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer .output_frames @@ -332,7 +377,7 @@ pub fn max_panes() { ]); let mut opts = CliArgs::default(); opts.max_panes = Some(4); - start(Box::new(fake_input_output.clone()), opts); + start(Box::new(fake_input_output.clone()), opts, Config::default()); let output_frames = fake_input_output .stdout_writer .output_frames @@ -364,7 +409,7 @@ pub fn toggle_focused_pane_fullscreen() { ]); let mut opts = CliArgs::default(); opts.max_panes = Some(4); - start(Box::new(fake_input_output.clone()), opts); + start(Box::new(fake_input_output.clone()), opts, Config::default()); let output_frames = fake_input_output .stdout_writer .output_frames diff --git a/src/tests/integration/close_pane.rs b/src/tests/integration/close_pane.rs index c7ce2038d..f73f4027d 100644 --- a/src/tests/integration/close_pane.rs +++ b/src/tests/integration/close_pane.rs @@ -5,6 +5,7 @@ use crate::tests::fakes::FakeInputOutput; use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots}; use crate::{start, CliArgs}; +use crate::common::input::config::Config; use crate::tests::utils::commands::{ CLOSE_PANE_IN_PANE_MODE, ESC, MOVE_FOCUS_IN_PANE_MODE, PANE_MODE, QUIT, RESIZE_DOWN_IN_RESIZE_MODE, RESIZE_LEFT_IN_RESIZE_MODE, RESIZE_MODE, RESIZE_UP_IN_RESIZE_MODE, @@ -39,7 +40,11 @@ pub fn close_pane_with_another_pane_above_it() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -77,7 +82,11 @@ pub fn close_pane_with_another_pane_below_it() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -112,7 +121,11 @@ pub fn close_pane_with_another_pane_to_the_left() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -148,7 +161,11 @@ pub fn close_pane_with_another_pane_to_the_right() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -189,7 +206,11 @@ pub fn close_pane_with_multiple_panes_above_it() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -228,7 +249,11 @@ pub fn close_pane_with_multiple_panes_below_it() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -269,7 +294,11 @@ pub fn close_pane_with_multiple_panes_to_the_left() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -308,7 +337,11 @@ pub fn close_pane_with_multiple_panes_to_the_right() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -369,7 +402,11 @@ pub fn close_pane_with_multiple_panes_above_it_away_from_screen_edges() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -426,7 +463,11 @@ pub fn close_pane_with_multiple_panes_below_it_away_from_screen_edges() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -485,7 +526,11 @@ pub fn close_pane_with_multiple_panes_to_the_left_away_from_screen_edges() { &CLOSE_PANE_IN_PANE_MODE, &QUIT, ]); - start(Box::new(fake_input_output.clone()), CliArgs::default()); + start( + Box::new(fake_input_output.clone()), + CliArgs::default(), + Config::default(), + ); let output_frames = fake_input_output .stdout_writer @@ -544,7 +589,11 @@ pub fn |