From 9f95f7fbbe5b56e65c00c26f580bf67a4001e146 Mon Sep 17 00:00:00 2001 From: Will Stott Date: Sat, 10 Dec 2022 13:29:41 +0000 Subject: [PATCH 1/2] Extend git-date's baseline tests to also re-format the parsed dates --- .../fixtures/generate_git_date_baseline.sh | 15 +++-- git-date/tests/time/baseline.rs | 63 +++++++++++++++++++ git-date/tests/time/mod.rs | 1 + git-date/tests/time/parse.rs | 45 +------------ 4 files changed, 74 insertions(+), 50 deletions(-) create mode 100644 git-date/tests/time/baseline.rs diff --git a/git-date/tests/fixtures/generate_git_date_baseline.sh b/git-date/tests/fixtures/generate_git_date_baseline.sh index 6c02cf7620d..f49c01f3bf8 100644 --- a/git-date/tests/fixtures/generate_git_date_baseline.sh +++ b/git-date/tests/fixtures/generate_git_date_baseline.sh @@ -5,10 +5,12 @@ git init; function baseline() { local test_date=$1 # first argument is the date to test + local test_name=$2 # second argument is the format name for re-formatting git -c section.key="$test_date" config --type=expiry-date section.key && status=0 || status=$? { echo "$test_date" + echo "$test_name" echo "$status" if [ $status == 0 ] then @@ -27,15 +29,16 @@ function baseline() { # ODO #baseline '2022-08-22' # rfc2822 -baseline 'Thu, 18 Aug 2022 12:45:06 +0800' +baseline 'Thu, 18 Aug 2022 12:45:06 +0800' 'RFC2822' # iso8601 -baseline '2022-08-17 22:04:58 +0200' +baseline '2022-08-17 22:04:58 +0200' 'ISO8601' # iso8601_strict -baseline '2022-08-17T21:43:13+08:00' +baseline '2022-08-17T21:43:13+08:00' 'ISO8601_STRICT' # default -baseline 'Thu Sep 04 2022 10:45:06 -0400' +baseline 'Thu Sep 04 2022 10:45:06 -0400' '' # cannot round-trip, incorrect day-of-week +baseline 'Sun Sep 04 2022 10:45:06 -0400' 'DEFAULT' # unix -baseline '123456789' +baseline '123456789' 'UNIX' # raw -baseline '1660874655 +0800' +baseline '1660874655 +0800' 'RAW' diff --git a/git-date/tests/time/baseline.rs b/git-date/tests/time/baseline.rs new file mode 100644 index 00000000000..7c49baadc8b --- /dev/null +++ b/git-date/tests/time/baseline.rs @@ -0,0 +1,63 @@ +use std::{collections::HashMap, time::SystemTime}; + +use git_date::time::{format, Format}; +use once_cell::sync::Lazy; + +type Result = std::result::Result>; + +static BASELINE: Lazy> = Lazy::new(|| { + let base = git_testtools::scripted_fixture_repo_read_only("generate_git_date_baseline.sh").unwrap(); + + (|| -> Result<_> { + let mut map = HashMap::new(); + let file = std::fs::read(base.join("baseline.git"))?; + let baseline = std::str::from_utf8(&file).expect("valid utf"); + let mut lines = baseline.lines(); + while let Some(date_str) = lines.next() { + let name = lines.next().expect("four lines per baseline").to_string(); + let exit_code = lines.next().expect("four lines per baseline").parse()?; + let output: u32 = lines + .next() + .expect("four lines per baseline") + .parse() + .expect("valid epoch value"); + map.insert(date_str.into(), (name, exit_code, output)); + } + Ok(map) + })() + .unwrap() +}); + +#[test] +fn baseline() { + for (pattern, (name, exit_code, output)) in BASELINE.iter() { + let res = git_date::parse(pattern.as_str(), Some(SystemTime::now())); + assert_eq!( + res.is_ok(), + *exit_code == 0, + "{pattern:?} disagrees with baseline: {res:?}" + ); + if *exit_code == 0 { + let t = res.unwrap(); + let actual = t.seconds_since_unix_epoch; + assert_eq!(actual, *output, "{pattern:?} disagrees with baseline: {actual:?}"); + if name == "" { + // This test is not appropriate for round-trip, as the input is malformed. + continue; + } + let reformatted = t.format(match name.as_str() { + "RFC2822" => Format::Custom(format::RFC2822), + "ISO8601" => Format::Custom(format::ISO8601), + "ISO8601_STRICT" => Format::Custom(format::ISO8601_STRICT), + "DEFAULT" => Format::Custom(format::DEFAULT), + "UNIX" => Format::Unix, + "RAW" => Format::Raw, + &_ => Format::Raw, + }); + assert_eq!( + reformatted, *pattern, + "{reformatted:?} disagrees with baseline: {pattern:?}" + ); + } + } +} diff --git a/git-date/tests/time/mod.rs b/git-date/tests/time/mod.rs index bf566246e22..1ad1cc91f2d 100644 --- a/git-date/tests/time/mod.rs +++ b/git-date/tests/time/mod.rs @@ -1,6 +1,7 @@ use bstr::ByteSlice; use git_date::{time::Sign, Time}; +mod baseline; mod format; mod parse; diff --git a/git-date/tests/time/parse.rs b/git-date/tests/time/parse.rs index 9dfcd245f50..b3705073018 100644 --- a/git-date/tests/time/parse.rs +++ b/git-date/tests/time/parse.rs @@ -1,49 +1,6 @@ -use std::{collections::HashMap, time::SystemTime}; +use std::time::SystemTime; -use bstr::{BString, ByteSlice}; use git_date::{time::Sign, Time}; -use once_cell::sync::Lazy; - -type Result = std::result::Result>; - -static BASELINE: Lazy> = Lazy::new(|| { - let base = git_testtools::scripted_fixture_repo_read_only("generate_git_date_baseline.sh").unwrap(); - - (|| -> Result<_> { - let mut map = HashMap::new(); - let baseline = std::fs::read(base.join("baseline.git"))?; - let mut lines = baseline.lines(); - while let Some(date_str) = lines.next() { - let exit_code = lines.next().expect("three lines per baseline").to_str()?.parse()?; - let output: u32 = lines - .next() - .expect("three lines per baseline") - .to_str() - .expect("valid utf") - .parse() - .expect("valid epoch value"); - map.insert(date_str.into(), (exit_code, output)); - } - Ok(map) - })() - .unwrap() -}); - -#[test] -fn baseline() { - for (pattern, (exit_code, output)) in BASELINE.iter() { - let res = git_date::parse(pattern.to_str().expect("valid pattern"), Some(SystemTime::now())); - assert_eq!( - res.is_ok(), - *exit_code == 0, - "{pattern:?} disagrees with baseline: {res:?}" - ); - if *exit_code == 0 { - let actual = res.unwrap().seconds_since_unix_epoch; - assert_eq!(actual, *output, "{pattern:?} disagrees with baseline: {actual:?}") - } - } -} #[test] fn special_time_is_ok_for_now() { From f4ea59db0a429801ab40b1294da4bffd9e0f80b3 Mon Sep 17 00:00:00 2001 From: Will Stott Date: Sat, 10 Dec 2022 13:34:36 +0000 Subject: [PATCH 2/2] fix: correctly parse raw dates with negative timezone offsets --- git-date/src/parse.rs | 7 +++++-- git-date/tests/time/parse.rs | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/git-date/src/parse.rs b/git-date/src/parse.rs index a631be27d9a..019d78bdb74 100644 --- a/git-date/src/parse.rs +++ b/git-date/src/parse.rs @@ -72,10 +72,13 @@ pub(crate) mod function { if offset.len() != 5 { return None; } - let sign = if &offset[..1] == "-" { Sign::Plus } else { Sign::Minus }; + let sign = if &offset[..1] == "-" { Sign::Minus } else { Sign::Plus }; let hours: i32 = offset[1..3].parse().ok()?; let minutes: i32 = offset[3..5].parse().ok()?; - let offset_in_seconds = hours * 3600 + minutes * 60; + let mut offset_in_seconds = hours * 3600 + minutes * 60; + if sign == Sign::Minus { + offset_in_seconds *= -1; + }; let time = Time { seconds_since_unix_epoch, offset_in_seconds, diff --git a/git-date/tests/time/parse.rs b/git-date/tests/time/parse.rs index b3705073018..bf93b8470df 100644 --- a/git-date/tests/time/parse.rs +++ b/git-date/tests/time/parse.rs @@ -40,6 +40,29 @@ fn rfc2822() { ); } +#[test] +fn raw() { + assert_eq!( + git_date::parse("1660874655 +0800", None).expect("parsed raw string"), + Time { + seconds_since_unix_epoch: 1660874655, + offset_in_seconds: 28800, + sign: Sign::Plus, + }, + "could not parse with raw format" + ); + + assert_eq!( + git_date::parse("1660874655 -0800", None).expect("parsed raw string"), + Time { + seconds_since_unix_epoch: 1660874655, + offset_in_seconds: -28800, + sign: Sign::Minus, + }, + "could not parse with raw format" + ); +} + #[test] fn invalid_dates_can_be_produced_without_current_time() { assert!(matches!(