Skip to content

Commit 30f84e7

Browse files
committed
test(date): validate locale formats via runtime expansion
Replace literal format code checks with validation of expanded output. Fixes test failures on macOS 15.7.2 where nl_langinfo returns composite format codes instead of explicit ones. Fixes uutils#9654
1 parent 502f3b1 commit 30f84e7

2 files changed

Lines changed: 72 additions & 23 deletions

File tree

.vscode/cspell.dictionaries/jargon.wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ shortcodes
136136
siginfo
137137
sigusr
138138
strcasecmp
139+
strtime
139140
subcommand
140141
subexpression
141142
submodule

src/uu/date/src/locale.rs

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,24 @@ mod tests {
112112
cfg_langinfo! {
113113
use super::*;
114114

115+
/// Helper function to expand a format string with a known test date
116+
///
117+
/// Uses a fixed test date: Monday, January 15, 2024, 14:30:45 UTC
118+
/// This allows us to validate format strings by checking their expanded output
119+
/// rather than looking for literal format codes.
120+
fn expand_format_with_test_date(format: &str) -> String {
121+
use jiff::civil::date;
122+
use jiff::fmt::strtime;
123+
124+
// Create test timestamp: Monday, January 15, 2024, 14:30:45 UTC
125+
let Ok(test_date) = date(2024, 1, 15).at(14, 30, 45, 0).in_tz("UTC") else {
126+
return String::new();
127+
};
128+
129+
// Expand the format string with the test date
130+
strtime::format(format, &test_date).unwrap_or_default()
131+
}
132+
115133
#[test]
116134
fn test_locale_detection() {
117135
// Just verify the function doesn't panic
@@ -121,10 +139,31 @@ mod tests {
121139
#[test]
122140
fn test_default_format_contains_valid_codes() {
123141
let format = get_locale_default_format();
124-
assert!(format.contains("%a")); // abbreviated weekday
125-
assert!(format.contains("%b")); // abbreviated month
126-
assert!(format.contains("%Y") || format.contains("%y")); // year (4-digit or 2-digit)
127-
assert!(format.contains("%Z")); // timezone
142+
143+
let expanded = expand_format_with_test_date(format);
144+
145+
// Verify expanded output contains expected components
146+
// Test date: Monday, January 15, 2024, 14:30:45
147+
assert!(
148+
expanded.contains("Mon") || expanded.contains("Monday"),
149+
"Expanded format should contain weekday name, got: {expanded}"
150+
);
151+
152+
assert!(
153+
expanded.contains("Jan") || expanded.contains("January"),
154+
"Expanded format should contain month name, got: {expanded}"
155+
);
156+
157+
assert!(
158+
expanded.contains("2024") || expanded.contains("24"),
159+
"Expanded format should contain year, got: {expanded}"
160+
);
161+
162+
// Keep literal %Z check - this is enforced by ensure_timezone_in_format()
163+
assert!(
164+
format.contains("%Z"),
165+
"Format string must contain %Z timezone (enforced by ensure_timezone_in_format)"
166+
);
128167
}
129168

130169
#[test]
@@ -135,25 +174,34 @@ mod tests {
135174
// The format should not be empty
136175
assert!(!format.is_empty(), "Locale format should not be empty");
137176

138-
// Should contain date/time components
139-
let has_date_component = format.contains("%a")
140-
|| format.contains("%A")
141-
|| format.contains("%b")
142-
|| format.contains("%B")
143-
|| format.contains("%d")
144-
|| format.contains("%e");
145-
assert!(has_date_component, "Format should contain date components");
146-
147-
// Should contain time component (hour)
148-
let has_time_component = format.contains("%H")
149-
|| format.contains("%I")
150-
|| format.contains("%k")
151-
|| format.contains("%l")
152-
|| format.contains("%r")
153-
|| format.contains("%R")
154-
|| format.contains("%T")
155-
|| format.contains("%X");
156-
assert!(has_time_component, "Format should contain time components");
177+
let expanded = expand_format_with_test_date(format);
178+
179+
// Verify expanded output contains date components
180+
// Test date: Monday, January 15, 2024
181+
let has_date_component = expanded.contains("15") // day
182+
|| expanded.contains("Jan") // month name
183+
|| expanded.contains("January") // full month
184+
|| expanded.contains("Mon") // weekday
185+
|| expanded.contains("Monday"); // full weekday
186+
187+
assert!(
188+
has_date_component,
189+
"Expanded format should contain date components, got: {expanded}"
190+
);
191+
192+
// Verify expanded output contains time components
193+
// Test time: 14:30:45
194+
let has_time_component = expanded.contains("14") // 24-hour
195+
|| expanded.contains("02") // 12-hour
196+
|| expanded.contains("30") // minutes
197+
|| expanded.contains(':') // time separator
198+
|| expanded.contains("PM") // AM/PM indicator
199+
|| expanded.contains("pm");
200+
201+
assert!(
202+
has_time_component,
203+
"Expanded format should contain time components, got: {expanded}"
204+
);
157205
}
158206

159207
#[test]

0 commit comments

Comments
 (0)