time/
quickcheck.rs
1use alloc::boxed::Box;
38
39use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen};
40
41use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
42
43macro_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 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}