referrerpolicy=no-referrer-when-downgrade

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}