polkadot_node_clock/mock.rs
1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
16
17//! Deterministic [`Clock`] implementation for tests.
18//!
19//! Stores a single wall-clock instant (in milliseconds since the UNIX epoch) that tests can
20//! advance explicitly via [`MockClock::set_millis`] / [`MockClock::inc`] /
21//! [`MockClock::inc_secs`].
22//!
23//! Note: this mock only virtualises wall-clock reads. `now()` still returns a real
24//! `Instant::now()` and `delay()` still uses a real `futures_timer::Delay`. Those are
25//! sufficient for current subsystem tests, which only assert on wall-clock-derived state. A
26//! follow-up could add fully virtual time (driving `delay` by `inc(_)`) once subsystems route
27//! their internal timers through the shared trait too.
28
29use crate::{BoxedDelay, Clock};
30use std::{
31 sync::{
32 atomic::{AtomicU64, Ordering},
33 Arc,
34 },
35 time::{Duration, Instant},
36};
37
38/// Test clock storing wall-clock milliseconds since the UNIX epoch.
39#[derive(Clone, Default)]
40pub struct MockClock {
41 millis: Arc<AtomicU64>,
42}
43
44impl MockClock {
45 /// Create a new clock fixed at the given wall-clock time in milliseconds since the UNIX
46 /// epoch.
47 pub fn new_at_millis(millis: u64) -> Self {
48 Self { millis: Arc::new(AtomicU64::new(millis)) }
49 }
50
51 /// Create a new clock fixed at the given wall-clock time in seconds since the UNIX epoch.
52 pub fn new_at_secs(secs: u64) -> Self {
53 Self::new_at_millis(secs.saturating_mul(1_000))
54 }
55
56 /// Advance the clock by `dur`.
57 pub fn inc(&self, dur: Duration) {
58 self.millis.fetch_add(dur.as_millis() as u64, Ordering::SeqCst);
59 }
60
61 /// Advance the clock by `secs` seconds.
62 pub fn inc_secs(&self, secs: u64) {
63 self.millis.fetch_add(secs.saturating_mul(1_000), Ordering::SeqCst);
64 }
65
66 /// Set the clock to the given wall-clock time in milliseconds since the UNIX epoch.
67 pub fn set_millis(&self, millis: u64) {
68 self.millis.store(millis, Ordering::SeqCst);
69 }
70
71 /// Set the clock to the given wall-clock time in seconds since the UNIX epoch.
72 pub fn set_secs(&self, secs: u64) {
73 self.set_millis(secs.saturating_mul(1_000));
74 }
75}
76
77impl Clock for MockClock {
78 fn now(&self) -> Instant {
79 Instant::now()
80 }
81
82 fn delay(&self, dur: Duration) -> BoxedDelay {
83 Box::pin(futures_timer::Delay::new(dur))
84 }
85
86 fn duration_since_epoch(&self) -> Duration {
87 Duration::from_millis(self.millis.load(Ordering::SeqCst))
88 }
89}