Rust CI / cargo fmt (push) Successful in 17s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 34s
Rust CI / cargo clippy (push) Successful in 35s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 10s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Successful in 1m14s
Rust CI / cargo doc (push) Successful in 23s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 29s
Supply chain / cargo-deny advisories (push) Successful in 9s
Supply chain / cargo-deny bans licenses sources (push) Successful in 5s
Supply chain / actionlint (push) Successful in 3s
Netem Interop / netem interop (push) Successful in 1m11s
113 lines
3.8 KiB
Rust
113 lines
3.8 KiB
Rust
use std::time::{Duration, Instant, UNIX_EPOCH};
|
|
|
|
use irtt_client::{
|
|
ClientEvent, ClientTimestamp, PacketMeta, RttSample, ServerTiming, SignedDuration,
|
|
};
|
|
use irtt_stats::{StatsCollector, StatsConfig};
|
|
|
|
fn reply(
|
|
base: Instant,
|
|
seq: u32,
|
|
send_ms: u64,
|
|
receive_ms: u64,
|
|
timing: ServerTiming,
|
|
) -> ClientEvent {
|
|
let raw = Duration::from_millis(receive_ms - send_ms);
|
|
ClientEvent::EchoReply {
|
|
seq,
|
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
|
sent_at: ClientTimestamp {
|
|
mono: base + Duration::from_millis(send_ms),
|
|
wall: UNIX_EPOCH + Duration::from_millis(200 + u64::from(seq) * 10),
|
|
},
|
|
received_at: ClientTimestamp {
|
|
mono: base + Duration::from_millis(receive_ms),
|
|
wall: UNIX_EPOCH + Duration::from_millis(240 + u64::from(seq) * 20),
|
|
},
|
|
rtt: RttSample {
|
|
raw,
|
|
adjusted: None,
|
|
effective: SignedDuration::from_duration(raw),
|
|
},
|
|
server_timing: Some(timing),
|
|
one_way: None,
|
|
received_stats: None,
|
|
bytes: 64,
|
|
packet_meta: PacketMeta::default(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn directional_ipdv_uses_matching_clock_domains_and_preserves_zero_and_absence() {
|
|
let first = ServerTiming {
|
|
receive_mono_ns: Some(100_000_000),
|
|
send_mono_ns: Some(101_000_000),
|
|
receive_wall_ns: Some(1_000_000_000),
|
|
send_wall_ns: Some(1_001_000_000),
|
|
midpoint_mono_ns: None,
|
|
midpoint_wall_ns: None,
|
|
processing: None,
|
|
};
|
|
let second = ServerTiming {
|
|
receive_mono_ns: Some(105_000_000),
|
|
send_mono_ns: Some(108_000_000),
|
|
receive_wall_ns: Some(1_020_000_000),
|
|
send_wall_ns: Some(1_026_000_000),
|
|
..first
|
|
};
|
|
// Wall and monotonic deltas deliberately disagree: availability alone
|
|
// cannot prove the formulas or which clock domain wins.
|
|
let mut wall_only = second;
|
|
wall_only.receive_mono_ns = None;
|
|
wall_only.send_mono_ns = None;
|
|
let mut no_send = wall_only;
|
|
no_send.receive_wall_ns = None;
|
|
let mut no_receive = wall_only;
|
|
no_receive.send_wall_ns = None;
|
|
let zero = ServerTiming {
|
|
receive_mono_ns: Some(110_000_000),
|
|
send_mono_ns: Some(111_000_000),
|
|
..second
|
|
};
|
|
for (label, current, send_ms, expected_send_ms, expected_receive_ms) in [
|
|
("monotonic preferred", second, 30, Some(5), Some(3)),
|
|
("wall fallback", wall_only, 30, Some(10), Some(5)),
|
|
("missing send endpoint", no_send, 30, None, Some(5)),
|
|
("missing receive endpoint", no_receive, 30, Some(10), None),
|
|
("reordered client send times", second, 10, Some(15), Some(3)),
|
|
("zero variation", zero, 30, Some(0), Some(0)),
|
|
] {
|
|
let base = Instant::now();
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
assert!(collector
|
|
.process(&reply(base, 0, 20, 40, first))
|
|
.ipdv_pairs
|
|
.is_empty());
|
|
let update = collector.process(&reply(base, 1, send_ms, 50, current));
|
|
assert_eq!(update.ipdv_pairs.len(), 1, "{label}");
|
|
let pair = &update.ipdv_pairs[0];
|
|
assert_eq!(
|
|
pair.send_ipdv,
|
|
expected_send_ms.map(Duration::from_millis),
|
|
"{label}"
|
|
);
|
|
assert_eq!(
|
|
pair.receive_ipdv,
|
|
expected_receive_ms.map(Duration::from_millis),
|
|
"{label}"
|
|
);
|
|
let snapshot = collector.snapshot();
|
|
for (expected, actual) in [
|
|
(expected_send_ms, snapshot.ipdv.send),
|
|
(expected_receive_ms, snapshot.ipdv.receive),
|
|
] {
|
|
assert_eq!(actual.count, u64::from(expected.is_some()), "{label}");
|
|
assert_eq!(
|
|
actual.total_ns,
|
|
i128::from(expected.unwrap_or(0)) * 1_000_000,
|
|
"{label}"
|
|
);
|
|
}
|
|
}
|
|
}
|