diff --git a/src/uu/date/src/locale.rs b/src/uu/date/src/locale.rs index 0dea975f9..c190c1a0d 100644 --- a/src/uu/date/src/locale.rs +++ b/src/uu/date/src/locale.rs @@ -29,12 +29,23 @@ cfg_langinfo! { use std::ffi::CStr; use std::sync::OnceLock; use nix::libc; + + #[cfg(test)] + use std::sync::Mutex; } cfg_langinfo! { /// Cached locale date/time format string static DEFAULT_FORMAT_CACHE: OnceLock<&'static str> = OnceLock::new(); + /// Mutex to serialize setlocale() calls during tests. + /// + /// setlocale() is process-global, so parallel tests that call it can + /// interfere with each other. This mutex ensures only one test accesses + /// locale functions at a time. + #[cfg(test)] + static LOCALE_MUTEX: Mutex<()> = Mutex::new(()); + /// Returns the default date format string for the current locale. /// /// The format respects locale preferences for time display (12-hour vs 24-hour), @@ -55,6 +66,11 @@ cfg_langinfo! { /// Retrieves the date/time format string from the system locale fn get_locale_format_string() -> Option { + // In tests, acquire mutex to prevent race conditions with setlocale() + // which is process-global and not thread-safe + #[cfg(test)] + let _lock = LOCALE_MUTEX.lock().unwrap(); + unsafe { // Set locale from environment variables libc::setlocale(libc::LC_TIME, c"".as_ptr()); @@ -158,11 +174,24 @@ mod tests { #[test] fn test_c_locale_format() { - // Save original locale + // Acquire mutex to prevent interference with other tests + let _lock = LOCALE_MUTEX.lock().unwrap(); + + // Save original locale (both environment and process locale) let original_lc_all = std::env::var("LC_ALL").ok(); let original_lc_time = std::env::var("LC_TIME").ok(); let original_lang = std::env::var("LANG").ok(); + // Save current process locale + let original_process_locale = unsafe { + let ptr = libc::setlocale(libc::LC_TIME, std::ptr::null()); + if ptr.is_null() { + None + } else { + CStr::from_ptr(ptr).to_str().ok().map(|s| s.to_string()) + } + }; + unsafe { // Set C locale std::env::set_var("LC_ALL", "C"); @@ -177,7 +206,7 @@ mod tests { if d_t_fmt_ptr.is_null() { None } else { - std::ffi::CStr::from_ptr(d_t_fmt_ptr).to_str().ok() + CStr::from_ptr(d_t_fmt_ptr).to_str().ok() } }; @@ -190,7 +219,7 @@ mod tests { assert!(uses_24_hour, "C locale should use 24-hour format, got: {locale_format}"); } - // Restore original locale + // Restore original environment variables unsafe { if let Some(val) = original_lc_all { std::env::set_var("LC_ALL", val); @@ -208,6 +237,17 @@ mod tests { std::env::remove_var("LANG"); } } + + // Restore original process locale + unsafe { + if let Some(locale) = original_process_locale { + let c_locale = std::ffi::CString::new(locale).unwrap(); + libc::setlocale(libc::LC_TIME, c_locale.as_ptr()); + } else { + // Restore from environment + libc::setlocale(libc::LC_TIME, c"".as_ptr()); + } + } } #[test] diff --git a/src/uucore/src/lib/features/backup_control.rs b/src/uucore/src/lib/features/backup_control.rs index f1ff8f816..dd5f6b610 100644 --- a/src/uucore/src/lib/features/backup_control.rs +++ b/src/uucore/src/lib/features/backup_control.rs @@ -683,6 +683,7 @@ mod tests { let result = determine_backup_mode(&matches).unwrap(); assert_eq!(result, BackupMode::Numbered); + unsafe { env::remove_var(ENV_VERSION_CONTROL) }; } #[test]