You've already forked parse_datetime
mirror of
https://github.com/uutils/parse_datetime.git
synced 2026-06-10 16:13:15 -07:00
improve the code coverage
This commit is contained in:
@@ -333,3 +333,146 @@ impl TryFrom<Vec<Item>> for DateTimeBuilder {
|
||||
Ok(builder)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// Helper functions to create test items by parsing
|
||||
fn timestamp() -> epoch::Timestamp {
|
||||
let mut input = "@1234567890";
|
||||
epoch::parse(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn date() -> date::Date {
|
||||
let mut input = "2023-06-15";
|
||||
date::parse(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn time() -> time::Time {
|
||||
let mut input = "12:30:00";
|
||||
time::parse(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn time_with_offset() -> time::Time {
|
||||
let mut input = "12:30:00+05:00";
|
||||
time::parse(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn weekday() -> weekday::Weekday {
|
||||
let mut input = "monday";
|
||||
weekday::parse(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn offset() -> offset::Offset {
|
||||
let mut input = "+05:00";
|
||||
offset::timezone_offset(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn relative() -> relative::Relative {
|
||||
let mut input = "1 day";
|
||||
relative::parse(&mut input).unwrap()
|
||||
}
|
||||
|
||||
fn timezone() -> jiff::tz::TimeZone {
|
||||
jiff::tz::TimeZone::UTC
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicate_items_errors() {
|
||||
let test_cases = vec![
|
||||
(
|
||||
vec![Item::TimeZone(timezone()), Item::TimeZone(timezone())],
|
||||
"timezone rule cannot appear more than once",
|
||||
),
|
||||
(
|
||||
vec![Item::Timestamp(timestamp()), Item::Timestamp(timestamp())],
|
||||
"timestamp cannot appear more than once",
|
||||
),
|
||||
(
|
||||
vec![Item::Date(date()), Item::Date(date())],
|
||||
"date cannot appear more than once",
|
||||
),
|
||||
(
|
||||
vec![Item::Time(time()), Item::Time(time())],
|
||||
"time cannot appear more than once",
|
||||
),
|
||||
(
|
||||
vec![Item::Weekday(weekday()), Item::Weekday(weekday())],
|
||||
"weekday cannot appear more than once",
|
||||
),
|
||||
(
|
||||
vec![Item::Offset(offset()), Item::Offset(offset())],
|
||||
"time offset cannot appear more than once",
|
||||
),
|
||||
];
|
||||
|
||||
for (items, expected_err) in test_cases {
|
||||
let result = DateTimeBuilder::try_from(items);
|
||||
assert_eq!(result.unwrap_err(), expected_err);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_cannot_be_combined_with_other_items() {
|
||||
let test_cases = vec![
|
||||
vec![Item::Date(date()), Item::Timestamp(timestamp())],
|
||||
vec![Item::Time(time()), Item::Timestamp(timestamp())],
|
||||
vec![Item::Weekday(weekday()), Item::Timestamp(timestamp())],
|
||||
vec![Item::Offset(offset()), Item::Timestamp(timestamp())],
|
||||
vec![Item::Relative(relative()), Item::Timestamp(timestamp())],
|
||||
vec![Item::Timestamp(timestamp()), Item::Date(date())],
|
||||
vec![Item::Timestamp(timestamp()), Item::Time(time())],
|
||||
vec![Item::Timestamp(timestamp()), Item::Weekday(weekday())],
|
||||
vec![Item::Timestamp(timestamp()), Item::Relative(relative())],
|
||||
vec![Item::Timestamp(timestamp()), Item::Offset(offset())],
|
||||
vec![Item::Timestamp(timestamp()), Item::Pure("2023".to_string())],
|
||||
];
|
||||
|
||||
for items in test_cases {
|
||||
let result = DateTimeBuilder::try_from(items);
|
||||
assert_eq!(
|
||||
result.unwrap_err(),
|
||||
"timestamp cannot be combined with other date/time items"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_time_offset_conflicts() {
|
||||
// Time with offset followed by separate Offset item
|
||||
let items1 = vec![Item::Time(time_with_offset()), Item::Offset(offset())];
|
||||
assert_eq!(
|
||||
DateTimeBuilder::try_from(items1).unwrap_err(),
|
||||
"time offset cannot appear more than once"
|
||||
);
|
||||
|
||||
// Offset item followed by Time with offset
|
||||
let items2 = vec![Item::Offset(offset()), Item::Time(time_with_offset())];
|
||||
assert_eq!(
|
||||
DateTimeBuilder::try_from(items2).unwrap_err(),
|
||||
"time offset and timezone are mutually exclusive"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_combination_date_time() {
|
||||
let items = vec![Item::Date(date()), Item::Time(time())];
|
||||
let result = DateTimeBuilder::try_from(items);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_combination_date_weekday() {
|
||||
let items = vec![Item::Date(date()), Item::Weekday(weekday())];
|
||||
let result = DateTimeBuilder::try_from(items);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_timestamp_alone() {
|
||||
let items = vec![Item::Timestamp(timestamp())];
|
||||
let result = DateTimeBuilder::try_from(items);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,3 +142,64 @@ pub(super) fn ctx_err(reason: &'static str) -> ContextError {
|
||||
err.push(StrContext::Expected(StrContextValue::Description(reason)));
|
||||
err
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_dec_int() {
|
||||
for (input, expected) in [
|
||||
("123", 123), // positive without sign
|
||||
("+123", 123), // positive with '+' sign
|
||||
("-123", -123), // negative with '-' sign
|
||||
("0", 0), // zero
|
||||
("+0", 0), // zero with '+' sign
|
||||
("-0", 0), // zero with '-' sign (parses as 0)
|
||||
("012", 12), // zero-prefixed (the main reason for this function)
|
||||
("+012", 12), // zero-prefixed with '+' sign
|
||||
("-012", -12), // zero-prefixed with '-' sign
|
||||
("00123", 123), // multiple leading zeros
|
||||
("2147483647", 2147483647), // i32::MAX
|
||||
("-2147483648", -2147483648), // i32::MIN
|
||||
] {
|
||||
let mut s = input;
|
||||
assert_eq!(
|
||||
dec_int::<ContextError>(&mut s).unwrap(),
|
||||
expected,
|
||||
"{input}"
|
||||
);
|
||||
}
|
||||
|
||||
for input in [
|
||||
"", // empty string
|
||||
"+", // sign without digits
|
||||
"-", // sign without digits
|
||||
"abc", // non-numeric
|
||||
"12a", // starts with digits but has non-digit after (but should parse "12" successfully)
|
||||
] {
|
||||
let mut s = input;
|
||||
let result = dec_int::<ContextError>(&mut s);
|
||||
// Note: "12a" will actually succeed and parse "12", leaving "a" unparsed
|
||||
if input == "12a" {
|
||||
assert_eq!(result.unwrap(), 12, "{input}");
|
||||
assert_eq!(s, "a", "Should leave 'a' unparsed");
|
||||
} else {
|
||||
assert!(result.is_err(), "{input} should fail");
|
||||
}
|
||||
}
|
||||
|
||||
// Test overflow cases
|
||||
for input in [
|
||||
"2147483648", // i32::MAX + 1
|
||||
"-2147483649", // i32::MIN - 1
|
||||
"99999999999", // way too large
|
||||
] {
|
||||
let mut s = input;
|
||||
assert!(
|
||||
dec_int::<ContextError>(&mut s).is_err(),
|
||||
"{input} should overflow"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user