Skip to content

Commit 0f3f2f7

Browse files
committed
test(lazer): add time tests
1 parent 742657b commit 0f3f2f7

File tree

3 files changed

+348
-3
lines changed

3 files changed

+348
-3
lines changed

.github/workflows/ci-lazer-rust.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ jobs:
4141
- name: Clippy check
4242
run: cargo clippy -p pyth-lazer-protocol -p pyth-lazer-client -p pyth-lazer-publisher-sdk --all-targets -- --deny warnings
4343
if: success() || failure()
44+
- name: Clippy check with mry
45+
run: cargo clippy -F mry -p pyth-lazer-protocol --all-targets -- --deny warnings
46+
if: success() || failure()
4447
- name: test
4548
run: cargo test -p pyth-lazer-protocol -p pyth-lazer-client -p pyth-lazer-publisher-sdk
4649
if: success() || failure()
50+
- name: test with mry
51+
run: cargo test -F mry -p pyth-lazer-protocol
52+
if: success() || failure()

lazer/sdk/rust/protocol/src/time.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#[cfg(test)]
2+
mod tests;
3+
14
use {
25
anyhow::Context,
36
protobuf::{
@@ -106,12 +109,12 @@ impl TimestampUs {
106109

107110
#[inline]
108111
pub fn elapsed(self) -> anyhow::Result<DurationUs> {
109-
self.duration_since(Self::now())
112+
Self::now().duration_since(self)
110113
}
111114

112115
#[inline]
113116
pub fn saturating_elapsed(self) -> DurationUs {
114-
self.saturating_duration_since(Self::now())
117+
Self::now().saturating_duration_since(self)
115118
}
116119

117120
#[inline]
@@ -300,6 +303,12 @@ impl DurationUs {
300303
u128::from(self.0) * 1000
301304
}
302305

306+
#[inline]
307+
pub fn as_nanos_i128(self) -> i128 {
308+
// never overflows
309+
i128::from(self.0) * 1000
310+
}
311+
303312
#[inline]
304313
pub fn from_nanos(nanos: u128) -> anyhow::Result<Self> {
305314
let micros = nanos.checked_div(1000).context("checked_div failed")?;
@@ -345,7 +354,7 @@ impl DurationUs {
345354
}
346355

347356
#[inline]
348-
pub fn from_days_u32(days: u32) -> Self {
357+
pub fn from_days_u16(days: u16) -> Self {
349358
// never overflows
350359
Self((days as u64) * 24 * 3600 * 1_000_000)
351360
}
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
use super::*;
2+
3+
type ChronoUtcDateTime = chrono::DateTime<chrono::Utc>;
4+
5+
#[test]
6+
fn timestamp_constructors() {
7+
assert!(TimestampUs::now() > TimestampUs::UNIX_EPOCH);
8+
assert!(TimestampUs::now() < TimestampUs::MAX);
9+
10+
assert_eq!(TimestampUs::from_micros(12345).as_micros(), 12345);
11+
assert_eq!(TimestampUs::from_micros(12345).as_nanos(), 12345000);
12+
assert_eq!(TimestampUs::from_micros(12345).as_millis(), 12);
13+
assert_eq!(TimestampUs::from_micros(12345).as_secs(), 0);
14+
15+
assert_eq!(TimestampUs::from_micros(123456789).as_millis(), 123456);
16+
assert_eq!(TimestampUs::from_micros(123456789).as_secs(), 123);
17+
18+
assert_eq!(
19+
TimestampUs::from_nanos(1234567890).unwrap().as_nanos(),
20+
1234567000
21+
);
22+
assert_eq!(
23+
TimestampUs::from_nanos(1234567890).unwrap().as_nanos_i128(),
24+
1234567000
25+
);
26+
27+
assert_eq!(TimestampUs::from_millis(25).unwrap().as_millis(), 25);
28+
assert_eq!(TimestampUs::from_millis(25).unwrap().as_micros(), 25000);
29+
30+
assert_eq!(TimestampUs::from_secs(25).unwrap().as_secs(), 25);
31+
assert_eq!(TimestampUs::from_secs(25).unwrap().as_millis(), 25000);
32+
assert_eq!(TimestampUs::from_secs(25).unwrap().as_micros(), 25000000);
33+
34+
TimestampUs::from_nanos(u128::from(u64::MAX) * 1000 + 5000).unwrap_err();
35+
TimestampUs::from_millis(5_000_000_000_000_000_000).unwrap_err();
36+
TimestampUs::from_secs(5_000_000_000_000_000).unwrap_err();
37+
}
38+
39+
#[test]
40+
fn duration_constructors() {
41+
assert_eq!(DurationUs::from_micros(12345).as_micros(), 12345);
42+
assert_eq!(DurationUs::from_micros(12345).as_nanos(), 12345000);
43+
assert_eq!(DurationUs::from_micros(12345).as_millis(), 12);
44+
assert_eq!(DurationUs::from_micros(12345).as_secs(), 0);
45+
46+
assert_eq!(DurationUs::from_micros(123456789).as_millis(), 123456);
47+
assert_eq!(DurationUs::from_micros(123456789).as_secs(), 123);
48+
49+
assert_eq!(
50+
DurationUs::from_nanos(1234567890).unwrap().as_nanos(),
51+
1234567000
52+
);
53+
assert_eq!(
54+
DurationUs::from_nanos(1234567890).unwrap().as_nanos_i128(),
55+
1234567000
56+
);
57+
58+
assert_eq!(DurationUs::from_millis(25).unwrap().as_millis(), 25);
59+
assert_eq!(DurationUs::from_millis(25).unwrap().as_micros(), 25000);
60+
61+
assert_eq!(DurationUs::from_secs(25).unwrap().as_secs(), 25);
62+
assert_eq!(DurationUs::from_secs(25).unwrap().as_millis(), 25000);
63+
assert_eq!(DurationUs::from_secs(25).unwrap().as_micros(), 25000000);
64+
65+
DurationUs::from_nanos(u128::from(u64::MAX) * 1000 + 5000).unwrap_err();
66+
DurationUs::from_millis(5_000_000_000_000_000_000).unwrap_err();
67+
DurationUs::from_secs(5_000_000_000_000_000).unwrap_err();
68+
69+
assert_eq!(DurationUs::from_millis_u32(42).as_micros(), 42_000);
70+
assert_eq!(DurationUs::from_secs_u32(42).as_micros(), 42_000_000);
71+
assert_eq!(DurationUs::from_days_u16(42).as_micros(), 3_628_800_000_000);
72+
73+
assert_eq!(
74+
DurationUs::from_millis_u32(u32::MAX).as_micros(),
75+
4_294_967_295_000
76+
);
77+
assert_eq!(
78+
DurationUs::from_secs_u32(u32::MAX).as_micros(),
79+
4_294_967_295_000_000
80+
);
81+
assert_eq!(
82+
DurationUs::from_days_u16(u16::MAX).as_micros(),
83+
5_662_224_000_000_000
84+
);
85+
}
86+
87+
#[test]
88+
#[allow(clippy::bool_assert_comparison)]
89+
fn timestamp_ops() {
90+
assert_eq!(
91+
TimestampUs::from_micros(123)
92+
.checked_sub(DurationUs::from_micros(23))
93+
.unwrap(),
94+
TimestampUs::from_micros(100)
95+
);
96+
TimestampUs::from_micros(123)
97+
.checked_sub(DurationUs::from_micros(223))
98+
.unwrap_err();
99+
100+
assert_eq!(
101+
TimestampUs::from_micros(123)
102+
.checked_add(DurationUs::from_micros(23))
103+
.unwrap(),
104+
TimestampUs::from_micros(146)
105+
);
106+
TimestampUs::from_micros(u64::MAX - 5)
107+
.checked_add(DurationUs::from_micros(223))
108+
.unwrap_err();
109+
110+
assert_eq!(
111+
TimestampUs::from_micros(123)
112+
.duration_since(TimestampUs::from_micros(23))
113+
.unwrap(),
114+
DurationUs::from_micros(100)
115+
);
116+
TimestampUs::from_micros(123)
117+
.duration_since(TimestampUs::from_micros(223))
118+
.unwrap_err();
119+
120+
assert_eq!(
121+
TimestampUs::from_micros(123).saturating_duration_since(TimestampUs::from_micros(23)),
122+
DurationUs::from_micros(100)
123+
);
124+
assert_eq!(
125+
TimestampUs::from_micros(123).saturating_duration_since(TimestampUs::from_micros(223)),
126+
DurationUs::ZERO
127+
);
128+
129+
assert_eq!(
130+
TimestampUs::from_micros(123).saturating_add(DurationUs::from_micros(100)),
131+
TimestampUs::from_micros(223)
132+
);
133+
assert_eq!(
134+
TimestampUs::from_micros(u64::MAX - 100).saturating_add(DurationUs::from_micros(200)),
135+
TimestampUs::from_micros(u64::MAX)
136+
);
137+
assert_eq!(
138+
TimestampUs::from_micros(123).saturating_sub(DurationUs::from_micros(100)),
139+
TimestampUs::from_micros(23)
140+
);
141+
assert_eq!(
142+
TimestampUs::from_micros(123).saturating_sub(DurationUs::from_micros(200)),
143+
TimestampUs::from_micros(0)
144+
);
145+
assert_eq!(
146+
TimestampUs::from_micros(123).is_multiple_of(DurationUs::from_micros(200)),
147+
false
148+
);
149+
assert_eq!(
150+
TimestampUs::from_micros(400).is_multiple_of(DurationUs::from_micros(200)),
151+
true
152+
);
153+
assert_eq!(
154+
TimestampUs::from_micros(400).is_multiple_of(DurationUs::from_micros(0)),
155+
false
156+
);
157+
assert_eq!(
158+
TimestampUs::from_micros(400)
159+
.next_multiple_of(DurationUs::from_micros(200))
160+
.unwrap(),
161+
TimestampUs::from_micros(400)
162+
);
163+
assert_eq!(
164+
TimestampUs::from_micros(400)
165+
.previous_multiple_of(DurationUs::from_micros(200))
166+
.unwrap(),
167+
TimestampUs::from_micros(400)
168+
);
169+
assert_eq!(
170+
TimestampUs::from_micros(678)
171+
.next_multiple_of(DurationUs::from_micros(200))
172+
.unwrap(),
173+
TimestampUs::from_micros(800)
174+
);
175+
assert_eq!(
176+
TimestampUs::from_micros(678)
177+
.previous_multiple_of(DurationUs::from_micros(200))
178+
.unwrap(),
179+
TimestampUs::from_micros(600)
180+
);
181+
TimestampUs::from_micros(678)
182+
.previous_multiple_of(DurationUs::from_micros(0))
183+
.unwrap_err();
184+
TimestampUs::from_micros(678)
185+
.next_multiple_of(DurationUs::from_micros(0))
186+
.unwrap_err();
187+
TimestampUs::from_micros(u64::MAX - 5)
188+
.next_multiple_of(DurationUs::from_micros(1000))
189+
.unwrap_err();
190+
}
191+
192+
#[test]
193+
#[allow(clippy::bool_assert_comparison)]
194+
fn duration_ops() {
195+
assert_eq!(
196+
DurationUs::from_micros(400).is_multiple_of(DurationUs::from_micros(200)),
197+
true
198+
);
199+
assert_eq!(
200+
DurationUs::from_micros(400).is_multiple_of(DurationUs::from_micros(300)),
201+
false
202+
);
203+
assert_eq!(
204+
DurationUs::from_micros(400).is_multiple_of(DurationUs::from_micros(0)),
205+
false
206+
);
207+
208+
assert_eq!(
209+
DurationUs::from_micros(123)
210+
.checked_add(DurationUs::from_micros(100))
211+
.unwrap(),
212+
DurationUs::from_micros(223)
213+
);
214+
DurationUs::from_micros(u64::MAX - 5)
215+
.checked_add(DurationUs::from_micros(100))
216+
.unwrap_err();
217+
218+
assert_eq!(
219+
DurationUs::from_micros(123)
220+
.checked_sub(DurationUs::from_micros(100))
221+
.unwrap(),
222+
DurationUs::from_micros(23)
223+
);
224+
DurationUs::from_micros(123)
225+
.checked_sub(DurationUs::from_micros(200))
226+
.unwrap_err();
227+
228+
assert_eq!(
229+
DurationUs::from_micros(123).checked_mul(100).unwrap(),
230+
DurationUs::from_micros(12300)
231+
);
232+
DurationUs::from_micros(u64::MAX - 5)
233+
.checked_mul(100)
234+
.unwrap_err();
235+
assert_eq!(
236+
DurationUs::from_micros(123).checked_div(100).unwrap(),
237+
DurationUs::from_micros(1)
238+
);
239+
assert_eq!(
240+
DurationUs::from_micros(12300).checked_div(100).unwrap(),
241+
DurationUs::from_micros(123)
242+
);
243+
DurationUs::from_micros(123).checked_div(0).unwrap_err();
244+
245+
assert!(DurationUs::ZERO.is_zero());
246+
assert!(!DurationUs::ZERO.is_positive());
247+
248+
assert!(DurationUs::from_micros(5).is_positive());
249+
assert!(!DurationUs::from_micros(5).is_zero());
250+
}
251+
252+
#[test]
253+
fn timestamp_conversions() {
254+
let system_time = SystemTime::UNIX_EPOCH + Duration::from_micros(3_456_789_123_456_789);
255+
let ts = TimestampUs::try_from(system_time).unwrap();
256+
assert_eq!(ts, TimestampUs::from_micros(3_456_789_123_456_789));
257+
assert_eq!(SystemTime::try_from(ts).unwrap(), system_time);
258+
259+
let proto_ts = ProtobufTimestamp::from(ts);
260+
assert_eq!(proto_ts.seconds, 3_456_789_123);
261+
assert_eq!(proto_ts.nanos, 456_789_000);
262+
assert_eq!(TimestampUs::try_from(&proto_ts).unwrap(), ts);
263+
assert_eq!(TimestampUs::try_from(proto_ts).unwrap(), ts);
264+
265+
let chrono_dt: ChronoUtcDateTime = "2079-07-17T03:12:03.456789Z".parse().unwrap();
266+
assert_eq!(ChronoUtcDateTime::try_from(ts).unwrap(), chrono_dt);
267+
assert_eq!(TimestampUs::try_from(chrono_dt).unwrap(), ts);
268+
}
269+
270+
#[test]
271+
fn duration_conversions() {
272+
let duration = DurationUs::from_micros(123_456_789);
273+
let std_duration = Duration::from(duration);
274+
assert_eq!(format!("{std_duration:?}"), "123.456789s");
275+
assert_eq!(DurationUs::try_from(std_duration).unwrap(), duration);
276+
277+
let proto_duration = ProtobufDuration::from(duration);
278+
assert_eq!(proto_duration.seconds, 123);
279+
assert_eq!(proto_duration.nanos, 456_789_000);
280+
assert_eq!(DurationUs::try_from(proto_duration).unwrap(), duration);
281+
}
282+
283+
#[derive(Debug, PartialEq, Deserialize, Serialize)]
284+
struct Test1 {
285+
t1: TimestampUs,
286+
d1: DurationUs,
287+
#[serde(with = "super::duration_us_serde_humantime")]
288+
d2: DurationUs,
289+
}
290+
291+
#[test]
292+
fn time_serde() {
293+
let test1 = Test1 {
294+
t1: TimestampUs::from_micros(123456789),
295+
d1: DurationUs::from_micros(123456789),
296+
d2: DurationUs::from_micros(123456789),
297+
};
298+
299+
let json = serde_json::to_string(&test1).unwrap();
300+
assert_eq!(
301+
json,
302+
r#"{"t1":123456789,"d1":123456789,"d2":"2m 3s 456ms 789us"}"#
303+
);
304+
assert_eq!(serde_json::from_str::<Test1>(&json).unwrap(), test1);
305+
}
306+
307+
#[cfg(feature = "mry")]
308+
#[test]
309+
#[mry::lock(TimestampUs::now)]
310+
fn now_tests() {
311+
use std::sync::atomic::{AtomicU64, Ordering};
312+
use std::sync::Arc;
313+
314+
let now = Arc::new(AtomicU64::new(42));
315+
let now2 = Arc::clone(&now);
316+
TimestampUs::mock_now()
317+
.returns_with(move || TimestampUs::from_micros(now2.load(Ordering::Relaxed)));
318+
319+
assert_eq!(TimestampUs::now().as_micros(), 42);
320+
321+
now.store(45, Ordering::Relaxed);
322+
let s = TimestampUs::now();
323+
now.store(95, Ordering::Relaxed);
324+
assert_eq!(s.elapsed().unwrap(), DurationUs::from_micros(50));
325+
assert_eq!(s.saturating_elapsed(), DurationUs::from_micros(50));
326+
327+
now.store(35, Ordering::Relaxed);
328+
s.elapsed().unwrap_err();
329+
assert_eq!(s.saturating_elapsed(), DurationUs::ZERO);
330+
}

0 commit comments

Comments
 (0)