use std::time::Duration;
use jiff::{SignedDuration, SpanRelativeTo, Timestamp};
use crate::Response;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Delta {
pub(crate) latency: Duration,
pub(crate) delta: SignedDuration,
}
impl Delta {
#[tracing::instrument(level = "trace")]
pub(crate) fn new(response: Response, current: Timestamp) -> Option<Self> {
let latency = (current - response.client)
.to_duration(SpanRelativeTo::days_are_24_hours())
.unwrap()
/ 2;
let local_at_midpoint = response.client + latency;
let delta = (response.server - local_at_midpoint)
.to_duration(SpanRelativeTo::days_are_24_hours())
.unwrap();
tracing::trace!(
?latency,
?local_at_midpoint,
?delta,
"response processing internals"
);
Duration::try_from(latency)
.ok()
.map(|latency| Self { latency, delta })
}
}
#[cfg(test)]
mod tests {
use std::{thread::sleep, time::Duration};
use super::*;
#[test]
fn client_ahead_of_server() {
let client_time = Timestamp::new(0, 300).unwrap();
let server_time = Timestamp::new(0, 200).unwrap();
let round_trip = SignedDuration::from_nanos(600);
let response = Response {
client: client_time,
server: server_time,
};
let processed = Delta::new(response, client_time + round_trip).unwrap();
assert_eq!(processed.latency, Duration::from_nanos(300), "latency");
assert_eq!(processed.delta, SignedDuration::from_nanos(-400), "delta");
}
#[test]
fn client_behind_server() {
let client_time = Timestamp::new(0, 500).unwrap();
let server_time = Timestamp::new(0, 1200).unwrap();
let round_trip = SignedDuration::from_nanos(800);
let response = Response {
client: client_time,
server: server_time,
};
let processed = Delta::new(response, client_time + round_trip).unwrap();
assert_eq!(processed.latency, Duration::from_nanos(400), "latency");
assert_eq!(processed.delta, SignedDuration::from_nanos(300), "delta");
}
#[test]
fn client_equal_server() {
let client_time = Timestamp::new(0, 500).unwrap();
let server_time = Timestamp::new(0, 700).unwrap();
let round_trip = SignedDuration::from_nanos(400);
let response = Response {
client: client_time,
server: server_time,
};
let processed = Delta::new(response, client_time + round_trip).unwrap();
assert_eq!(processed.latency, Duration::from_nanos(200), "latency");
assert_eq!(processed.delta, SignedDuration::from_nanos(0), "delta");
}
#[test]
fn clock_went_backwards() {
let sent_time = Timestamp::new(0, 500).unwrap();
let server_time = Timestamp::new(0, 700).unwrap();
let arrive_time = Timestamp::new(0, 200).unwrap();
let response = Response {
client: sent_time,
server: server_time,
};
let proc = Delta::new(response, arrive_time);
assert!(proc.is_none(), "{proc:?}");
}
#[test]
fn with_sleep() {
let sent_time = Timestamp::now();
sleep(Duration::from_millis(10));
let server_time = Timestamp::now();
sleep(Duration::from_millis(10));
let arrive_time = Timestamp::now();
let response = Response {
client: sent_time,
server: server_time,
};
let processed = Delta::new(response, arrive_time).unwrap();
if cfg!(target_os = "linux") {
assert!(
processed.latency > Duration::from_millis(9)
&& processed.latency < Duration::from_millis(11),
"latency {:?}",
processed.latency
);
assert!(
processed.delta >= SignedDuration::from_micros(-100)
&& processed.delta <= SignedDuration::from_micros(100),
"delta {:?}",
processed.delta
);
} else {
assert!(
processed.latency > Duration::from_millis(1)
&& processed.latency < Duration::from_millis(100),
"latency {:?}",
processed.latency
);
assert!(
processed.delta >= SignedDuration::from_millis(-20)
&& processed.delta <= SignedDuration::from_millis(20),
"delta {:?}",
processed.delta
);
}
}
}