protest_extras/generators/
datetime.rs1use protest::{Generator, GeneratorConfig};
11use rand::Rng;
12use std::time::{Duration, SystemTime, UNIX_EPOCH};
13
14#[derive(Debug, Clone)]
22pub struct TimestampGenerator {
23 min: i64,
24 max: i64,
25}
26
27impl TimestampGenerator {
28 pub fn new(min: i64, max: i64) -> Self {
30 Self { min, max }
31 }
32
33 pub fn recent() -> Self {
35 let now = SystemTime::now()
36 .duration_since(UNIX_EPOCH)
37 .unwrap()
38 .as_secs() as i64;
39 let ten_years_ago = now - (10 * 365 * 24 * 60 * 60);
40 Self {
41 min: ten_years_ago,
42 max: now,
43 }
44 }
45
46 pub fn future() -> Self {
48 let now = SystemTime::now()
49 .duration_since(UNIX_EPOCH)
50 .unwrap()
51 .as_secs() as i64;
52 let ten_years_later = now + (10 * 365 * 24 * 60 * 60);
53 Self {
54 min: now,
55 max: ten_years_later,
56 }
57 }
58}
59
60impl Generator<i64> for TimestampGenerator {
61 fn generate(&self, rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> i64 {
62 rng.r#gen_range(self.min..=self.max)
63 }
64
65 fn shrink(&self, value: &i64) -> Box<dyn Iterator<Item = i64>> {
66 let mut shrinks = Vec::new();
67
68 if *value > 0 && self.min <= 0 {
70 shrinks.push(0);
71 }
72
73 if *value > self.min {
75 shrinks.push(self.min);
76 }
77
78 if *value > self.min {
80 let half = (*value + self.min) / 2;
81 if half >= self.min {
82 shrinks.push(half);
83 }
84 }
85
86 Box::new(shrinks.into_iter())
87 }
88}
89
90#[derive(Debug, Clone)]
96pub struct DurationGenerator {
97 min_secs: u64,
98 max_secs: u64,
99}
100
101impl DurationGenerator {
102 pub fn new(min_secs: u64, max_secs: u64) -> Self {
104 Self { min_secs, max_secs }
105 }
106
107 pub fn milliseconds() -> Self {
109 Self {
110 min_secs: 0,
111 max_secs: 1,
112 }
113 }
114
115 pub fn seconds() -> Self {
117 Self {
118 min_secs: 0,
119 max_secs: 60,
120 }
121 }
122
123 pub fn minutes() -> Self {
125 Self {
126 min_secs: 0,
127 max_secs: 60 * 60,
128 }
129 }
130
131 pub fn hours() -> Self {
133 Self {
134 min_secs: 0,
135 max_secs: 24 * 60 * 60,
136 }
137 }
138}
139
140impl Generator<Duration> for DurationGenerator {
141 fn generate(&self, rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> Duration {
142 let secs = rng.r#gen_range(self.min_secs..=self.max_secs);
143 let nanos = rng.r#gen_range(0..1_000_000_000);
144 Duration::new(secs, nanos)
145 }
146
147 fn shrink(&self, value: &Duration) -> Box<dyn Iterator<Item = Duration>> {
148 let mut shrinks = Vec::new();
149
150 if value.as_secs() > 0 || value.subsec_nanos() > 0 {
152 shrinks.push(Duration::ZERO);
153 }
154
155 let min_dur = Duration::from_secs(self.min_secs);
157 if *value > min_dur {
158 shrinks.push(min_dur);
159 }
160
161 if value.as_secs() > 0 {
163 shrinks.push(Duration::from_secs(value.as_secs() / 2));
164 }
165
166 if value.subsec_nanos() > 0 {
168 shrinks.push(Duration::from_secs(value.as_secs()));
169 }
170
171 Box::new(shrinks.into_iter())
172 }
173}
174
175#[derive(Debug, Clone)]
181pub struct SystemTimeGenerator {
182 start: SystemTime,
183 max_offset_secs: u64,
184}
185
186impl SystemTimeGenerator {
187 pub fn new(start: SystemTime, max_offset_secs: u64) -> Self {
191 Self {
192 start,
193 max_offset_secs,
194 }
195 }
196
197 pub fn around_now() -> Self {
199 let now = SystemTime::now();
200 let one_year = 365 * 24 * 60 * 60;
201
202 let start = now - Duration::from_secs(one_year);
204
205 Self {
206 start,
207 max_offset_secs: one_year * 2, }
209 }
210
211 pub fn from_epoch(max_offset_secs: u64) -> Self {
213 Self {
214 start: UNIX_EPOCH,
215 max_offset_secs,
216 }
217 }
218}
219
220impl Generator<SystemTime> for SystemTimeGenerator {
221 fn generate(&self, rng: &mut dyn rand::RngCore, _config: &GeneratorConfig) -> SystemTime {
222 let offset_secs = rng.r#gen_range(0..=self.max_offset_secs);
223 self.start + Duration::from_secs(offset_secs)
224 }
225
226 fn shrink(&self, value: &SystemTime) -> Box<dyn Iterator<Item = SystemTime>> {
227 let mut shrinks = Vec::new();
228
229 if *value > self.start {
231 shrinks.push(self.start);
232 }
233
234 if let Ok(duration_since_start) = value.duration_since(self.start) {
236 let half_secs = duration_since_start.as_secs() / 2;
237 if half_secs > 0 {
238 shrinks.push(self.start + Duration::from_secs(half_secs));
239 }
240 }
241
242 Box::new(shrinks.into_iter())
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use rand::thread_rng;
250
251 #[test]
252 fn test_timestamp_generator() {
253 let generator = TimestampGenerator::new(0, 1_000_000);
254 let mut rng = thread_rng();
255 let config = GeneratorConfig::default();
256
257 for _ in 0..10 {
258 let ts = generator.generate(&mut rng, &config);
259 assert!((0..=1_000_000).contains(&ts));
260 }
261 }
262
263 #[test]
264 fn test_timestamp_recent() {
265 let generator = TimestampGenerator::recent();
266 let mut rng = thread_rng();
267 let config = GeneratorConfig::default();
268
269 let now = SystemTime::now()
270 .duration_since(UNIX_EPOCH)
271 .unwrap()
272 .as_secs() as i64;
273
274 for _ in 0..10 {
275 let ts = generator.generate(&mut rng, &config);
276 assert!(ts <= now);
277 assert!(ts >= now - (10 * 365 * 24 * 60 * 60));
278 }
279 }
280
281 #[test]
282 fn test_duration_generator() {
283 let generator = DurationGenerator::new(0, 100);
284 let mut rng = thread_rng();
285 let config = GeneratorConfig::default();
286
287 for _ in 0..10 {
288 let dur = generator.generate(&mut rng, &config);
289 assert!(dur.as_secs() <= 100);
290 }
291 }
292
293 #[test]
294 fn test_duration_presets() {
295 let mut rng = thread_rng();
296 let config = GeneratorConfig::default();
297
298 let generator = DurationGenerator::seconds();
299 let dur = generator.generate(&mut rng, &config);
300 assert!(dur.as_secs() <= 60);
301
302 let generator = DurationGenerator::minutes();
303 let dur = generator.generate(&mut rng, &config);
304 assert!(dur.as_secs() <= 3600);
305 }
306
307 #[test]
308 fn test_system_time_generator() {
309 let start = UNIX_EPOCH;
310 let generator = SystemTimeGenerator::new(start, 1_000_000);
311 let mut rng = thread_rng();
312 let config = GeneratorConfig::default();
313
314 for _ in 0..10 {
315 let time = generator.generate(&mut rng, &config);
316 assert!(time >= start);
317
318 let duration = time.duration_since(start).unwrap();
319 assert!(duration.as_secs() <= 1_000_000);
320 }
321 }
322
323 #[test]
324 fn test_system_time_around_now() {
325 let generator = SystemTimeGenerator::around_now();
326 let mut rng = thread_rng();
327 let config = GeneratorConfig::default();
328
329 let now = SystemTime::now();
330
331 for _ in 0..10 {
332 let time = generator.generate(&mut rng, &config);
333
334 if let Ok(duration) = now.duration_since(time) {
337 assert!(duration.as_secs() <= 365 * 24 * 60 * 60 + 10);
338 } else if let Ok(duration) = time.duration_since(now) {
339 assert!(duration.as_secs() <= 365 * 24 * 60 * 60 + 10);
340 }
341 }
342 }
343}