diff options
Diffstat (limited to 'third_party/rust/time/src/quickcheck.rs')
-rw-r--r-- | third_party/rust/time/src/quickcheck.rs | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/third_party/rust/time/src/quickcheck.rs b/third_party/rust/time/src/quickcheck.rs new file mode 100644 index 0000000000..707f3e0a97 --- /dev/null +++ b/third_party/rust/time/src/quickcheck.rs @@ -0,0 +1,220 @@ +//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::Arbitrary) trait. +//! +//! This enables users to write tests such as this, and have test values provided automatically: +//! +//! ``` +//! # #![allow(dead_code)] +//! use quickcheck::quickcheck; +//! use time::Date; +//! +//! struct DateRange { +//! from: Date, +//! to: Date, +//! } +//! +//! impl DateRange { +//! fn new(from: Date, to: Date) -> Result<Self, ()> { +//! Ok(DateRange { from, to }) +//! } +//! } +//! +//! quickcheck! { +//! fn date_range_is_well_defined(from: Date, to: Date) -> bool { +//! let r = DateRange::new(from, to); +//! if from <= to { +//! r.is_ok() +//! } else { +//! r.is_err() +//! } +//! } +//! } +//! ``` +//! +//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in +//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple +//! anyway. + +use alloc::boxed::Box; + +use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen}; + +use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday}; + +/// Obtain an arbitrary value between the minimum and maximum inclusive. +macro_rules! arbitrary_between { + ($type:ty; $gen:expr, $min:expr, $max:expr) => {{ + let min = $min; + let max = $max; + let range = max - min; + <$type>::arbitrary($gen).rem_euclid(range + 1) + min + }}; +} + +impl Arbitrary for Date { + fn arbitrary(g: &mut Gen) -> Self { + Self::from_julian_day_unchecked(arbitrary_between!( + i32; + g, + Self::MIN.to_julian_day(), + Self::MAX.to_julian_day() + )) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + self.to_ordinal_date() + .shrink() + .flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)), + ) + } +} + +impl Arbitrary for Duration { + fn arbitrary(g: &mut Gen) -> Self { + Self::nanoseconds_i128(arbitrary_between!( + i128; + g, + Self::MIN.whole_nanoseconds(), + Self::MAX.whole_nanoseconds() + )) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.subsec_nanoseconds(), self.whole_seconds()) + .shrink() + .map(|(mut nanoseconds, seconds)| { + // Coerce the sign if necessary. + if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) { + nanoseconds *= -1; + } + + Self::new_unchecked(seconds, nanoseconds) + }), + ) + } +} + +impl Arbitrary for Time { + fn arbitrary(g: &mut Gen) -> Self { + Self::__from_hms_nanos_unchecked( + arbitrary_between!(u8; g, 0, 23), + arbitrary_between!(u8; g, 0, 59), + arbitrary_between!(u8; g, 0, 59), + arbitrary_between!(u32; g, 0, 999_999_999), + ) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + self.as_hms_nano() + .shrink() + .map(|(hour, minute, second, nanosecond)| { + Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond) + }), + ) + } +} + +impl Arbitrary for PrimitiveDateTime { + fn arbitrary(g: &mut Gen) -> Self { + Self::new(<_>::arbitrary(g), <_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.date, self.time) + .shrink() + .map(|(date, time)| Self { date, time }), + ) + } +} + +impl Arbitrary for UtcOffset { + fn arbitrary(g: &mut Gen) -> Self { + let seconds = arbitrary_between!(i32; g, -86_399, 86_399); + Self::__from_hms_unchecked( + (seconds / 3600) as _, + ((seconds % 3600) / 60) as _, + (seconds % 60) as _, + ) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + self.as_hms().shrink().map(|(hours, minutes, seconds)| { + Self::__from_hms_unchecked(hours, minutes, seconds) + }), + ) + } +} + +impl Arbitrary for OffsetDateTime { + fn arbitrary(g: &mut Gen) -> Self { + let datetime = PrimitiveDateTime::arbitrary(g); + datetime.assume_offset(<_>::arbitrary(g)) + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + Box::new( + (self.local_datetime, self.offset) + .shrink() + .map(|(local_datetime, offset)| local_datetime.assume_offset(offset)), + ) + } +} + +impl Arbitrary for Weekday { + fn arbitrary(g: &mut Gen) -> Self { + use Weekday::*; + match arbitrary_between!(u8; g, 0, 6) { + 0 => Monday, + 1 => Tuesday, + 2 => Wednesday, + 3 => Thursday, + 4 => Friday, + 5 => Saturday, + val => { + debug_assert!(val == 6); + Sunday + } + } + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + match self { + Self::Monday => empty_shrinker(), + _ => single_shrinker(self.previous()), + } + } +} + +impl Arbitrary for Month { + fn arbitrary(g: &mut Gen) -> Self { + use Month::*; + match arbitrary_between!(u8; g, 1, 12) { + 1 => January, + 2 => February, + 3 => March, + 4 => April, + 5 => May, + 6 => June, + 7 => July, + 8 => August, + 9 => September, + 10 => October, + 11 => November, + val => { + debug_assert!(val == 12); + December + } + } + } + + fn shrink(&self) -> Box<dyn Iterator<Item = Self>> { + match self { + Self::January => empty_shrinker(), + _ => single_shrinker(self.previous()), + } + } +} |