Skip to content

Commit 8aa4f8c

Browse files
committed
uucore/human: fix tests to use uncached helpers for locale testing
Previously, tests in human.rs were failing on Ubuntu because OnceLock caching prevented tests from seeing locale changes. This commit adds uncached test-only helper functions: - get_thousands_separator_uncached(): Gets separator without caching - format_with_thousands_separator_uncached(): Formats without caching These helpers allow tests to properly verify locale-specific behavior by bypassing the OnceLock cache that caused test failures. Tests affected: - test_format_with_thousands_separator_locale - test_get_thousands_separator
1 parent 13225ab commit 8aa4f8c

1 file changed

Lines changed: 106 additions & 11 deletions

File tree

  • src/uucore/src/lib/features/format

src/uucore/src/lib/features/format/human.rs

Lines changed: 106 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,59 @@ fn get_thousands_separator() -> char {
143143
})
144144
}
145145

146+
/// Test-only helper: Get thousands separator without caching
147+
/// This allows tests to change locale environment variables and see the effects
148+
#[cfg(test)]
149+
fn get_thousands_separator_uncached() -> char {
150+
#[cfg(feature = "i18n-decimal")]
151+
{
152+
use crate::i18n::get_numeric_locale;
153+
let (locale, _encoding) = get_numeric_locale();
154+
155+
// C and POSIX locales have no thousands separator
156+
// The default locale from i18n is en-US-posix for C/POSIX
157+
if locale.to_string().contains("posix") {
158+
return '\0';
159+
}
160+
161+
get_grouping_separator_from_icu(locale.clone())
162+
}
163+
164+
#[cfg(not(feature = "i18n-decimal"))]
165+
{
166+
// Fallback implementation when i18n-decimal feature is disabled
167+
// Try to read LC_NUMERIC or LANG environment variable
168+
if let Ok(locale) = std::env::var("LC_NUMERIC")
169+
.or_else(|_| std::env::var("LC_ALL"))
170+
.or_else(|_| std::env::var("LANG"))
171+
{
172+
// C and POSIX locales have no thousands separator
173+
if locale == "C" || locale == "POSIX" || locale.starts_with("C.") {
174+
return '\0';
175+
}
176+
177+
// Simple heuristic: European locales use period, others use comma
178+
// This covers common cases like de_DE, fr_FR, it_IT, es_ES, nl_NL, etc.
179+
if locale.starts_with("de_")
180+
|| locale.starts_with("fr_")
181+
|| locale.starts_with("it_")
182+
|| locale.starts_with("es_")
183+
|| locale.starts_with("nl_")
184+
|| locale.starts_with("pt_")
185+
|| locale.starts_with("da_")
186+
|| locale.starts_with("sv_")
187+
|| locale.starts_with("no_")
188+
|| locale.starts_with("fi_")
189+
{
190+
return '.';
191+
}
192+
}
193+
194+
// Default to comma (en_US style)
195+
','
196+
}
197+
}
198+
146199
/// Format a number with thousands separators based on LC_NUMERIC locale.
147200
///
148201
/// This function reads the LC_NUMERIC environment variable to determine
@@ -195,6 +248,39 @@ pub fn format_with_thousands_separator(number: u64) -> String {
195248
result
196249
}
197250

