From 04094ced84a95ebebedfc287533d110094a3ea53 Mon Sep 17 00:00:00 2001 From: Alexandre Lissy Date: Thu, 3 Apr 2025 16:10:43 +0000 Subject: [PATCH] Bug 1725134 - Handle XDG_CONFIG_HOME in crashreporter r=afranchuk Differential Revision: https://phabricator.services.mozilla.com/D122358 --- .../crashreporter/client/app/src/config.rs | 64 +++++++++++++++++-- .../crashreporter/client/app/src/std/env.rs | 22 +++++-- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/toolkit/crashreporter/client/app/src/config.rs b/toolkit/crashreporter/client/app/src/config.rs index d1103541751f..999b1d623326 100644 --- a/toolkit/crashreporter/client/app/src/config.rs +++ b/toolkit/crashreporter/client/app/src/config.rs @@ -17,6 +17,48 @@ const MINIDUMP_PRUNE_SAVE_COUNT: usize = 10; #[cfg(test)] pub mod test { pub const MINIDUMP_PRUNE_SAVE_COUNT: usize = super::MINIDUMP_PRUNE_SAVE_COUNT; + + cfg_if::cfg_if! { + if #[cfg(target_os = "linux")] { + use crate::std::{mock, env}; + + fn cfg_get_data_dir_root() -> crate::std::path::PathBuf { + let cfg = super::Config::new(); + cfg.get_data_dir_root("vendor").unwrap() + } + + #[test] + fn data_dir_root_xdg_default() { + mock::builder() + .set(env::MockHomeDir, "home_dir".into()) + .run(|| { + let path = cfg_get_data_dir_root(); + assert_eq!(path, crate::std::path::PathBuf::from("home_dir/.config/vendor")); + }); + } + + #[test] + fn data_dir_root_xdg_home() { + mock::builder() + .set(env::MockEnv("XDG_CONFIG_HOME".into()), "home_dir/xdg/config".into()) + .run(|| { + let path = cfg_get_data_dir_root(); + assert_eq!(path, crate::std::path::PathBuf::from("home_dir/xdg/config/vendor")); + }); + } + + #[test] + fn data_dir_root_legacy_force() { + mock::builder() + .set(env::MockHomeDir, "home_dir".into()) + .set(env::MockEnv("MOZ_LEGACY_HOME".into()), "1".into()) + .run(|| { + let path = cfg_get_data_dir_root(); + assert_eq!(path, crate::std::path::PathBuf::from("home_dir/.vendor")); + }); + } + } + } } mod buildid_section { @@ -428,6 +470,22 @@ impl Config { } } + #[cfg(target_os = "linux")] + fn get_data_dir_root(&self, vendor: &str) -> anyhow::Result { + // home_dir is deprecated due to incorrect behavior on windows, but we only use it on linux + #[allow(deprecated)] + let data_path = if std::env::var_os("MOZ_LEGACY_HOME").is_some() { + std::env::home_dir().map(|h| h.join(format!(".{}", vendor.to_lowercase()))) + } else { + std::env::var_os("XDG_CONFIG_HOME") + .map(PathBuf::from) + .or_else(|| std::env::home_dir().map(|home| home.join(".config"))) + .map(|h| h.join(format!("{}", vendor.to_lowercase()))) + } + .with_context(|| self.string("crashreporter-error-no-home-dir"))?; + Ok(data_path) + } + cfg_if::cfg_if! { if #[cfg(mock)] { fn get_data_dir(&self, vendor: &str, product: &str) -> anyhow::Result { @@ -439,11 +497,7 @@ impl Config { } } else if #[cfg(target_os = "linux")] { fn get_data_dir(&self, vendor: &str, product: &str) -> anyhow::Result { - // home_dir is deprecated due to incorrect behavior on windows, but we only use it on linux - #[allow(deprecated)] - let mut data_path = - std::env::home_dir().with_context(|| self.string("crashreporter-error-no-home-dir"))?; - data_path.push(format!(".{}", vendor.to_lowercase())); + let mut data_path = self.get_data_dir_root(vendor)?; data_path.push(product.to_lowercase()); data_path.push("Crash Reports"); Ok(data_path) diff --git a/toolkit/crashreporter/client/app/src/std/env.rs b/toolkit/crashreporter/client/app/src/std/env.rs index 77d9d4aecc58..b135a1b7db4d 100644 --- a/toolkit/crashreporter/client/app/src/std/env.rs +++ b/toolkit/crashreporter/client/app/src/std/env.rs @@ -10,6 +10,14 @@ mock_key! { pub struct MockCurrentExe => std::path::PathBuf } +mock_key! { + pub struct MockEnv(pub OsString) => String +} + +mock_key! { + pub struct MockHomeDir => super::path::PathBuf +} + pub struct ArgsOs { argv0: Option, } @@ -26,12 +34,14 @@ impl Iterator for ArgsOs { } } -pub fn var>(_key: K) -> Result { - unimplemented!("no var access in tests") +pub fn var>(key: K) -> Result { + MockEnv(key.as_ref().to_os_string()) + .try_get(|value| value.clone()) + .ok_or(VarError::NotPresent) } -pub fn var_os>(_key: K) -> Option { - unimplemented!("no var access in tests") +pub fn var_os>(key: K) -> Option { + MockEnv(key.as_ref().to_os_string()).try_get(|value| value.clone().into()) } pub fn args_os() -> ArgsOs { @@ -40,6 +50,10 @@ pub fn args_os() -> ArgsOs { }) } +pub fn home_dir() -> Option { + MockHomeDir.try_get(|p| p.clone()) +} + #[allow(unused)] pub fn current_exe() -> std::io::Result { Ok(MockCurrentExe.get(|r| r.clone().into()))