mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
tests: deduplicate locale availability checks into shared is_locale_available()
This commit is contained in:
committed by
Daniel Hofstetter
parent
1658fc8f3d
commit
ffa088d467
@@ -13,6 +13,8 @@ use regex::Regex;
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
use uucore::process::geteuid;
|
||||
use uutests::util::TestScenario;
|
||||
#[cfg(unix)]
|
||||
use uutests::util::is_locale_available;
|
||||
use uutests::{at_and_ucmd, new_ucmd, util_name};
|
||||
|
||||
#[test]
|
||||
@@ -1840,20 +1842,7 @@ fn test_date_parenthesis_vs_other_special_chars() {
|
||||
fn test_date_iranian_locale_solar_hijri_calendar() {
|
||||
// Test Iranian locale uses Solar Hijri calendar
|
||||
// Verify the Solar Hijri calendar is used in the Iranian locale
|
||||
use std::process::Command;
|
||||
|
||||
// Check if Iranian locale is available
|
||||
let locale_check = Command::new("locale")
|
||||
.env("LC_ALL", "fa_IR.UTF-8")
|
||||
.arg("charmap")
|
||||
.output();
|
||||
|
||||
let locale_available = match locale_check {
|
||||
Ok(output) => String::from_utf8_lossy(&output.stdout).trim() == "UTF-8",
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
if !locale_available {
|
||||
if !is_locale_available("fa_IR.UTF-8") {
|
||||
println!("Skipping Iranian locale test - fa_IR.UTF-8 locale not available");
|
||||
return;
|
||||
}
|
||||
@@ -1920,20 +1909,7 @@ fn test_date_iranian_locale_solar_hijri_calendar() {
|
||||
fn test_date_ethiopian_locale_calendar() {
|
||||
// Test Ethiopian locale uses Ethiopian calendar
|
||||
// Verify the Ethiopian calendar is used in the Ethiopian locale
|
||||
use std::process::Command;
|
||||
|
||||
// Check if Ethiopian locale is available
|
||||
let locale_check = Command::new("locale")
|
||||
.env("LC_ALL", "am_ET.UTF-8")
|
||||
.arg("charmap")
|
||||
.output();
|
||||
|
||||
let locale_available = match locale_check {
|
||||
Ok(output) => String::from_utf8_lossy(&output.stdout).trim() == "UTF-8",
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
if !locale_available {
|
||||
if !is_locale_available("am_ET.UTF-8") {
|
||||
println!("Skipping Ethiopian locale test - am_ET.UTF-8 locale not available");
|
||||
return;
|
||||
}
|
||||
@@ -2000,20 +1976,7 @@ fn test_date_ethiopian_locale_calendar() {
|
||||
fn test_date_thai_locale_solar_calendar() {
|
||||
// Test Thai locale uses Thai solar calendar
|
||||
// Verify the Thai solar calendar is used with the Thai locale
|
||||
use std::process::Command;
|
||||
|
||||
// Check if Thai locale is available
|
||||
let locale_check = Command::new("locale")
|
||||
.env("LC_ALL", "th_TH.UTF-8")
|
||||
.arg("charmap")
|
||||
.output();
|
||||
|
||||
let locale_available = match locale_check {
|
||||
Ok(output) => String::from_utf8_lossy(&output.stdout).trim() == "UTF-8",
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
if !locale_available {
|
||||
if !is_locale_available("th_TH.UTF-8") {
|
||||
println!("Skipping Thai locale test - th_TH.UTF-8 locale not available");
|
||||
return;
|
||||
}
|
||||
|
||||
+122
-46
@@ -3,7 +3,7 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// spell-checker:ignore (words) ints (linux) NOFILE dfgi
|
||||
// spell-checker:ignore (words) ints (linux) NOFILE dfgi abmon avril
|
||||
#![allow(clippy::cast_possible_wrap)]
|
||||
|
||||
use std::env;
|
||||
@@ -15,6 +15,8 @@ use std::time::Duration;
|
||||
use uutests::at_and_ucmd;
|
||||
use uutests::new_ucmd;
|
||||
use uutests::util::TestScenario;
|
||||
#[cfg(unix)]
|
||||
use uutests::util::is_locale_available;
|
||||
|
||||
fn test_helper(file_name: &str, possible_args: &[&str]) {
|
||||
for args in possible_args {
|
||||
@@ -603,44 +605,106 @@ fn test_month_default2() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_sort_french_locale() {
|
||||
// Test locale-aware month sorting (mirrors GNU sort-month.sh test).
|
||||
// Uses French UTF-8 locale where abbreviated months are:
|
||||
// spell-checker:disable-next-line
|
||||
// janv. févr. mars avril mai juin juil. août sept. oct. nov. déc.
|
||||
let result = new_ucmd!()
|
||||
.env("LC_ALL", "fr_FR.UTF-8")
|
||||
.arg("-M")
|
||||
// spell-checker:disable-next-line
|
||||
.pipe_in("mai\ndéc.\njanv.\njuin\nfévr.\nmars\navril\njuil.\naoût\nsept.\noct.\nnov.\n")
|
||||
.run();
|
||||
|
||||
// Skip if locale is not available on the system
|
||||
if result.succeeded() {
|
||||
assert_eq!(
|
||||
result.stdout_str(),
|
||||
"janv.\nfévr.\nmars\navril\nmai\njuin\njuil.\naoût\nsept.\noct.\nnov.\ndéc.\n",
|
||||
);
|
||||
/// Query the system for abbreviated month names via `locale abmon`.
|
||||
/// Returns a vector of 12 month abbreviations in order (Jan..Dec),
|
||||
/// or None if the command fails or returns unexpected output.
|
||||
#[cfg(any(target_vendor = "apple", target_os = "openbsd"))]
|
||||
fn get_system_abmon(locale: &str) -> Option<Vec<String>> {
|
||||
let output = Command::new("locale")
|
||||
.env("LC_ALL", locale)
|
||||
.arg("abmon")
|
||||
.output()
|
||||
.ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
let text = String::from_utf8(output.stdout).ok()?;
|
||||
let months: Vec<String> = text
|
||||
.trim()
|
||||
.split(';')
|
||||
.map(String::from)
|
||||
.filter(|m| !m.is_empty())
|
||||
.collect();
|
||||
if months.len() == 12 {
|
||||
Some(months)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_sort_hungarian_locale() {
|
||||
// Hungarian abbreviated months: jan febr márc ápr máj jún júl aug szept okt nov dec
|
||||
let result = new_ucmd!()
|
||||
.env("LC_ALL", "hu_HU.UTF-8")
|
||||
.arg("-M")
|
||||
// spell-checker:disable-next-line
|
||||
.pipe_in("máj\ndec\njan\njún\nfebr\nmárc\nápr\njúl\naug\nszept\nokt\nnov\n")
|
||||
.run();
|
||||
/// Build shuffled input and sorted expected output from month names.
|
||||
#[cfg(any(target_vendor = "apple", target_os = "openbsd"))]
|
||||
fn month_sort_input_expected(months: &[String]) -> (String, String) {
|
||||
// Shuffled order: May, Dec, Jan, Jun, Feb, Mar, Apr, Jul, Aug, Sep, Oct, Nov
|
||||
let shuffle_order = [4, 11, 0, 5, 1, 2, 3, 6, 7, 8, 9, 10];
|
||||
let input = shuffle_order
|
||||
.iter()
|
||||
.map(|&i| months[i].as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
+ "\n";
|
||||
let expected = months.join("\n") + "\n";
|
||||
(input, expected)
|
||||
}
|
||||
|
||||
if result.succeeded() {
|
||||
assert_eq!(
|
||||
result.stdout_str(),
|
||||
"jan\nfebr\nmárc\nápr\nmáj\njún\njúl\naug\nszept\nokt\nnov\ndec\n",
|
||||
);
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_month_sort_french_locale() {
|
||||
let locale = "fr_FR.UTF-8";
|
||||
if !is_locale_available(locale) {
|
||||
return;
|
||||
}
|
||||
// spell-checker:disable
|
||||
// On macOS/OpenBSD, abbreviated month names vary across OS versions (different CLDR data),
|
||||
// so we query the system dynamically. On other platforms, glibc values are stable.
|
||||
#[cfg(any(target_vendor = "apple", target_os = "openbsd"))]
|
||||
let (input, expected) = {
|
||||
let Some(months) = get_system_abmon(locale) else {
|
||||
return;
|
||||
};
|
||||
month_sort_input_expected(&months)
|
||||
};
|
||||
#[cfg(not(any(target_vendor = "apple", target_os = "openbsd")))]
|
||||
let (input, expected) = (
|
||||
"mai\ndéc.\njanv.\njuin\nfévr.\nmars\navril\njuil.\naoût\nsept.\noct.\nnov.\n".to_string(),
|
||||
"janv.\nfévr.\nmars\navril\nmai\njuin\njuil.\naoût\nsept.\noct.\nnov.\ndéc.\n".to_string(),
|
||||
);
|
||||
// spell-checker:enable
|
||||
new_ucmd!()
|
||||
.env("LC_ALL", locale)
|
||||
.arg("-M")
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_is(expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_month_sort_hungarian_locale() {
|
||||
let locale = "hu_HU.UTF-8";
|
||||
if !is_locale_available(locale) {
|
||||
return;
|
||||
}
|
||||
// spell-checker:disable
|
||||
#[cfg(any(target_vendor = "apple", target_os = "openbsd"))]
|
||||
let (input, expected) = {
|
||||
let Some(months) = get_system_abmon(locale) else {
|
||||
return;
|
||||
};
|
||||
month_sort_input_expected(&months)
|
||||
};
|
||||
#[cfg(not(any(target_vendor = "apple", target_os = "openbsd")))]
|
||||
let (input, expected) = (
|
||||
"máj\ndec\njan\njún\nfebr\nmárc\nápr\njúl\naug\nszept\nokt\nnov\n".to_string(),
|
||||
"jan\nfebr\nmárc\nápr\nmáj\njún\njúl\naug\nszept\nokt\nnov\ndec\n".to_string(),
|
||||
);
|
||||
// spell-checker:enable
|
||||
new_ucmd!()
|
||||
.env("LC_ALL", locale)
|
||||
.arg("-M")
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_is(expected);
|
||||
}
|
||||
|
||||
/// Test that embedded blanks in month names cause a non-match (GNU compat).
|
||||
@@ -698,20 +762,32 @@ fn test_month_sort_french_embedded_blanks() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_month_sort_japanese_locale() {
|
||||
// Japanese abbreviated months have leading spaces: " 1月" " 2月" ... "10月" "11月" "12月"
|
||||
let result = new_ucmd!()
|
||||
.env("LC_ALL", "ja_JP.UTF-8")
|
||||
.arg("-M")
|
||||
.pipe_in("5月\n12月\n1月\n6月\n2月\n3月\n4月\n7月\n8月\n9月\n10月\n11月\n")
|
||||
.run();
|
||||
|
||||
if result.succeeded() {
|
||||
assert_eq!(
|
||||
result.stdout_str(),
|
||||
"1月\n2月\n3月\n4月\n5月\n6月\n7月\n8月\n9月\n10月\n11月\n12月\n",
|
||||
);
|
||||
let locale = "ja_JP.UTF-8";
|
||||
if !is_locale_available(locale) {
|
||||
return;
|
||||
}
|
||||
// On macOS/OpenBSD, abbreviated month names may differ, so query dynamically.
|
||||
#[cfg(any(target_vendor = "apple", target_os = "openbsd"))]
|
||||
let (input, expected) = {
|
||||
let Some(months) = get_system_abmon(locale) else {
|
||||
return;
|
||||
};
|
||||
month_sort_input_expected(&months)
|
||||
};
|
||||
// Japanese abbreviated months are numeric (1月..12月) on glibc
|
||||
#[cfg(not(any(target_vendor = "apple", target_os = "openbsd")))]
|
||||
let (input, expected) = (
|
||||
"5月\n12月\n1月\n6月\n2月\n3月\n4月\n7月\n8月\n9月\n10月\n11月\n".to_string(),
|
||||
"1月\n2月\n3月\n4月\n5月\n6月\n7月\n8月\n9月\n10月\n11月\n12月\n".to_string(),
|
||||
);
|
||||
new_ucmd!()
|
||||
.env("LC_ALL", locale)
|
||||
.arg("-M")
|
||||
.pipe_in(input)
|
||||
.succeeds()
|
||||
.stdout_is(expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -100,6 +100,19 @@ pub fn is_ci() -> bool {
|
||||
env::var("CI").is_ok_and(|s| s.eq_ignore_ascii_case("true"))
|
||||
}
|
||||
|
||||
/// Check if a locale is available on the system by verifying that
|
||||
/// `locale charmap` returns `"UTF-8"` when `LC_ALL` is set to the given locale.
|
||||
#[cfg(unix)]
|
||||
pub fn is_locale_available(locale: &str) -> bool {
|
||||
use std::process::Command;
|
||||
Command::new("locale")
|
||||
.env("LC_ALL", locale)
|
||||
.arg("charmap")
|
||||
.output()
|
||||
.map(|o| String::from_utf8_lossy(&o.stdout).trim() == "UTF-8")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Read a test scenario fixture, returning its bytes
|
||||
fn read_scenario_fixture<S: AsRef<OsStr>>(tmpd: Option<&Rc<TempDir>>, file_rel_path: S) -> Vec<u8> {
|
||||
let tmpdir_path = tmpd.as_ref().unwrap().as_ref().path();
|
||||
|
||||
Reference in New Issue
Block a user