1use std::sync::{Arc, Mutex};
2
3use rtime_core::clock::{Clock, ClockError};
4use rtime_core::timestamp::{NtpDuration, NtpTimestamp};
5
6#[derive(Clone)]
9pub struct MockClock {
10 state: Arc<Mutex<MockClockState>>,
11}
12
13struct MockClockState {
14 current_time: NtpTimestamp,
15 frequency_ppm: f64,
16 adjustable: bool,
17}
18
19impl MockClock {
20 pub fn new(initial_time: NtpTimestamp) -> Self {
21 Self {
22 state: Arc::new(Mutex::new(MockClockState {
23 current_time: initial_time,
24 frequency_ppm: 0.0,
25 adjustable: true,
26 })),
27 }
28 }
29
30 pub fn set_time(&self, time: NtpTimestamp) {
32 self.state.lock().unwrap().current_time = time;
33 }
34
35 pub fn advance(&self, nanos: i64) {
37 let mut state = self.state.lock().unwrap();
38 let raw = state.current_time.raw() as i128;
39 let ntp_delta = (nanos as i128) * (1i128 << 32) / 1_000_000_000;
41 state.current_time = NtpTimestamp::from_raw((raw + ntp_delta) as u64);
42 }
43
44 pub fn set_adjustable(&self, adjustable: bool) {
46 self.state.lock().unwrap().adjustable = adjustable;
47 }
48
49 pub fn get_frequency(&self) -> f64 {
51 self.state.lock().unwrap().frequency_ppm
52 }
53}
54
55impl Clock for MockClock {
56 fn now(&self) -> Result<NtpTimestamp, ClockError> {
57 Ok(self.state.lock().unwrap().current_time)
58 }
59
60 fn step(&self, offset: NtpDuration) -> Result<(), ClockError> {
61 let mut state = self.state.lock().unwrap();
62 if !state.adjustable {
63 return Err(ClockError::PermissionDenied);
64 }
65 let raw = state.current_time.raw() as i128;
66 let ntp_delta = (offset.to_nanos() as i128) * (1i128 << 32) / 1_000_000_000;
67 state.current_time = NtpTimestamp::from_raw((raw + ntp_delta) as u64);
68 Ok(())
69 }
70
71 fn adjust_frequency(&self, ppm: f64) -> Result<(), ClockError> {
72 let mut state = self.state.lock().unwrap();
73 if !state.adjustable {
74 return Err(ClockError::PermissionDenied);
75 }
76 state.frequency_ppm = ppm;
77 Ok(())
78 }
79
80 fn frequency_offset(&self) -> Result<f64, ClockError> {
81 Ok(self.state.lock().unwrap().frequency_ppm)
82 }
83
84 fn resolution(&self) -> NtpDuration {
85 NtpDuration::from_nanos(1)
86 }
87
88 fn max_frequency_adjustment(&self) -> f64 {
89 500.0
90 }
91
92 fn is_adjustable(&self) -> bool {
93 self.state.lock().unwrap().adjustable
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn mock_clock_basic() {
103 let clock = MockClock::new(NtpTimestamp::new(1000, 0));
104 let t = clock.now().unwrap();
105 assert_eq!(t.seconds(), 1000);
106 }
107
108 #[test]
109 fn mock_clock_advance() {
110 let clock = MockClock::new(NtpTimestamp::new(1000, 0));
111 clock.advance(1_000_000_000); let t = clock.now().unwrap();
113 assert_eq!(t.seconds(), 1001);
114 }
115
116 #[test]
117 fn mock_clock_step() {
118 let clock = MockClock::new(NtpTimestamp::new(1000, 0));
119 clock.step(NtpDuration::from_nanos(2_000_000_000)).unwrap();
120 let t = clock.now().unwrap();
121 assert_eq!(t.seconds(), 1002);
122 }
123
124 #[test]
125 fn mock_clock_not_adjustable() {
126 let clock = MockClock::new(NtpTimestamp::new(1000, 0));
127 clock.set_adjustable(false);
128 assert!(!clock.is_adjustable());
129 assert!(clock.step(NtpDuration::from_nanos(1000)).is_err());
130 assert!(clock.adjust_frequency(1.0).is_err());
131 }
132
133 #[test]
134 fn mock_clock_frequency() {
135 let clock = MockClock::new(NtpTimestamp::new(1000, 0));
136 clock.adjust_frequency(10.5).unwrap();
137 assert!((clock.frequency_offset().unwrap() - 10.5).abs() < f64::EPSILON);
138 }
139}