Skip to main content

time/
quickcheck.rs

1//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::Arbitrary) trait.
2//!
3//! This enables users to write tests such as this, and have test values provided automatically:
4//!
5//! ```ignore
6//! # #![expect(dead_code)]
7//! use quickcheck::quickcheck;
8//! use time::Date;
9//!
10//! struct DateRange {
11//!     from: Date,
12//!     to: Date,
13//! }
14//!
15//! impl DateRange {
16//!     fn new(from: Date, to: Date) -> Result<Self, ()> {
17//!         Ok(DateRange { from, to })
18//!     }
19//! }
20//!
21//! quickcheck! {
22//!     fn date_range_is_well_defined(from: Date, to: Date) -> bool {
23//!         let r = DateRange::new(from, to);
24//!         if from <= to {
25//!             r.is_ok()
26//!         } else {
27//!             r.is_err()
28//!         }
29//!     }
30//! }
31//! ```
32//!
33//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in
34//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple
35//! anyway.
36
37use alloc::boxed::Box;
38
39use quickcheck::{Arbitrary, Gen, empty_shrinker, single_shrinker};
40
41use crate::{
42    Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, Timestamp, UtcDateTime,
43    UtcOffset, Weekday,
44};
45
46/// Obtain an arbitrary value between the minimum and maximum inclusive.
47macro_rules! arbitrary_between {
48    ($type:ty; $gen:expr, $min:expr, $max:expr) => {{
49        let min = $min;
50        let max = $max;
51        let range = max - min;
52        <$type>::arbitrary($gen).rem_euclid(range + 1) + min
53    }};
54}
55
56impl Arbitrary for Date {
57    #[inline]
58    fn arbitrary(g: &mut Gen) -> Self {
59        // Safety: The Julian day number is in range.
60        unsafe {
61            Self::from_julian_day_unchecked(arbitrary_between!(
62                i32;
63                g,
64                Self::MIN.to_julian_day(),
65                Self::MAX.to_julian_day()
66            ))
67        }
68    }
69
70    #[inline]
71    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
72        Box::new(
73            self.to_ordinal_date()
74                .shrink()
75                .flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
76        )
77    }
78}
79
80impl Arbitrary for Duration {
81    #[inline]
82    fn arbitrary(g: &mut Gen) -> Self {
83        Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g))
84    }
85
86    #[inline]
87    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
88        Box::new(
89            (self.subsec_nanoseconds_ranged(), self.whole_seconds())
90                .shrink()
91                .map(|(mut nanoseconds, seconds)| {
92                    // Coerce the sign if necessary.
93                    if (seconds > 0 && nanoseconds.get() < 0)
94                        || (seconds < 0 && nanoseconds.get() > 0)
95                    {
96                        nanoseconds = nanoseconds.neg();
97                    }
98
99                    Self::new_ranged_unchecked(seconds, nanoseconds)
100                }),
101        )
102    }
103}
104
105impl Arbitrary for Time {
106    #[inline]
107    fn arbitrary(g: &mut Gen) -> Self {
108        Self::from_hms_nanos_ranged(
109            <_>::arbitrary(g),
110            <_>::arbitrary(g),
111            <_>::arbitrary(g),
112            <_>::arbitrary(g),
113        )
114    }
115
116    #[inline]
117    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
118        Box::new(
119            self.as_hms_nano_ranged()
120                .shrink()
121                .map(|(hour, minute, second, nanosecond)| {
122                    Self::from_hms_nanos_ranged(hour, minute, second, nanosecond)
123                }),
124        )
125    }
126}
127
128impl Arbitrary for PrimitiveDateTime {
129    #[inline]
130    fn arbitrary(g: &mut Gen) -> Self {
131        Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
132    }
133
134    #[inline]
135    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
136        Box::new(
137            (self.date(), self.time())
138                .shrink()
139                .map(|(date, time)| Self::new(date, time)),
140        )
141    }
142}
143
144impl Arbitrary for UtcOffset {
145    #[inline]
146    fn arbitrary(g: &mut Gen) -> Self {
147        Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
148    }
149
150    #[inline]
151    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
152        Box::new(
153            self.as_hms_ranged()
154                .shrink()
155                .map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)),
156        )
157    }
158}
159
160impl Arbitrary for OffsetDateTime {
161    #[inline]
162    fn arbitrary(g: &mut Gen) -> Self {
163        Self::new_in_offset(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
164    }
165
166    #[inline]
167    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
168        Box::new(
169            (self.date(), self.time(), self.offset())
170                .shrink()
171                .map(|(date, time, offset)| Self::new_in_offset(date, time, offset)),
172        )
173    }
174}
175
176impl Arbitrary for UtcDateTime {
177    #[inline]
178    fn arbitrary(g: &mut Gen) -> Self {
179        Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
180    }
181
182    #[inline]
183    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
184        Box::new(
185            (self.date(), self.time())
186                .shrink()
187                .map(|(date, time)| Self::new(date, time)),
188        )
189    }
190}
191
192impl Arbitrary for Timestamp {
193    #[inline]
194    fn arbitrary(g: &mut Gen) -> Self {
195        Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g))
196    }
197
198    #[inline]
199    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
200        Box::new(
201            self.as_parts_ranged()
202                .shrink()
203                .map(|(seconds, nanoseconds)| Self::new_ranged(seconds, nanoseconds)),
204        )
205    }
206}
207
208impl Arbitrary for Weekday {
209    #[inline]
210    fn arbitrary(g: &mut Gen) -> Self {
211        use Weekday::*;
212        match arbitrary_between!(u8; g, 0, 6) {
213            0 => Monday,
214            1 => Tuesday,
215            2 => Wednesday,
216            3 => Thursday,
217            4 => Friday,
218            5 => Saturday,
219            val => {
220                debug_assert!(val == 6);
221                Sunday
222            }
223        }
224    }
225
226    #[inline]
227    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
228        match self {
229            Self::Monday => empty_shrinker(),
230            _ => single_shrinker(self.previous()),
231        }
232    }
233}
234
235impl Arbitrary for Month {
236    #[inline]
237    fn arbitrary(g: &mut Gen) -> Self {
238        use Month::*;
239        match arbitrary_between!(u8; g, 1, 12) {
240            1 => January,
241            2 => February,
242            3 => March,
243            4 => April,
244            5 => May,
245            6 => June,
246            7 => July,
247            8 => August,
248            9 => September,
249            10 => October,
250            11 => November,
251            val => {
252                debug_assert!(val == 12);
253                December
254            }
255        }
256    }
257
258    #[inline]
259    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
260        match self {
261            Self::January => empty_shrinker(),
262            _ => single_shrinker(self.previous()),
263        }
264    }
265}