//! 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 { //! 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { match self { Self::January => empty_shrinker(), _ => single_shrinker(self.previous()), } } }