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