From 7c41d800bab807d8773ece5a5c84b723d349fd4c Mon Sep 17 00:00:00 2001 From: Vinny Meller Date: Sun, 2 Jun 2024 01:33:22 -0400 Subject: [PATCH] Add config tests Adds some sanity check tests to the config processing --- Cargo.lock | 139 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 + src/config.rs | 102 +++++++++++++++++++++++++++++----- src/layout.rs | 2 +- src/workspace.rs | 14 ++--- 5 files changed, 239 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66e4ee9..456640f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,6 +409,83 @@ dependencies = [ "syn", ] +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -713,6 +790,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "proc-macro2" version = "1.0.84" @@ -830,6 +919,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "scc" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ad2bbb0ae5100a07b7a6f2ed7ab5fd0045551a4c507989b7a620046ea3efdc" +dependencies = [ + "sdd", +] + [[package]] name = "schemars" version = "0.8.21" @@ -860,6 +958,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sdd" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84345e4c9bd703274a082fb80caaa99b7612be48dfaa1dd9266577ec412309d" + [[package]] name = "serde" version = "1.0.203" @@ -902,6 +1006,31 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" +dependencies = [ + "futures", + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha2" version = "0.10.8" @@ -952,6 +1081,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -1060,6 +1198,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "serial_test", "shellexpand", "xdg", ] diff --git a/Cargo.toml b/Cargo.toml index 5ee6d58..bb4bcf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,6 @@ serde = { version = "1", features = ["derive"] } serde_json = "1.0.117" shellexpand = "3" xdg = "2.4" + +[dev-dependencies] +serial_test = "*" diff --git a/src/config.rs b/src/config.rs index 8b84c09..faeb242 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,3 @@ -// TODO: figure out how to handle turning the config file into the final structs used -// throughout the program. this shit is a mess!! - use crate::layout::LayoutDefinition; use crate::workspace::{ HasAnyFileCondition, MissingAllFilesCondition, MissingAnyFileCondition, NullCondition, @@ -170,7 +167,7 @@ impl RawTwmGlobal { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct TwmGlobal { pub search_paths: Vec, pub exclude_path_components: Vec, @@ -273,22 +270,23 @@ impl FromStr for RawTwmGlobal { } impl TwmGlobal { - pub fn load() -> Result { + fn get_config_path() -> Result { let config_file_name = format!("{}.yaml", clap::crate_name!()); - let config_path = match std::env::var_os("TWM_CONFIG_FILE") { + match std::env::var_os("TWM_CONFIG_FILE") { // if TWM_CONFIG_FILE is not set, search xdg dirs for config file as normal None => { let xdg_dirs = xdg::BaseDirectories::with_prefix(clap::crate_name!()) .with_context(|| "Failed to load XDG dirs.")?; - xdg_dirs.find_config_file(config_file_name) + Ok(xdg_dirs.get_config_file(config_file_name)) } // if TWM_CONFIG_FILE is set, read from there no questions asked - Some(config_file_path) => Some(PathBuf::from(config_file_path)), - }; - let raw_config = match config_path { - Some(path) => RawTwmGlobal::try_from(&path), - None => RawTwmGlobal::from_str(""), - }?; + Some(config_file_path) => Ok(PathBuf::from(config_file_path)), + } + } + + pub fn load() -> Result { + let config_path = TwmGlobal::get_config_path()?; + let raw_config = RawTwmGlobal::try_from(&config_path)?; let config = TwmGlobal::try_from(raw_config) .with_context(|| "Failed to validate configuration settings.")?; Ok(config) @@ -334,10 +332,88 @@ impl TwmLayout { mod tests { use super::*; + use serial_test::serial; #[test] fn test_empty_config_is_valid() { let raw_config = RawTwmGlobal::from_str("").unwrap(); let _ = TwmGlobal::try_from(raw_config).unwrap(); } + + #[test] + fn test_invalid_config_key_is_error() { + let raw_config = RawTwmGlobal::from_str("foo: bar"); + assert!(raw_config.is_err()); + } + + /// Just check the default values get set correctly to make sure I don't unintentionally change the default behavior somehow + #[test] + fn test_config_default_values() { + let raw_config = RawTwmGlobal::from_str("").unwrap(); + let config = TwmGlobal::try_from(raw_config).unwrap(); + assert_eq!( + config, + TwmGlobal { + search_paths: vec![shellexpand::tilde("~").to_string()], + exclude_path_components: vec![], + workspace_definitions: vec![WorkspaceDefinition { + name: String::from("default"), + conditions: vec![HasAnyFileCondition { + files: vec![".git".to_string()], + } + .into()], + default_layout: None, + }], + session_name_path_components: 1, + layouts: vec![], + max_search_depth: 3, + } + ) + } + + /// Make noise if we change which env var overrides the config file path or it breaks + #[test] + #[serial] + fn test_get_config_path_env_var_override() { + let orig_twm = std::env::var_os("TWM_CONFIG_FILE"); + let config_file = "/tmp/twm.yaml"; + std::env::set_var("TWM_CONFIG_FILE", config_file); + + let config_path = TwmGlobal::get_config_path().unwrap(); + assert_eq!(config_path, PathBuf::from(config_file)); + + if let Some(twm) = orig_twm { + std::env::set_var("TWM_CONFIG_FILE", twm); + } else { + std::env::remove_var("TWM_CONFIG_FILE"); + } + } + + #[test] + #[serial] + fn test_get_config_path_xdg_default() { + let orig_twm = std::env::var_os("TWM_CONFIG_FILE"); + let orig_home = std::env::var_os("HOME"); + let orig_xdg = std::env::var_os("XDG_CONFIG_HOME"); + std::env::remove_var("TWM_CONFIG_FILE"); + std::env::set_var("HOME", "/tmp"); + std::env::set_var("XDG_CONFIG_HOME", "/tmp/.config"); + + let config_path = TwmGlobal::get_config_path().unwrap(); + assert_eq!(config_path, PathBuf::from("/tmp/.config/twm/twm.yaml")); + + if let Some(twm) = orig_twm { + std::env::set_var("TWM_CONFIG_FILE", twm); + } + if let Some(home) = orig_home { + std::env::set_var("HOME", home); + } else { + std::env::remove_var("HOME"); + } + if let Some(xdg) = orig_xdg { + std::env::set_var("XDG_CONFIG_HOME", xdg); + } else { + std::env::remove_var("XDG_CONFIG_HOME"); + } + } } diff --git a/src/layout.rs b/src/layout.rs index 12349ae..ea476c2 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -1,7 +1,7 @@ use schemars::JsonSchema; use serde::Deserialize; -#[derive(Deserialize, Debug, Clone, JsonSchema)] +#[derive(Deserialize, Debug, Clone, JsonSchema, PartialEq, Eq)] #[serde(deny_unknown_fields)] pub struct LayoutDefinition { /// Name of the layout. diff --git a/src/workspace.rs b/src/workspace.rs index c7ffb24..c75ecdb 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -1,7 +1,7 @@ use enum_dispatch::enum_dispatch; use std::path::Path; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct WorkspaceDefinition { pub name: String, pub conditions: Vec, @@ -9,7 +9,7 @@ pub struct WorkspaceDefinition { } #[enum_dispatch] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum WorkspaceConditionEnum { HasAnyFileCondition, HasAllFilesCondition, @@ -23,7 +23,7 @@ pub trait WorkspaceCondition { fn meets_condition(&self, path: &Path) -> bool; } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HasAnyFileCondition { pub files: Vec, } @@ -39,7 +39,7 @@ impl WorkspaceCondition for HasAnyFileCondition { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HasAllFilesCondition { pub files: Vec, } @@ -55,7 +55,7 @@ impl WorkspaceCondition for HasAllFilesCondition { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MissingAnyFileCondition { pub files: Vec, } @@ -71,7 +71,7 @@ impl WorkspaceCondition for MissingAnyFileCondition { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MissingAllFilesCondition { pub files: Vec, } @@ -89,7 +89,7 @@ impl WorkspaceCondition for MissingAllFilesCondition { /// A condition that always returns true, used as a default condition if no others /// are specified. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct NullCondition {} impl WorkspaceCondition for NullCondition {