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