Skip to main content

atomic_time/
quickcheck.rs

1use core::sync::atomic::Ordering;
2use std::{
3  boxed::Box,
4  time::{Duration, SystemTime},
5};
6
7use ::quickcheck::{Arbitrary, Gen};
8
9use crate::{AtomicDuration, AtomicOptionDuration, AtomicOptionSystemTime, AtomicSystemTime};
10
11fn system_time_from_parts((seconds, nanoseconds): (u32, u32)) -> SystemTime {
12  let duration = Duration::new(
13    u64::from(seconds.min(i32::MAX as u32)),
14    nanoseconds % 1_000_000_000,
15  );
16  SystemTime::UNIX_EPOCH
17    .checked_add(duration)
18    .unwrap_or(SystemTime::UNIX_EPOCH)
19}
20
21fn system_time_from_duration(duration: Duration) -> SystemTime {
22  SystemTime::UNIX_EPOCH
23    .checked_add(duration)
24    .unwrap_or(SystemTime::UNIX_EPOCH)
25}
26
27fn duration_since_epoch(system_time: SystemTime) -> Duration {
28  system_time
29    .duration_since(SystemTime::UNIX_EPOCH)
30    .unwrap_or(Duration::ZERO)
31}
32
33impl Clone for AtomicDuration {
34  /// Creates an atomic snapshot of this value with sequentially consistent
35  /// ordering.
36  ///
37  /// If another thread writes concurrently, the clone contains the value
38  /// observed at this load's atomic linearization point.
39  #[inline(always)]
40  fn clone(&self) -> Self {
41    Self::new(self.load(Ordering::SeqCst))
42  }
43}
44
45impl Clone for AtomicOptionDuration {
46  /// Creates an atomic snapshot of this value with sequentially consistent
47  /// ordering.
48  ///
49  /// If another thread writes concurrently, the clone contains the value
50  /// observed at this load's atomic linearization point.
51  #[inline(always)]
52  fn clone(&self) -> Self {
53    Self::new(self.load(Ordering::SeqCst))
54  }
55}
56
57impl Clone for AtomicSystemTime {
58  /// Creates an atomic snapshot of this value with sequentially consistent
59  /// ordering.
60  ///
61  /// If another thread writes concurrently, the clone contains the value
62  /// observed at this load's atomic linearization point.
63  #[inline(always)]
64  fn clone(&self) -> Self {
65    Self::new(self.load(Ordering::SeqCst))
66  }
67}
68
69impl Clone for AtomicOptionSystemTime {
70  /// Creates an atomic snapshot of this value with sequentially consistent
71  /// ordering.
72  ///
73  /// If another thread writes concurrently, the clone contains the value
74  /// observed at this load's atomic linearization point.
75  #[inline(always)]
76  fn clone(&self) -> Self {
77    Self::new(self.load(Ordering::SeqCst))
78  }
79}
80
81impl Arbitrary for AtomicDuration {
82  fn arbitrary(g: &mut Gen) -> Self {
83    Self::new(Duration::arbitrary(g))
84  }
85
86  fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
87    Box::new(self.load(Ordering::SeqCst).shrink().map(Self::new))
88  }
89}
90
91impl Arbitrary for AtomicOptionDuration {
92  fn arbitrary(g: &mut Gen) -> Self {
93    Self::new(Option::<Duration>::arbitrary(g))
94  }
95
96  fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
97    Box::new(self.load(Ordering::SeqCst).shrink().map(Self::new))
98  }
99}
100
101impl Arbitrary for AtomicSystemTime {
102  fn arbitrary(g: &mut Gen) -> Self {
103    Self::new(system_time_from_parts(<(u32, u32)>::arbitrary(g)))
104  }
105
106  fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
107    Box::new(
108      duration_since_epoch(self.load(Ordering::SeqCst))
109        .shrink()
110        .map(|duration| Self::new(system_time_from_duration(duration))),
111    )
112  }
113}
114
115impl Arbitrary for AtomicOptionSystemTime {
116  fn arbitrary(g: &mut Gen) -> Self {
117    Self::new(Option::<(u32, u32)>::arbitrary(g).map(system_time_from_parts))
118  }
119
120  fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
121    Box::new(
122      self
123        .load(Ordering::SeqCst)
124        .map(duration_since_epoch)
125        .shrink()
126        .map(|duration| Self::new(duration.map(system_time_from_duration))),
127    )
128  }
129}
130
131#[cfg(test)]
132mod tests {
133  use super::*;
134
135  #[test]
136  fn arbitrary_values_can_be_loaded() {
137    let mut generator = Gen::new(32);
138
139    let duration = AtomicDuration::arbitrary(&mut generator);
140    let _ = duration.load(Ordering::SeqCst);
141
142    let option_duration = AtomicOptionDuration::arbitrary(&mut generator);
143    let _ = option_duration.load(Ordering::SeqCst);
144
145    let system_time = AtomicSystemTime::arbitrary(&mut generator);
146    assert!(system_time
147      .load(Ordering::SeqCst)
148      .duration_since(SystemTime::UNIX_EPOCH)
149      .is_ok());
150
151    let option_system_time = AtomicOptionSystemTime::arbitrary(&mut generator);
152    assert!(option_system_time
153      .load(Ordering::SeqCst)
154      .is_none_or(|value| value.duration_since(SystemTime::UNIX_EPOCH).is_ok()));
155  }
156
157  #[test]
158  fn clone_takes_a_snapshot() {
159    let duration = AtomicDuration::new(Duration::from_secs(1));
160    let duration_clone = duration.clone();
161    duration.store(Duration::from_secs(2), Ordering::SeqCst);
162    assert_eq!(
163      duration_clone.load(Ordering::SeqCst),
164      Duration::from_secs(1)
165    );
166
167    let option_duration = AtomicOptionDuration::new(Some(Duration::from_secs(1)));
168    let option_duration_clone = option_duration.clone();
169    option_duration.store(None, Ordering::SeqCst);
170    assert_eq!(
171      option_duration_clone.load(Ordering::SeqCst),
172      Some(Duration::from_secs(1))
173    );
174
175    let system_time = AtomicSystemTime::new(SystemTime::UNIX_EPOCH);
176    let system_time_clone = system_time.clone();
177    system_time.store(
178      SystemTime::UNIX_EPOCH + Duration::from_secs(1),
179      Ordering::SeqCst,
180    );
181    assert_eq!(
182      system_time_clone.load(Ordering::SeqCst),
183      SystemTime::UNIX_EPOCH
184    );
185
186    let option_system_time = AtomicOptionSystemTime::new(Some(SystemTime::UNIX_EPOCH));
187    let option_system_time_clone = option_system_time.clone();
188    option_system_time.store(None, Ordering::SeqCst);
189    assert_eq!(
190      option_system_time_clone.load(Ordering::SeqCst),
191      Some(SystemTime::UNIX_EPOCH)
192    );
193  }
194
195  #[test]
196  fn shrunk_system_times_remain_valid() {
197    let system_time = AtomicSystemTime::new(SystemTime::UNIX_EPOCH + Duration::from_secs(10));
198    for value in system_time.shrink().take(64) {
199      assert!(value
200        .load(Ordering::SeqCst)
201        .duration_since(SystemTime::UNIX_EPOCH)
202        .is_ok());
203    }
204
205    let none = AtomicOptionSystemTime::none();
206    assert!(none.shrink().next().is_none());
207
208    let some = AtomicOptionSystemTime::new(Some(SystemTime::UNIX_EPOCH + Duration::from_secs(10)));
209    for value in some.shrink().take(64) {
210      assert!(value
211        .load(Ordering::SeqCst)
212        .is_none_or(|time| time.duration_since(SystemTime::UNIX_EPOCH).is_ok()));
213    }
214  }
215}