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::{empty_shrinker, single_shrinker, Arbitrary, Gen};
40
41use crate::{
42    Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcDateTime, UtcOffset, Weekday,
43};
44
45/// Obtain an arbitrary value between the minimum and maximum inclusive.
46macro_rules! arbitrary_between {
47    ($type:ty; $gen:expr, $min:expr, $max:expr) => {{
48        let min = $min;
49        let max = $max;
50        let range = max - min;
51        <$type>::arbitrary($gen).rem_euclid(range + 1) + min
52    }};
53}
54
55impl Arbitrary for Date {
56    #[inline]
57    fn arbitrary(g: &mut Gen) -> Self {
58        // Safety: The Julian day number is in range.
59        unsafe {
60            Self::from_julian_day_unchecked(arbitrary_between!(
61                i32;
62                g,
63                Self::MIN.to_julian_day(),
64                Self::MAX.to_julian_day()
65            ))
66        }
67    }
68
69    #[inline]
70    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
71        Box::new(
72            self.to_ordinal_date()
73                .shrink()
74                .flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
75        )
76    }
77}
78
79impl Arbitrary for Duration {
80    #[inline]
81    fn arbitrary(g: &mut Gen) -> Self {
82        Self::new_ranged(<_>::arbitrary(g), <_>::arbitrary(g))
83    }
84
85    #[inline]
86    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
87        Box::new(
88            (self.subsec_nanoseconds_ranged(), self.whole_seconds())
89                .shrink()
90                .map(|(mut nanoseconds, seconds)| {
91                    // Coerce the sign if necessary.
92                    if (seconds > 0 && nanoseconds.get() < 0)
93                        || (seconds < 0 && nanoseconds.get() > 0)
94                    {
95                        nanoseconds = nanoseconds.neg();
96                    }
97
98                    Self::new_ranged_unchecked(seconds, nanoseconds)
99                }),
100        )
101    }
102}
103
104impl Arbitrary for Time {
105    #[inline]
106    fn arbitrary(g: &mut Gen) -> Self {
107        Self::from_hms_nanos_ranged(
108            <_>::arbitrary(g),
109            <_>::arbitrary(g),
110            <_>::arbitrary(g),
111            <_>::arbitrary(g),
112        )
113    }
114
115    #[inline]
116    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
117        Box::new(
118            self.as_hms_nano_ranged()
119                .shrink()
120                .map(|(hour, minute, second, nanosecond)| {
121                    Self::from_hms_nanos_ranged(hour, minute, second, nanosecond)
122                }),
123        )
124    }
125}
126
127impl Arbitrary for PrimitiveDateTime {
128    #[inline]
129    fn arbitrary(g: &mut Gen) -> Self {
130        Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
131    }
132
133    #[inline]
134    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
135        Box::new(
136            (self.date(), self.time())
137                .shrink()
138                .map(|(date, time)| Self::new(date, time)),
139        )
140    }
141}
142
143impl Arbitrary for UtcOffset {
144    #[inline]
145    fn arbitrary(g: &mut Gen) -> Self {
146        Self::from_hms_ranged(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
147    }
148
149    #[inline]
150    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
151        Box::new(
152            self.as_hms_ranged()
153                .shrink()
154                .map(|(hours, minutes, seconds)| Self::from_hms_ranged(hours, minutes, seconds)),
155        )
156    }
157}
158
159impl Arbitrary for OffsetDateTime {
160    #[inline]
161    fn arbitrary(g: &mut Gen) -> Self {
162        Self::new_in_offset(<_>::arbitrary(g), <_>::arbitrary(g), <_>::arbitrary(g))
163    }
164
165    #[inline]
166    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
167        Box::new(
168            (self.date(), self.time(), self.offset())
169                .shrink()
170                .map(|(date, time, offset)| Self::new_in_offset(date, time, offset)),
171        )
172    }
173}
174
175impl Arbitrary for UtcDateTime {
176    #[inline]
177    fn arbitrary(g: &mut Gen) -> Self {
178        Self::new(<_>::arbitrary(g), <_>::arbitrary(g))
179    }
180
181    #[inline]
182    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
183        Box::new(
184            (self.date(), self.time())
185                .shrink()
186                .map(|(date, time)| Self::new(date, time)),
187        )
188    }
189}
190
191impl Arbitrary for Weekday {
192    #[inline]
193    fn arbitrary(g: &mut Gen) -> Self {
194        use Weekday::*;
195        match arbitrary_between!(u8; g, 0, 6) {
196            0 => Monday,
197            1 => Tuesday,
198            2 => Wednesday,
199            3 => Thursday,
200            4 => Friday,
201            5 => Saturday,
202            val => {
203                debug_assert!(val == 6);
204                Sunday
205            }
206        }
207    }
208
209    #[inline]
210    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
211        match self {
212            Self::Monday => empty_shrinker(),
213            _ => single_shrinker(self.previous()),
214        }
215    }
216}
217
218impl Arbitrary for Month {
219    #[inline]
220    fn arbitrary(g: &mut Gen) -> Self {
221        use Month::*;
222        match arbitrary_between!(u8; g, 1, 12) {
223            1 => January,
224            2 => February,
225            3 => March,
226            4 => April,
227            5 => May,
228            6 => June,
229            7 => July,
230            8 => August,
231            9 => September,
232            10 => October,
233            11 => November,
234            val => {
235                debug_assert!(val == 12);
236                December
237            }
238        }
239    }
240
241    #[inline]
242    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
243        match self {
244            Self::January => empty_shrinker(),
245            _ => single_shrinker(self.previous()),
246        }
247    }
248}