251+
/// Test-only helper: Format number with uncached separator detection
252+
/// This allows tests to change locale environment variables and see the effects
253+
#[cfg(test)]
254+
fn format_with_thousands_separator_uncached(number: u64) -> String {
255+
const GROUPING_SIZE: usize = 3;
256+
257+
let separator = get_thousands_separator_uncached();
258+
259+
// C/POSIX locale has no thousands separator
260+
if separator == '\0' {
261+
return number.to_string();
262+
}
263+
264+
let num_str = number.to_string();
265+
let len = num_str.len();
266+
267+
// Numbers less than 1000 don't need separators
268+
if len <= GROUPING_SIZE {
269+
return num_str;
270+
}
271+
272+
let mut result = String::with_capacity(len + (len - 1) / GROUPING_SIZE);
273+
274+
for (i, ch) in num_str.chars().enumerate() {
275+
if i > 0 && (len - i) % GROUPING_SIZE == 0 {
276+
result.push(separator);
277+
}
278+
result.push(ch);
279+
}
280+
281+
result
282+
}
283+
198284
#[cfg(test)]
199285
mod tests {
200286
use super::*;
@@ -280,23 +366,32 @@ mod tests {
280366

281367
// Test with German locale (uses period as separator)
282368
std::env::set_var("LC_NUMERIC", "de_DE.UTF-8");
283-
assert_eq!(format_with_thousands_separator(1234567), "1.234.567");
369+
assert_eq!(
370+
format_with_thousands_separator_uncached(1234567),
371+
"1.234.567"
372+
);
284373

285374
// Test with French locale (uses period as separator)
286375
std::env::set_var("LC_NUMERIC", "fr_FR.UTF-8");
287-
assert_eq!(format_with_thousands_separator(1234567), "1.234.567");
376+
assert_eq!(
377+
format_with_thousands_separator_uncached(1234567),
378+
"1.234.567"
379+
);
288380

289381
// Test with US locale (uses comma as separator)
290382
std::env::set_var("LC_NUMERIC", "en_US.UTF-8");
291-
assert_eq!(format_with_thousands_separator(1234567), "1,234,567");
383+
assert_eq!(
384+
format_with_thousands_separator_uncached(1234567),
385+
"1,234,567"
386+
);
292387

293388
// Test with C locale (no separator)
294389
std::env::set_var("LC_NUMERIC", "C");
295-
assert_eq!(format_with_thousands_separator(1234567), "1234567");
390+
assert_eq!(format_with_thousands_separator_uncached(1234567), "1234567");
296391

297392
// Test with POSIX locale (no separator)
298393
std::env::set_var("LC_NUMERIC", "POSIX");
299-
assert_eq!(format_with_thousands_separator(1234567), "1234567");
394+
assert_eq!(format_with_thousands_separator_uncached(1234567), "1234567");
300395

301396
// Restore original locale variables
302397
std::env::remove_var("LC_NUMERIC");
@@ -325,26 +420,26 @@ mod tests {
325420
std::env::remove_var("LC_NUMERIC");
326421
std::env::remove_var("LC_ALL");
327422
std::env::remove_var("LANG");
328-
assert_eq!(get_thousands_separator(), ',');
423+
assert_eq!(get_thousands_separator_uncached(), ',');
329424

330425
// Test C locale
331426
std::env::set_var("LC_NUMERIC", "C");
332-
assert_eq!(get_thousands_separator(), '\0');
427+
assert_eq!(get_thousands_separator_uncached(), '\0');
333428

334429
// Test POSIX locale
335430
std::env::set_var("LC_NUMERIC", "POSIX");
336-
assert_eq!(get_thousands_separator(), '\0');
431+
assert_eq!(get_thousands_separator_uncached(), '\0');
337432

338433
// Test European locales
339434
std::env::set_var("LC_NUMERIC", "de_DE.UTF-8");
340-
assert_eq!(get_thousands_separator(), '.');
435+
assert_eq!(get_thousands_separator_uncached(), '.');
341436

342437
std::env::set_var("LC_NUMERIC", "fr_FR.UTF-8");
343-
assert_eq!(get_thousands_separator(), '.');
438+
assert_eq!(get_thousands_separator_uncached(), '.');
344439

345440
// Test US locale
346441
std::env::set_var("LC_NUMERIC", "en_US.UTF-8");
347-
assert_eq!(get_thousands_separator(), ',');
442+
assert_eq!(get_thousands_separator_uncached(), ',');
348443

349444
// Restore original locale
350445
std::env::remove_var("LC_NUMERIC");

0 commit comments

Comments
 (0)