use std::sync::Mutex;
pub static ENV_LOCK: Mutex<()> = Mutex::new(());
pub fn with_fake_xdg<R>(f: impl FnOnce() -> R) -> R {
let _guard = ENV_LOCK.lock().unwrap_or_else(|poisoned| {
poisoned.into_inner()
});
with_fake_xdg_unlocked(f)
}
fn with_fake_xdg_unlocked<R>(f: impl FnOnce() -> R) -> R {
let previous_xdg_data_home = std::env::var_os("XDG_DATA_HOME");
let base = std::env::temp_dir().join(format!("nmrs-test-{}", uuid::Uuid::new_v4()));
std::fs::create_dir_all(&base).expect("failed to create temp directory for test");
unsafe {
std::env::set_var("XDG_DATA_HOME", &base);
}
struct Cleanup {
base: std::path::PathBuf,
previous_xdg_data_home: Option<std::ffi::OsString>,
}
impl Drop for Cleanup {
fn drop(&mut self) {
unsafe {
match &self.previous_xdg_data_home {
Some(value) => std::env::set_var("XDG_DATA_HOME", value),
None => std::env::remove_var("XDG_DATA_HOME"),
}
}
let _ = std::fs::remove_dir_all(&self.base);
}
}
let _cleanup = Cleanup {
base,
previous_xdg_data_home,
};
f()
}
#[cfg(test)]
mod tests {
use super::*;
struct RestoreXdg(Option<std::ffi::OsString>);
impl Drop for RestoreXdg {
fn drop(&mut self) {
unsafe {
match &self.0 {
Some(value) => std::env::set_var("XDG_DATA_HOME", value),
None => std::env::remove_var("XDG_DATA_HOME"),
}
}
}
}
#[test]
fn fake_xdg_restores_existing_value() {
let _lock = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let _restore = RestoreXdg(std::env::var_os("XDG_DATA_HOME"));
let original = std::env::temp_dir().join("nmrs-original-xdg");
unsafe { std::env::set_var("XDG_DATA_HOME", &original) };
with_fake_xdg_unlocked(|| {
assert_ne!(
std::env::var_os("XDG_DATA_HOME").as_deref(),
Some(original.as_os_str())
);
});
assert_eq!(
std::env::var_os("XDG_DATA_HOME").as_deref(),
Some(original.as_os_str())
);
}
#[test]
fn fake_xdg_removes_value_when_initially_unset() {
let _lock = ENV_LOCK
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
let _restore = RestoreXdg(std::env::var_os("XDG_DATA_HOME"));
unsafe { std::env::remove_var("XDG_DATA_HOME") };
with_fake_xdg_unlocked(|| {
assert!(std::env::var_os("XDG_DATA_HOME").is_some());
});
assert!(std::env::var_os("XDG_DATA_HOME").is_none());
}
}