The TUI suppressed late-reply timing by rebuilding the event with sent_at, rtt, server_timing, one_way and received_stats erased. That leaked stats policy into event construction and, as a side effect, reclassified a matched late reply as an untracked late reply. StatsConfig now carries an explicit LateReplyMode. Measure keeps today's behavior for every existing caller. CountOnly keeps the reply a late unique reply and keeps packet, byte and late counters updating, while suppressing timing, one-way, server-processing and IPDV contributions and reporting contributed_sample == false. Cumulative and rolling snapshots share the policy. Genuinely untracked late replies stay untracked in both modes. The TUI selects CountOnly and passes the original event straight to the collector.
544 lines
19 KiB
Rust
544 lines
19 KiB
Rust
mod common;
|
|
|
|
use std::time::{Duration, Instant, UNIX_EPOCH};
|
|
|
|
use common::{
|
|
adjusted_reply, reply_with_received_stats, sent, ts, unadjusted_late_reply, unadjusted_reply,
|
|
};
|
|
use irtt_client::{
|
|
ClientEvent, ClientTimestamp, OneWayDelaySample, PacketMeta, RttSample, SignedDuration,
|
|
};
|
|
use irtt_stats::{
|
|
EventStatsUpdate, IpdvPairUpdate, LateReplyMode, SampleMode, StatsCollector, StatsConfig,
|
|
};
|
|
|
|
#[test]
|
|
fn running_only_samples_avoid_finite_retention() {
|
|
let mut collector = StatsCollector::new(StatsConfig::continuous());
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
collector.process(&unadjusted_reply(1, 20));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.rtt.primary.median_ns, None);
|
|
assert_eq!(snapshot.rtt.raw.median_ns, None);
|
|
assert_eq!(snapshot.rtt.adjusted.count, 0);
|
|
assert_eq!(snapshot.rtt.adjusted.median_ns, None);
|
|
assert_eq!(snapshot.ipdv.round_trip.median_ns, None);
|
|
assert_eq!(snapshot.one_way_delay.send_delay.median_ns, None);
|
|
assert_eq!(snapshot.one_way_delay.receive_delay.median_ns, None);
|
|
}
|
|
|
|
#[test]
|
|
fn continuous_mode_tracks_running_samples_without_exact_medians() {
|
|
let mut collector = StatsCollector::new(StatsConfig::continuous());
|
|
for seq in 0..4104 {
|
|
collector.process(&unadjusted_reply(seq, 10));
|
|
}
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.rtt.primary.count, 4104);
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 4103);
|
|
assert_eq!(snapshot.rtt.primary.median_ns, None);
|
|
assert_eq!(snapshot.ipdv.round_trip.median_ns, None);
|
|
}
|
|
|
|
#[test]
|
|
fn cumulative_rtt_uses_signed_effective_and_tracks_raw() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&adjusted_reply(0, 1, -2));
|
|
collector.process(&adjusted_reply(1, 10, 8));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.rtt.primary.count, 2);
|
|
assert_eq!(snapshot.rtt.primary.min_ns, Some(-2_000_000));
|
|
assert_eq!(snapshot.rtt.primary.median_ns, Some(3_000_000.0));
|
|
assert_eq!(snapshot.rtt.raw.total_ns, 11_000_000);
|
|
assert_eq!(snapshot.rtt.adjusted.count, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn late_unique_counts_and_duplicates_do_not_update_duplicate_measurements() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&sent(1, ts(10)));
|
|
collector.process(&unadjusted_reply(1, 10));
|
|
collector.process(&unadjusted_late_reply(0, 20));
|
|
collector.process(&ClientEvent::DuplicateReply {
|
|
seq: 0,
|
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
|
received_at: ts(50),
|
|
bytes: 64,
|
|
});
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.packets.packets_sent, 2);
|
|
assert_eq!(snapshot.packets.packets_received, 3);
|
|
assert_eq!(snapshot.packets.unique_replies, 2);
|
|
assert_eq!(snapshot.packets.duplicates, 1);
|
|
assert_eq!(snapshot.packets.late_packets, 1);
|
|
assert_eq!(snapshot.packets.bytes_received, 64 + 64 + 64);
|
|
assert_eq!(snapshot.rtt.primary.count, 2);
|
|
assert_eq!(snapshot.loss.lost_packets, 0);
|
|
assert_eq!(snapshot.loss.duplicate_percent, 100.0 / 3.0);
|
|
}
|
|
|
|
#[test]
|
|
fn final_loss_uses_sent_minus_unique_replies_not_echo_loss_events() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&sent(1, ts(10)));
|
|
collector.process(&ClientEvent::EchoLoss {
|
|
seq: 0,
|
|
sent_at: ts(0),
|
|
timeout_at: Instant::now(),
|
|
});
|
|
collector.process(&unadjusted_late_reply(0, 10));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.events.loss_events, 1);
|
|
assert_eq!(snapshot.packets.unique_replies, 1);
|
|
assert_eq!(snapshot.loss.lost_packets, 1);
|
|
assert_eq!(snapshot.loss.packet_loss_percent, 50.0);
|
|
}
|
|
|
|
fn assert_no_ipdv_pairs(update: &EventStatsUpdate) {
|
|
assert!(update.ipdv_pairs.is_empty(), "{update:?}");
|
|
}
|
|
|
|
fn assert_one_ipdv_pair(
|
|
update: &EventStatsUpdate,
|
|
previous_seq: u32,
|
|
current_seq: u32,
|
|
rtt_ipdv: Duration,
|
|
) -> &IpdvPairUpdate {
|
|
assert_eq!(update.ipdv_pairs.len(), 1, "{update:?}");
|
|
let pair = &update.ipdv_pairs[0];
|
|
assert_eq!(pair.previous_seq, previous_seq);
|
|
assert_eq!(pair.current_seq, current_seq);
|
|
assert_eq!(pair.rtt_ipdv, rtt_ipdv);
|
|
pair
|
|
}
|
|
|
|
fn reply_at(seq: u32, raw_ms: u64, sent_ms: u64) -> ClientEvent {
|
|
let sent_at = ClientTimestamp {
|
|
mono: Instant::now() + Duration::from_millis(sent_ms),
|
|
wall: UNIX_EPOCH + Duration::from_millis(sent_ms),
|
|
};
|
|
let received_at = ClientTimestamp {
|
|
mono: sent_at.mono + Duration::from_millis(raw_ms),
|
|
wall: sent_at.wall + Duration::from_millis(raw_ms),
|
|
};
|
|
let raw = Duration::from_millis(raw_ms);
|
|
ClientEvent::EchoReply {
|
|
seq,
|
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
|
sent_at,
|
|
received_at,
|
|
rtt: RttSample {
|
|
raw,
|
|
adjusted: None,
|
|
effective: SignedDuration::from_duration(raw),
|
|
},
|
|
server_timing: None,
|
|
one_way: None,
|
|
received_stats: None,
|
|
bytes: 64,
|
|
packet_meta: PacketMeta::default(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn ipdv_is_sequence_adjacent_and_gap_preserving() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
let first = collector.process(&unadjusted_reply(0, 10));
|
|
let gap = collector.process(&unadjusted_reply(2, 15));
|
|
let adjacent = collector.process(&unadjusted_reply(3, 12));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert!(first.contributed_sample);
|
|
assert_no_ipdv_pairs(&first);
|
|
|
|
assert!(gap.contributed_sample);
|
|
assert_no_ipdv_pairs(&gap);
|
|
|
|
assert!(adjacent.contributed_sample);
|
|
assert_one_ipdv_pair(&adjacent, 2, 3, Duration::from_millis(3));
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 1);
|
|
assert_eq!(snapshot.ipdv.round_trip.total_ns, 3_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn ipdv_wraparound_sequence_is_adjacent() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
let first = collector.process(&reply_at(u32::MAX, 10, 0));
|
|
let wrapped = collector.process(&reply_at(0, 14, 10));
|
|
|
|
assert!(first.contributed_sample);
|
|
assert_no_ipdv_pairs(&first);
|
|
|
|
assert!(wrapped.contributed_sample);
|
|
assert_one_ipdv_pair(&wrapped, u32::MAX, 0, Duration::from_millis(4));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 1);
|
|
assert_eq!(snapshot.ipdv.round_trip.total_ns, 4_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn ipdv_wraparound_gap_is_preserved() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
let before_wrap = collector.process(&reply_at(u32::MAX - 1, 10, 0));
|
|
let wrapped = collector.process(&reply_at(0, 14, 20));
|
|
|
|
assert!(before_wrap.contributed_sample);
|
|
assert_no_ipdv_pairs(&before_wrap);
|
|
|
|
assert!(wrapped.contributed_sample);
|
|
assert_no_ipdv_pairs(&wrapped);
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn late_reply_can_complete_ipdv_pair() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&unadjusted_reply(1, 20));
|
|
let update = collector.process(&unadjusted_late_reply(0, 10));
|
|
|
|
let snapshot = collector.snapshot();
|
|
|
|
assert!(update.contributed_sample);
|
|
assert_one_ipdv_pair(&update, 0, 1, Duration::from_millis(10));
|
|
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 1);
|
|
assert_eq!(snapshot.ipdv.round_trip.total_ns, 10_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn update_exposes_directional_ipdv_when_available() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
let update = collector.process(&unadjusted_reply(1, 13));
|
|
|
|
assert!(update.contributed_sample);
|
|
|
|
let pair = assert_one_ipdv_pair(&update, 0, 1, Duration::from_millis(3));
|
|
assert!(pair.send_ipdv.is_some());
|
|
assert!(pair.receive_ipdv.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn gap_fill_update_exposes_both_completed_ipdv_pairs() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
|
|
let first = collector.process(&unadjusted_reply(0, 10));
|
|
let gap = collector.process(&unadjusted_reply(2, 20));
|
|
let fill = collector.process(&unadjusted_reply(1, 13));
|
|
|
|
assert!(first.contributed_sample);
|
|
assert!(first.ipdv_pairs.is_empty());
|
|
|
|
assert!(gap.contributed_sample);
|
|
assert!(gap.ipdv_pairs.is_empty());
|
|
|
|
assert!(fill.contributed_sample);
|
|
assert_eq!(fill.ipdv_pairs.len(), 2);
|
|
|
|
assert_eq!(fill.ipdv_pairs[0].previous_seq, 0);
|
|
assert_eq!(fill.ipdv_pairs[0].current_seq, 1);
|
|
assert_eq!(fill.ipdv_pairs[0].rtt_ipdv, Duration::from_millis(3));
|
|
|
|
assert_eq!(fill.ipdv_pairs[1].previous_seq, 1);
|
|
assert_eq!(fill.ipdv_pairs[1].current_seq, 2);
|
|
assert_eq!(fill.ipdv_pairs[1].rtt_ipdv, Duration::from_millis(7));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 2);
|
|
assert_eq!(snapshot.ipdv.round_trip.total_ns, 10_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn server_processing_and_one_way_require_available_samples() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
collector.process(&ClientEvent::LateReply {
|
|
seq: 9,
|
|
highest_seen: 10,
|
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
|
sent_at: None,
|
|
received_at: ts(100),
|
|
rtt: None,
|
|
server_timing: None,
|
|
one_way: None,
|
|
received_stats: None,
|
|
bytes: 64,
|
|
packet_meta: PacketMeta::default(),
|
|
});
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.server_processing.processing.count, 1);
|
|
assert_eq!(snapshot.one_way_delay.send_delay.count, 1);
|
|
assert_eq!(snapshot.events.untracked_late_replies, 1);
|
|
assert_eq!(snapshot.packets.packets_received, 2);
|
|
assert_eq!(snapshot.packets.unique_replies, 1);
|
|
assert_eq!(snapshot.packets.late_packets, 1);
|
|
assert_eq!(snapshot.packets.bytes_received, 128);
|
|
}
|
|
|
|
#[test]
|
|
fn negative_one_way_delay_samples_are_included_in_stats() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
let sent_at = ts(0);
|
|
let received_at = ClientTimestamp {
|
|
mono: sent_at.mono + Duration::from_millis(10),
|
|
wall: sent_at.wall + Duration::from_millis(10),
|
|
};
|
|
|
|
collector.process(&ClientEvent::EchoReply {
|
|
seq: 0,
|
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
|
sent_at,
|
|
received_at,
|
|
rtt: RttSample {
|
|
raw: Duration::from_millis(10),
|
|
adjusted: None,
|
|
effective: SignedDuration::from_nanos(10_000_000),
|
|
},
|
|
server_timing: None,
|
|
one_way: Some(OneWayDelaySample {
|
|
client_to_server: Some(SignedDuration::from_nanos(-1_000_000)),
|
|
server_to_client: Some(SignedDuration::from_nanos(-2_000_000)),
|
|
}),
|
|
received_stats: None,
|
|
bytes: 64,
|
|
packet_meta: PacketMeta::default(),
|
|
});
|
|
|
|
let snapshot = collector.snapshot();
|
|
|
|
assert_eq!(snapshot.one_way_delay.send_delay.count, 1);
|
|
assert_eq!(snapshot.one_way_delay.send_delay.min_ns, Some(-1_000_000));
|
|
assert_eq!(snapshot.one_way_delay.send_delay.total_ns, -1_000_000);
|
|
assert_eq!(snapshot.one_way_delay.receive_delay.count, 1);
|
|
assert_eq!(
|
|
snapshot.one_way_delay.receive_delay.min_ns,
|
|
Some(-2_000_000)
|
|
);
|
|
assert_eq!(snapshot.one_way_delay.receive_delay.total_ns, -2_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn older_cumulative_server_receive_count_does_not_regress() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&unadjusted_reply(1, 10));
|
|
collector.process(&unadjusted_late_reply(0, 10));
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.packets.server_packets_received, Some(2));
|
|
}
|
|
|
|
#[test]
|
|
fn rolling_count_eviction_recomputes_from_bounded_events() {
|
|
let mut collector = StatsCollector::new(StatsConfig {
|
|
samples: SampleMode::RunningOnly,
|
|
rolling_count: Some(2),
|
|
rolling_time: None,
|
|
late_replies: LateReplyMode::Measure,
|
|
});
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
collector.process(&unadjusted_reply(1, 20));
|
|
|
|
let rolling = collector.rolling_count().unwrap();
|
|
assert_eq!(rolling.packets.packets_sent, 0);
|
|
assert_eq!(rolling.packets.unique_replies, 2);
|
|
assert_eq!(rolling.rtt.primary.count, 2);
|
|
}
|
|
|
|
#[test]
|
|
fn rolling_time_eviction_uses_event_timestamps() {
|
|
let mut collector = StatsCollector::new(StatsConfig {
|
|
samples: SampleMode::RunningOnly,
|
|
rolling_count: None,
|
|
rolling_time: Some(Duration::from_millis(15)),
|
|
late_replies: LateReplyMode::Measure,
|
|
});
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&sent(1, ts(10)));
|
|
collector.process(&sent(2, ts(30)));
|
|
|
|
let rolling = collector.rolling_time().unwrap();
|
|
assert_eq!(rolling.packets.packets_sent, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_and_all_lost_edges_are_defined() {
|
|
let empty = StatsCollector::new(StatsConfig::finite()).snapshot();
|
|
assert_eq!(empty.loss.packet_loss_percent, 0.0);
|
|
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&sent(0, ts(0)));
|
|
let all_lost = collector.snapshot();
|
|
assert_eq!(all_lost.loss.lost_packets, 1);
|
|
assert_eq!(all_lost.loss.packet_loss_percent, 100.0);
|
|
}
|
|
|
|
#[test]
|
|
fn receive_window_is_stored_raw_and_never_drives_directional_loss() {
|
|
// Window-only negotiation: the server reports a bitmap but no receive count.
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&reply_with_received_stats(0, 10, None, Some(0x1)));
|
|
|
|
// `0x1` represents no earlier history. It must be preserved verbatim and
|
|
// must not be read as 63 lost packets or stand in for a receive count.
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.packets.server_received_window, Some(0x1));
|
|
assert_eq!(snapshot.packets.server_packets_received, None);
|
|
assert_eq!(snapshot.loss.upstream_loss_packets, None);
|
|
assert_eq!(snapshot.loss.downstream_loss_packets, None);
|
|
assert_eq!(snapshot.loss.upstream_loss_percent, 0.0);
|
|
assert_eq!(snapshot.loss.downstream_loss_percent, 0.0);
|
|
|
|
// An advancing bitmap is stored verbatim across the full 64-bit range.
|
|
collector.process(&sent(1, ts(10)));
|
|
collector.process(&reply_with_received_stats(1, 10, None, Some(u64::MAX)));
|
|
assert_eq!(
|
|
collector.snapshot().packets.server_received_window,
|
|
Some(u64::MAX)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn directional_loss_uses_server_received_count_when_available() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&sent(1, ts(10)));
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
|
|
let loss = collector.snapshot().loss;
|
|
assert_eq!(loss.lost_packets, 1);
|
|
assert_eq!(loss.upstream_loss_packets, Some(1));
|
|
assert_eq!(loss.downstream_loss_packets, Some(0));
|
|
assert_eq!(loss.packet_loss_percent, 50.0);
|
|
assert_eq!(loss.upstream_loss_percent, 50.0);
|
|
}
|
|
|
|
fn count_only(base: StatsConfig) -> StatsConfig {
|
|
StatsConfig {
|
|
late_replies: LateReplyMode::CountOnly,
|
|
..base
|
|
}
|
|
}
|
|
|
|
fn untracked_late_reply(seq: u32, received_ms: u64) -> ClientEvent {
|
|
ClientEvent::LateReply {
|
|
seq,
|
|
highest_seen: seq + 1,
|
|
remote: "127.0.0.1:2112".parse().unwrap(),
|
|
sent_at: None,
|
|
received_at: ts(received_ms),
|
|
rtt: None,
|
|
server_timing: None,
|
|
one_way: None,
|
|
received_stats: None,
|
|
bytes: 64,
|
|
packet_meta: PacketMeta::default(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn matched_late_reply_measures_under_the_default_policy() {
|
|
let mut collector = StatsCollector::new(StatsConfig::finite());
|
|
collector.process(&sent(0, ts(0)));
|
|
let update = collector.process(&unadjusted_late_reply(0, 10));
|
|
|
|
assert!(update.contributed_sample);
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.events.late_unique_replies, 1);
|
|
assert_eq!(snapshot.events.untracked_late_replies, 0);
|
|
assert_eq!(snapshot.packets.unique_replies, 1);
|
|
assert_eq!(snapshot.packets.late_packets, 1);
|
|
assert_eq!(snapshot.packets.packets_received, 1);
|
|
assert_eq!(snapshot.packets.bytes_received, 64);
|
|
assert_eq!(snapshot.rtt.primary.count, 1);
|
|
assert_eq!(snapshot.server_processing.processing.count, 1);
|
|
assert_eq!(snapshot.one_way_delay.send_delay.count, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn matched_late_reply_counts_without_measuring_under_count_only() {
|
|
let mut collector = StatsCollector::new(count_only(StatsConfig::finite()));
|
|
collector.process(&sent(0, ts(0)));
|
|
let update = collector.process(&unadjusted_late_reply(0, 10));
|
|
|
|
assert!(!update.contributed_sample);
|
|
assert!(update.ipdv_pairs.is_empty());
|
|
|
|
let snapshot = collector.snapshot();
|
|
// Still a matched late unique reply, not a reclassified untracked one.
|
|
assert_eq!(snapshot.events.late_unique_replies, 1);
|
|
assert_eq!(snapshot.events.untracked_late_replies, 0);
|
|
assert_eq!(snapshot.packets.unique_replies, 1);
|
|
assert_eq!(snapshot.packets.late_packets, 1);
|
|
assert_eq!(snapshot.packets.packets_received, 1);
|
|
assert_eq!(snapshot.packets.bytes_received, 64);
|
|
// ...but it measures nothing.
|
|
assert_eq!(snapshot.rtt.primary.count, 0);
|
|
assert_eq!(snapshot.rtt.raw.count, 0);
|
|
assert_eq!(snapshot.server_processing.processing.count, 0);
|
|
assert_eq!(snapshot.one_way_delay.send_delay.count, 0);
|
|
assert_eq!(snapshot.one_way_delay.receive_delay.count, 0);
|
|
assert_eq!(snapshot.ipdv.round_trip.count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn count_only_late_replies_do_not_complete_ipdv_pairs() {
|
|
let mut collector = StatsCollector::new(count_only(StatsConfig::finite()));
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
let update = collector.process(&unadjusted_late_reply(1, 13));
|
|
|
|
assert!(update.ipdv_pairs.is_empty());
|
|
assert_eq!(collector.snapshot().ipdv.round_trip.count, 0);
|
|
}
|
|
|
|
#[test]
|
|
fn untracked_late_replies_stay_untracked_in_both_policies() {
|
|
for config in [StatsConfig::finite(), count_only(StatsConfig::finite())] {
|
|
let mut collector = StatsCollector::new(config);
|
|
let update = collector.process(&untracked_late_reply(9, 100));
|
|
|
|
assert!(!update.contributed_sample);
|
|
|
|
let snapshot = collector.snapshot();
|
|
assert_eq!(snapshot.events.untracked_late_replies, 1);
|
|
assert_eq!(snapshot.events.late_unique_replies, 0);
|
|
assert_eq!(snapshot.packets.unique_replies, 0);
|
|
assert_eq!(snapshot.packets.late_packets, 1);
|
|
assert_eq!(snapshot.packets.packets_received, 1);
|
|
assert_eq!(snapshot.rtt.primary.count, 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn rolling_snapshots_use_the_same_late_reply_policy() {
|
|
let mut collector = StatsCollector::new(StatsConfig {
|
|
rolling_count: Some(8),
|
|
rolling_time: None,
|
|
..count_only(StatsConfig::continuous())
|
|
});
|
|
collector.process(&sent(0, ts(0)));
|
|
collector.process(&unadjusted_reply(0, 10));
|
|
collector.process(&unadjusted_late_reply(1, 10));
|
|
|
|
let rolling = collector.rolling_count().unwrap();
|
|
assert_eq!(rolling.events.late_unique_replies, 1);
|
|
assert_eq!(rolling.events.untracked_late_replies, 0);
|
|
assert_eq!(rolling.packets.unique_replies, 2);
|
|
assert_eq!(rolling.packets.late_packets, 1);
|
|
assert_eq!(rolling.rtt.primary.count, 1);
|
|
assert_eq!(rolling.ipdv.round_trip.count, 0);
|
|
}
|