mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-18 22:26:12 +01:00
Add some config unit tests
This commit is contained in:
parent
8a2a6a3185
commit
cea9d9048a
6 changed files with 260 additions and 19 deletions
45
Cargo.lock
generated
45
Cargo.lock
generated
|
@ -1199,6 +1199,16 @@ version = "0.6.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
|
@ -1449,6 +1459,29 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.4.1",
|
||||
"smallvec",
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
|
@ -1505,7 +1538,6 @@ dependencies = [
|
|||
"bitflags 2.4.2",
|
||||
"clap",
|
||||
"image",
|
||||
"lazy_static",
|
||||
"nix",
|
||||
"pinnacle-api-defs",
|
||||
"prost",
|
||||
|
@ -1515,6 +1547,8 @@ dependencies = [
|
|||
"smithay",
|
||||
"smithay-drm-extras",
|
||||
"sysinfo",
|
||||
"temp-env",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
|
@ -2084,6 +2118,15 @@ dependencies = [
|
|||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "temp-env"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.0"
|
||||
|
|
|
@ -26,7 +26,6 @@ anyhow = { version = "1.0.79", features = ["backtrace"] }
|
|||
clap = { version = "4.4.18", features = ["derive"] }
|
||||
xkbcommon = "0.7.0"
|
||||
xdg = "2.5.2"
|
||||
lazy_static = "1.4.0"
|
||||
sysinfo = "0.30.5"
|
||||
nix = { version = "0.27.1", features = ["user", "resource"] }
|
||||
prost = "0.12.3"
|
||||
|
@ -41,6 +40,10 @@ pinnacle-api-defs = { path = "./pinnacle-api-defs" }
|
|||
[build-dependencies]
|
||||
xdg = "2.5.2"
|
||||
|
||||
[dev-dependencies]
|
||||
temp-env = "0.3.6"
|
||||
tempfile = "3.10.0"
|
||||
|
||||
[features]
|
||||
default = ["egl", "winit", "udev", "xwayland"]
|
||||
egl = ["smithay/use_system_lib", "smithay/backend_egl"]
|
||||
|
|
209
src/config.rs
209
src/config.rs
|
@ -31,6 +31,7 @@ use sysinfo::ProcessRefreshKind;
|
|||
use tokio::{sync::mpsc::UnboundedSender, task::JoinHandle};
|
||||
use toml::Table;
|
||||
|
||||
use xdg::BaseDirectories;
|
||||
use xkbcommon::xkb::Keysym;
|
||||
|
||||
use crate::{
|
||||
|
@ -42,7 +43,7 @@ const DEFAULT_SOCKET_DIR: &str = "/tmp";
|
|||
|
||||
/// The metaconfig struct containing what to run, what envs to run it with, various keybinds, and
|
||||
/// the target socket directory.
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
pub struct Metaconfig {
|
||||
pub command: Vec<String>,
|
||||
pub envs: Option<Table>,
|
||||
|
@ -51,13 +52,13 @@ pub struct Metaconfig {
|
|||
pub socket_dir: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
pub struct Keybind {
|
||||
modifiers: Vec<Modifier>,
|
||||
key: Key,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone, Copy)]
|
||||
#[derive(serde::Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
enum Modifier {
|
||||
Shift,
|
||||
Ctrl,
|
||||
|
@ -84,7 +85,7 @@ impl From<Vec<self::Modifier>> for ModifierMask {
|
|||
}
|
||||
|
||||
// TODO: accept xkbcommon names instead
|
||||
#[derive(serde::Deserialize, Debug, Clone, Copy)]
|
||||
#[derive(serde::Deserialize, Debug, Clone, Copy, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[repr(u32)]
|
||||
pub enum Key {
|
||||
|
@ -207,12 +208,12 @@ fn parse_metaconfig(config_dir: &Path) -> anyhow::Result<Metaconfig> {
|
|||
|
||||
/// Get the config dir. This is $PINNACLE_CONFIG_DIR, then $XDG_CONFIG_HOME/pinnacle,
|
||||
/// then ~/.config/pinnacle.
|
||||
pub fn get_config_dir() -> PathBuf {
|
||||
pub fn get_config_dir(xdg_base_dirs: &BaseDirectories) -> PathBuf {
|
||||
let config_dir = std::env::var("PINNACLE_CONFIG_DIR")
|
||||
.ok()
|
||||
.and_then(|s| Some(PathBuf::from(shellexpand::full(&s).ok()?.to_string())));
|
||||
|
||||
config_dir.unwrap_or(crate::XDG_BASE_DIRS.get_config_home())
|
||||
config_dir.unwrap_or(xdg_base_dirs.get_config_home())
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -224,7 +225,7 @@ impl State {
|
|||
|
||||
tracing::info!("Starting config at {}", config_dir.display());
|
||||
|
||||
let default_lua_config_dir = crate::XDG_BASE_DIRS.get_data_file("default_config");
|
||||
let default_lua_config_dir = self.xdg_base_dirs.get_data_file("default_config");
|
||||
|
||||
let load_default_config = |state: &mut State, reason: &str| {
|
||||
tracing::error!(
|
||||
|
@ -281,7 +282,7 @@ impl State {
|
|||
socket_dir
|
||||
} else {
|
||||
// Otherwise, use $XDG_RUNTIME_DIR. If that doesn't exist, use /tmp.
|
||||
crate::XDG_BASE_DIRS
|
||||
self.xdg_base_dirs
|
||||
.get_runtime_directory()
|
||||
.cloned()
|
||||
.unwrap_or(PathBuf::from(DEFAULT_SOCKET_DIR))
|
||||
|
@ -427,7 +428,7 @@ impl State {
|
|||
|
||||
std::env::set_var(
|
||||
"PINNACLE_PROTO_DIR",
|
||||
crate::XDG_BASE_DIRS.get_data_file("protobuf"),
|
||||
self.xdg_base_dirs.get_data_file("protobuf"),
|
||||
);
|
||||
|
||||
let (grpc_sender, grpc_receiver) =
|
||||
|
@ -503,3 +504,193 @@ impl State {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::env::var;
|
||||
|
||||
#[test]
|
||||
fn config_dir_with_relative_env_works() -> anyhow::Result<()> {
|
||||
let relative_path = "api/rust/examples/default_config";
|
||||
|
||||
temp_env::with_var("PINNACLE_CONFIG_DIR", Some(relative_path), || {
|
||||
let xdg_base_dirs = BaseDirectories::with_prefix("pinnacle")?;
|
||||
|
||||
// Prepending the relative path with the current dir *shouldn't* be necessary, me thinks
|
||||
let expected = PathBuf::from(relative_path);
|
||||
|
||||
assert_eq!(get_config_dir(&xdg_base_dirs), expected);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_with_tilde_env_works() -> anyhow::Result<()> {
|
||||
temp_env::with_var("PINNACLE_CONFIG_DIR", Some("~/some/dir/somewhere/"), || {
|
||||
let xdg_base_dirs = BaseDirectories::with_prefix("pinnacle")?;
|
||||
let expected = PathBuf::from(var("HOME")?).join("some/dir/somewhere");
|
||||
|
||||
assert_eq!(get_config_dir(&xdg_base_dirs), expected);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_with_absolute_env_works() -> anyhow::Result<()> {
|
||||
let absolute_path = "/its/morbin/time";
|
||||
|
||||
temp_env::with_var("PINNACLE_CONFIG_DIR", Some(absolute_path), || {
|
||||
let xdg_base_dirs = BaseDirectories::with_prefix("pinnacle")?;
|
||||
let expected = PathBuf::from(absolute_path);
|
||||
|
||||
assert_eq!(get_config_dir(&xdg_base_dirs), expected);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_without_env_and_with_xdg_works() -> anyhow::Result<()> {
|
||||
let xdg_config_home = "/some/different/xdg/config/path";
|
||||
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("PINNACLE_CONFIG_DIR", None),
|
||||
("XDG_CONFIG_HOME", Some(xdg_config_home)),
|
||||
],
|
||||
|| {
|
||||
let xdg_base_dirs = BaseDirectories::with_prefix("pinnacle")?;
|
||||
let expected = PathBuf::from(xdg_config_home).join("pinnacle");
|
||||
|
||||
assert_eq!(get_config_dir(&xdg_base_dirs), expected);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_dir_without_env_and_without_xdg_works() -> anyhow::Result<()> {
|
||||
temp_env::with_vars(
|
||||
[
|
||||
("PINNACLE_CONFIG_DIR", None::<&str>),
|
||||
("XDG_CONFIG_HOME", None),
|
||||
],
|
||||
|| {
|
||||
let xdg_base_dirs = BaseDirectories::with_prefix("pinnacle")?;
|
||||
let expected = PathBuf::from(var("HOME")?).join(".config/pinnacle");
|
||||
|
||||
assert_eq!(get_config_dir(&xdg_base_dirs), expected);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn full_metaconfig_successfully_parses() -> anyhow::Result<()> {
|
||||
let metaconfig_text = r#"
|
||||
command = ["lua", "init.lua"]
|
||||
|
||||
reload_keybind = { modifiers = ["Ctrl", "Alt"], key = "r" }
|
||||
kill_keybind = { modifiers = ["Ctrl", "Alt", "Shift"], key = "escape" }
|
||||
|
||||
socket_dir = "/path/to/socket/dir"
|
||||
|
||||
[envs]
|
||||
MARCO = "polo"
|
||||
SUN = "chips"
|
||||
"#;
|
||||
|
||||
let metaconfig_dir = tempfile::tempdir()?;
|
||||
std::fs::write(
|
||||
metaconfig_dir.path().join("metaconfig.toml"),
|
||||
metaconfig_text,
|
||||
)?;
|
||||
|
||||
let expected_metaconfig = Metaconfig {
|
||||
command: vec!["lua".to_string(), "init.lua".to_string()],
|
||||
envs: Some(toml::Table::from_iter([
|
||||
("MARCO".to_string(), toml::Value::String("polo".to_string())),
|
||||
("SUN".to_string(), toml::Value::String("chips".to_string())),
|
||||
])),
|
||||
reload_keybind: Keybind {
|
||||
modifiers: vec![Modifier::Ctrl, Modifier::Alt],
|
||||
key: Key::R,
|
||||
},
|
||||
kill_keybind: Keybind {
|
||||
modifiers: vec![Modifier::Ctrl, Modifier::Alt, Modifier::Shift],
|
||||
key: Key::Escape,
|
||||
},
|
||||
socket_dir: Some("/path/to/socket/dir".to_string()),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
parse_metaconfig(metaconfig_dir.path())?,
|
||||
expected_metaconfig
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimal_metaconfig_successfully_parses() -> anyhow::Result<()> {
|
||||
let metaconfig_text = r#"
|
||||
command = ["lua", "init.lua"]
|
||||
|
||||
reload_keybind = { modifiers = ["Ctrl", "Alt"], key = "r" }
|
||||
kill_keybind = { modifiers = ["Ctrl", "Alt", "Shift"], key = "escape" }
|
||||
"#;
|
||||
|
||||
let metaconfig_dir = tempfile::tempdir()?;
|
||||
std::fs::write(
|
||||
metaconfig_dir.path().join("metaconfig.toml"),
|
||||
metaconfig_text,
|
||||
)?;
|
||||
|
||||
let expected_metaconfig = Metaconfig {
|
||||
command: vec!["lua".to_string(), "init.lua".to_string()],
|
||||
envs: None,
|
||||
reload_keybind: Keybind {
|
||||
modifiers: vec![Modifier::Ctrl, Modifier::Alt],
|
||||
key: Key::R,
|
||||
},
|
||||
kill_keybind: Keybind {
|
||||
modifiers: vec![Modifier::Ctrl, Modifier::Alt, Modifier::Shift],
|
||||
key: Key::Escape,
|
||||
},
|
||||
socket_dir: None,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
parse_metaconfig(metaconfig_dir.path())?,
|
||||
expected_metaconfig
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incorrect_metaconfig_does_not_parse() -> anyhow::Result<()> {
|
||||
let metaconfig_text = r#"
|
||||
command = "lua" # not an array
|
||||
|
||||
reload_keybind = { modifiers = ["Ctrl", "Alt"], key = "r" }
|
||||
# Missing `kill_keybind`
|
||||
# kill_keybind = { modifiers = ["Ctrl", "Alt", "Shift"], key = "escape" }
|
||||
"#;
|
||||
|
||||
let metaconfig_dir = tempfile::tempdir()?;
|
||||
std::fs::write(
|
||||
metaconfig_dir.path().join("metaconfig.toml"),
|
||||
metaconfig_text,
|
||||
)?;
|
||||
|
||||
assert!(parse_metaconfig(metaconfig_dir.path()).is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,7 +292,7 @@ impl State {
|
|||
self.shutdown();
|
||||
}
|
||||
Some(KeyAction::ReloadConfig) => {
|
||||
self.start_config(crate::config::get_config_dir())
|
||||
self.start_config(crate::config::get_config_dir(&self.xdg_base_dirs))
|
||||
.expect("failed to restart config");
|
||||
}
|
||||
None => (),
|
||||
|
|
|
@ -32,11 +32,6 @@ mod state;
|
|||
mod tag;
|
||||
mod window;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref XDG_BASE_DIRS: BaseDirectories =
|
||||
BaseDirectories::with_prefix("pinnacle").expect("couldn't create xdg BaseDirectories");
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
#[group(id = "backend", required = false, multiple = false)]
|
||||
struct Backends {
|
||||
|
@ -63,7 +58,7 @@ struct Args {
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let xdg_state_dir = XDG_BASE_DIRS.get_state_home();
|
||||
let xdg_state_dir = BaseDirectories::with_prefix("pinnacle")?.get_state_home();
|
||||
|
||||
let appender = tracing_appender::rolling::Builder::new()
|
||||
.rotation(Rotation::HOURLY)
|
||||
|
|
11
src/state.rs
11
src/state.rs
|
@ -4,6 +4,7 @@ use crate::{
|
|||
backend::Backend, config::Config, cursor::Cursor, focus::FocusState,
|
||||
grab::resize_grab::ResizeSurfaceState, window::WindowElement,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use smithay::{
|
||||
desktop::{PopupManager, Space},
|
||||
input::{keyboard::XkbConfig, pointer::CursorImageStatus, Seat, SeatState},
|
||||
|
@ -32,6 +33,7 @@ use smithay::{
|
|||
};
|
||||
use std::{cell::RefCell, sync::Arc, time::Duration};
|
||||
use sysinfo::{ProcessRefreshKind, RefreshKind};
|
||||
use xdg::BaseDirectories;
|
||||
|
||||
use crate::input::InputState;
|
||||
|
||||
|
@ -91,6 +93,8 @@ pub struct State {
|
|||
|
||||
// Currently only used to keep track of if the server has started
|
||||
pub grpc_server_join_handle: Option<tokio::task::JoinHandle<()>>,
|
||||
|
||||
pub xdg_base_dirs: BaseDirectories,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -150,7 +154,9 @@ impl State {
|
|||
)?;
|
||||
|
||||
loop_handle.insert_idle(|state| {
|
||||
if let Err(err) = state.start_config(crate::config::get_config_dir()) {
|
||||
if let Err(err) =
|
||||
state.start_config(crate::config::get_config_dir(&state.xdg_base_dirs))
|
||||
{
|
||||
panic!("failed to start config: {err}");
|
||||
}
|
||||
});
|
||||
|
@ -264,6 +270,9 @@ impl State {
|
|||
),
|
||||
|
||||
grpc_server_join_handle: None,
|
||||
|
||||
xdg_base_dirs: BaseDirectories::with_prefix("pinnacle")
|
||||
.context("couldn't create xdg BaseDirectories")?,
|
||||
};
|
||||
|
||||
Ok(state)
|
||||
|
|
Loading…
Reference in a new issue