tests: clean up more tests across the project
Rust CI / cargo fmt (push) Successful in 17s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 33s
Rust CI / cargo clippy (push) Successful in 34s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Failing after 33s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 9s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo doc (push) Successful in 22s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 27s
Supply chain / cargo-deny advisories (push) Successful in 7s
Supply chain / cargo-deny bans licenses sources (push) Successful in 6s
Supply chain / actionlint (push) Successful in 3s
Netem Interop / netem interop (push) Successful in 1m3s
Rust CI / cargo fmt (push) Successful in 17s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 33s
Rust CI / cargo clippy (push) Successful in 34s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Failing after 33s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Failing after 9s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Failing after 8s
Rust CI / cargo doc (push) Successful in 22s
Fuzz Smoke / cargo fuzz smoke (push) Failing after 27s
Supply chain / cargo-deny advisories (push) Successful in 7s
Supply chain / cargo-deny bans licenses sources (push) Successful in 6s
Supply chain / actionlint (push) Successful in 3s
Netem Interop / netem interop (push) Successful in 1m3s
This commit is contained in:
17 files changed
+28
-1435
No files matched your search
@@ -1,74 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn params_from_config_maps_compatibility_fields() {
|
||||
let config = ClientConfig {
|
||||
duration: Some(Duration::from_secs(5)),
|
||||
interval: Duration::from_millis(250),
|
||||
length: 1472,
|
||||
received_stats: ReceivedStats::Window,
|
||||
stamp_at: StampAt::Midpoint,
|
||||
clock: Clock::Wall,
|
||||
dscp: 46,
|
||||
hmac_key: Some(b"secret".to_vec()),
|
||||
server_fill: Some("rand".to_owned()),
|
||||
..ClientConfig::default()
|
||||
};
|
||||
|
||||
let params = params_from_config(&config).unwrap();
|
||||
assert_eq!(params.protocol_version, PROTOCOL_VERSION);
|
||||
assert_eq!(params.duration_ns, 5_000_000_000);
|
||||
assert_eq!(params.interval_ns, 250_000_000);
|
||||
assert_eq!(params.length, 1472);
|
||||
assert_eq!(params.received_stats, ReceivedStats::Window);
|
||||
assert_eq!(params.stamp_at, StampAt::Midpoint);
|
||||
assert_eq!(params.clock, Clock::Wall);
|
||||
assert_eq!(
|
||||
params.dscp, 184,
|
||||
"config DSCP codepoint 46 must be shifted into raw wire byte 184"
|
||||
);
|
||||
assert_eq!(
|
||||
params.server_fill.as_ref().map(|fill| fill.value.as_str()),
|
||||
Some("rand")
|
||||
);
|
||||
assert_eq!(config.hmac_key.as_deref(), Some(b"secret".as_slice()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn params_from_config_accepts_boundary_values() {
|
||||
for length in [0, 1, 1472, 4096, MAX_UDP_PAYLOAD_LENGTH] {
|
||||
let config = ClientConfig {
|
||||
length,
|
||||
..ClientConfig::default()
|
||||
};
|
||||
assert_eq!(
|
||||
params_from_config(&config).unwrap().length,
|
||||
i64::from(length)
|
||||
);
|
||||
}
|
||||
|
||||
let config = ClientConfig {
|
||||
dscp: 63,
|
||||
..ClientConfig::default()
|
||||
};
|
||||
assert_eq!(
|
||||
params_from_config(&config).unwrap().dscp,
|
||||
252,
|
||||
"boundary codepoint 63 must be shifted into raw wire byte 252"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn params_from_config_encodes_continuous_duration_as_zero() {
|
||||
let config = ClientConfig {
|
||||
duration: None,
|
||||
..ClientConfig::default()
|
||||
};
|
||||
assert_eq!(params_from_config(&config).unwrap().duration_ns, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn params_from_config_rejects_invalid_values() {
|
||||
fn connect_rejects_invalid_configuration() {
|
||||
let i64_max_ns = u64::try_from(i64::MAX).unwrap();
|
||||
let too_large = Duration::from_nanos(i64_max_ns) + Duration::from_nanos(1);
|
||||
let cases = [
|
||||
@@ -151,10 +84,10 @@ fn params_from_config_rejects_invalid_values() {
|
||||
for (name, config, expected_reason) in cases {
|
||||
assert!(
|
||||
matches!(
|
||||
params_from_config(&config),
|
||||
Err(ClientError::InvalidConfig { reason }) if reason.contains(expected_reason)
|
||||
Client::connect(config),
|
||||
Err(ClientError::InvalidConfig { .. })
|
||||
),
|
||||
"{name} should fail with InvalidConfig containing {expected_reason:?}"
|
||||
"{name} should fail with InvalidConfig ({expected_reason})"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::{
|
||||
future::{poll_fn, Future},
|
||||
mem,
|
||||
net::{SocketAddr, UdpSocket},
|
||||
pin::Pin,
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
@@ -16,7 +15,7 @@ use irtt_proto::{
|
||||
use tokio::runtime::{Builder, Runtime};
|
||||
|
||||
use super::*;
|
||||
use crate::{ClientAuthConfig, NegotiationPolicy, RunMode};
|
||||
use crate::{ClientAuthConfig, RunMode};
|
||||
|
||||
const TOKEN: u64 = 0x1234_5678_90ab_cdef;
|
||||
|
||||
@@ -766,52 +765,6 @@ fn terminal_targets_count_toward_retained_generation_limit() {
|
||||
assert!(task.snapshot().targets.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pruning_leaves_no_stale_target_work() {
|
||||
let (mut task, _) = ManagedClient::task(
|
||||
config(ManagedPacing::Staggered),
|
||||
vec![
|
||||
ManagedTargetConfig::new("one", "127.0.0.1:9"),
|
||||
ManagedTargetConfig::new("two", "127.0.0.1:10"),
|
||||
ManagedTargetConfig::new("three", "127.0.0.1:11"),
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
task.cursor = 2;
|
||||
task.timeout_cursor = 2;
|
||||
task.send_cursor = 2;
|
||||
task.scan_remaining = 3;
|
||||
task.burst_remaining = 3;
|
||||
task.stagger_remaining = 3;
|
||||
|
||||
task.targets[2].desired = false;
|
||||
task.targets[2].state = TargetState::Terminal;
|
||||
task.prune_undesired_terminal();
|
||||
|
||||
// Timeout discovery indexes `targets` with `timeout_cursor` directly, so a
|
||||
// prune must leave it safe to use; the target and send passes modulo their
|
||||
// own cursors. Pending per-pass work must not outlive the targets it was
|
||||
// scheduled for either.
|
||||
assert_eq!(task.targets.len(), 2);
|
||||
assert!(task.timeout_cursor < task.targets.len());
|
||||
assert!(task.scan_remaining <= task.targets.len());
|
||||
assert!(task.burst_remaining <= task.targets.len());
|
||||
assert!(task.stagger_remaining <= task.targets.len());
|
||||
assert!(!task.poll_timeout_pass(Instant::now()));
|
||||
|
||||
for target in &mut task.targets {
|
||||
target.desired = false;
|
||||
target.state = TargetState::Terminal;
|
||||
}
|
||||
task.prune_undesired_terminal();
|
||||
|
||||
assert!(task.targets.is_empty());
|
||||
assert_eq!(task.scan_remaining, 0);
|
||||
assert_eq!(task.burst_remaining, 0);
|
||||
assert_eq!(task.stagger_remaining, 0);
|
||||
assert!(!task.poll_timeout_pass(Instant::now()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dynamic_rejections_leave_transaction_state_unchanged() {
|
||||
let mut managed = config(ManagedPacing::Staggered);
|
||||
@@ -1562,79 +1515,6 @@ fn staggered_pacing_fairness() {
|
||||
second.finish();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stagger_gate_tracks_active_membership() {
|
||||
let first =
|
||||
start_server_negotiating(ServerBehavior::Echo, None, Some(Duration::from_millis(100)));
|
||||
let second_gate = Arc::new(PacketGate::default());
|
||||
let second = start_server_with_gates(
|
||||
ServerBehavior::Echo,
|
||||
None,
|
||||
Some(Duration::from_secs(1)),
|
||||
Some(Arc::clone(&second_gate)),
|
||||
None,
|
||||
);
|
||||
let mut managed = config(ManagedPacing::Staggered);
|
||||
managed.client.duration = Some(Duration::from_millis(1_250));
|
||||
managed.client.interval = Duration::from_secs(1);
|
||||
managed.client.negotiation_policy = NegotiationPolicy::Loose;
|
||||
managed.client.open_timeouts = vec![Duration::from_secs(2)];
|
||||
let (task, handle) = ManagedClient::task(
|
||||
managed,
|
||||
vec![target("first", first.addr), target("second", second.addr)],
|
||||
)
|
||||
.unwrap();
|
||||
let runtime = runtime();
|
||||
let mut task = Box::pin(task);
|
||||
drive_task_until(&runtime, &mut task, |task| {
|
||||
task.targets[0].counters.packets_sent == 1
|
||||
});
|
||||
let one_target_last = task.last_stagger_send.unwrap();
|
||||
let one_target_gate = task.send_gate.unwrap();
|
||||
assert_eq!(
|
||||
task.send_gate,
|
||||
one_target_last.checked_add(Duration::from_millis(100))
|
||||
);
|
||||
second_gate.release();
|
||||
drive_task_until(&runtime, &mut task, |task| task.active_count() == 2);
|
||||
let last = task.last_stagger_send.unwrap();
|
||||
let gate = task.send_gate.unwrap();
|
||||
assert_eq!(gate, last.checked_add(Duration::from_millis(50)).unwrap());
|
||||
assert!(gate <= one_target_gate);
|
||||
assert!(gate > last && gate < last.checked_add(Duration::from_millis(100)).unwrap());
|
||||
drive_task_until(&runtime, &mut task, |task| {
|
||||
task.targets[1].counters.packets_sent == 1
|
||||
});
|
||||
|
||||
let accepted_at = Instant::now();
|
||||
task.record_stagger_acceptance(
|
||||
SendResult::Failed { accepted: true },
|
||||
Some(Duration::from_millis(50)),
|
||||
accepted_at,
|
||||
);
|
||||
assert_eq!(
|
||||
task.send_gate,
|
||||
accepted_at.checked_add(Duration::from_millis(50))
|
||||
);
|
||||
let preserved_gate = task.send_gate;
|
||||
let TargetState::Active { client } =
|
||||
mem::replace(&mut task.targets[0].state, TargetState::Terminal)
|
||||
else {
|
||||
panic!("fast target was not active before removal");
|
||||
};
|
||||
assert!(task.begin_drain(0, client, ManagedTargetEndReason::Stopped, Instant::now()));
|
||||
assert_eq!(task.send_gate, preserved_gate);
|
||||
drive_task_until(&runtime, &mut task, |task| {
|
||||
task.targets[1].counters.packets_sent == 2
|
||||
});
|
||||
|
||||
drop(handle.stop());
|
||||
let outcome = runtime.block_on(task);
|
||||
assert_eq!(outcome.failed_target_outcomes, 0);
|
||||
first.finish();
|
||||
second.finish();
|
||||
}
|
||||
|
||||
/// The stagger gate must not fold each send's observed wakeup latency into
|
||||
/// every later slot.
|
||||
///
|
||||
|
||||
@@ -1177,10 +1177,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn connected_machine(config: ClientConfig) -> SessionMachine {
|
||||
SessionMachine::new(config, "127.0.0.1:2112".parse().unwrap()).unwrap()
|
||||
}
|
||||
|
||||
fn normal_open_reply(machine: &SessionMachine, token: u64) -> OpenReply {
|
||||
OpenReply {
|
||||
flags: flags::FLAG_OPEN | flags::FLAG_REPLY,
|
||||
@@ -1189,210 +1185,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn encoded_open_reply(
|
||||
machine: &SessionMachine,
|
||||
reply: &OpenReply,
|
||||
) -> Result<Vec<u8>, irtt_proto::ProtoError> {
|
||||
irtt_proto::encode_open_reply(reply, machine.config.hmac_key.as_deref())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_wrong_direction_and_invalid_flags_are_ignored() {
|
||||
let machine = connected_machine(ClientConfig::default());
|
||||
assert!(matches!(
|
||||
machine.inspect_open_datagram(&[0_u8]),
|
||||
Ok(OpenDatagramDisposition::Ignore)
|
||||
));
|
||||
|
||||
let request = machine.prepare_open_request().unwrap();
|
||||
assert!(matches!(
|
||||
machine.inspect_open_datagram(&request.bytes),
|
||||
Ok(OpenDatagramDisposition::Ignore)
|
||||
));
|
||||
|
||||
let reply = normal_open_reply(&machine, 0x1020_3040_5060_7080);
|
||||
let mut reserved = encoded_open_reply(&machine, &reply).unwrap();
|
||||
reserved[3] |= 0x10;
|
||||
assert!(matches!(
|
||||
machine.inspect_open_datagram(&reserved),
|
||||
Ok(OpenDatagramDisposition::Ignore)
|
||||
));
|
||||
assert!(matches!(machine.state, MachineState::Connected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_unexpected_and_bad_hmac_are_ignored() {
|
||||
let key = b"open-key".to_vec();
|
||||
let authenticated = connected_machine(ClientConfig {
|
||||
hmac_key: Some(key.clone()),
|
||||
..ClientConfig::default()
|
||||
});
|
||||
let reply = normal_open_reply(&authenticated, 0x1020_3040_5060_7080);
|
||||
let plain = irtt_proto::encode_open_reply(&reply, None).unwrap();
|
||||
assert!(matches!(
|
||||
authenticated.inspect_open_datagram(&plain),
|
||||
Ok(OpenDatagramDisposition::Ignore)
|
||||
));
|
||||
|
||||
let mut bad = encoded_open_reply(&authenticated, &reply).unwrap();
|
||||
*bad.last_mut().unwrap() ^= 0x80;
|
||||
assert!(matches!(
|
||||
authenticated.inspect_open_datagram(&bad),
|
||||
Ok(OpenDatagramDisposition::Ignore)
|
||||
));
|
||||
|
||||
let plain_machine = connected_machine(ClientConfig::default());
|
||||
let unexpected = irtt_proto::encode_open_reply(&reply, Some(&key)).unwrap();
|
||||
assert!(matches!(
|
||||
plain_machine.inspect_open_datagram(&unexpected),
|
||||
Ok(OpenDatagramDisposition::Ignore)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authenticated_zero_token_is_trusted_and_terminal() {
|
||||
let key = b"open-key".to_vec();
|
||||
let machine = connected_machine(ClientConfig {
|
||||
hmac_key: Some(key.clone()),
|
||||
..ClientConfig::default()
|
||||
});
|
||||
let reply = normal_open_reply(&machine, 0x1020_3040_5060_7080);
|
||||
let mut packet = encoded_open_reply(&machine, &reply).unwrap();
|
||||
let token_offset = 4 + irtt_proto::HMAC_SIZE;
|
||||
packet[token_offset..token_offset + 8].fill(0);
|
||||
irtt_proto::compute_hmac_in_place(&key, &mut packet, 4).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
machine.inspect_open_datagram(&packet),
|
||||
Err(ClientError::Protocol(irtt_proto::ProtoError::ZeroToken))
|
||||
));
|
||||
assert!(matches!(machine.state, MachineState::Connected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_invalid_parameter_encoding_is_terminal() {
|
||||
let machine = connected_machine(ClientConfig::default());
|
||||
let mut packet = irtt_proto::MAGIC.to_vec();
|
||||
packet.push(flags::FLAG_OPEN | flags::FLAG_REPLY);
|
||||
packet.extend_from_slice(&0x1020_3040_5060_7080_u64.to_le_bytes());
|
||||
packet.push(0x80);
|
||||
|
||||
assert!(matches!(
|
||||
machine.inspect_open_datagram(&packet),
|
||||
Err(ClientError::Protocol(
|
||||
irtt_proto::ProtoError::TruncatedVarint
|
||||
))
|
||||
));
|
||||
assert!(matches!(machine.state, MachineState::Connected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_rejection_version_and_run_mode_failures_do_not_open() {
|
||||
let machine = connected_machine(ClientConfig::default());
|
||||
let rejection = OpenReply {
|
||||
flags: flags::FLAG_OPEN | flags::FLAG_REPLY | flags::FLAG_CLOSE,
|
||||
token: 0,
|
||||
params: machine.requested.clone(),
|
||||
};
|
||||
assert!(matches!(
|
||||
machine
|
||||
.prepare_open_acceptance(rejection, timestamp(Instant::now()))
|
||||
.unwrap_err()
|
||||
.primary,
|
||||
ClientError::ServerRejected
|
||||
));
|
||||
|
||||
let mut version = normal_open_reply(&machine, 0x1020_3040_5060_7080);
|
||||
version.params.protocol_version += 1;
|
||||
assert!(matches!(
|
||||
machine
|
||||
.prepare_open_acceptance(version, timestamp(Instant::now()))
|
||||
.unwrap_err()
|
||||
.primary,
|
||||
ClientError::ProtocolVersionMismatch { .. }
|
||||
));
|
||||
|
||||
let no_test = connected_machine(ClientConfig {
|
||||
run_mode: RunMode::NoTest,
|
||||
..ClientConfig::default()
|
||||
});
|
||||
assert!(matches!(
|
||||
no_test
|
||||
.prepare_open_acceptance(
|
||||
normal_open_reply(&no_test, 0x1020_3040_5060_7080),
|
||||
timestamp(Instant::now()),
|
||||
)
|
||||
.unwrap_err()
|
||||
.primary,
|
||||
ClientError::UnexpectedNoTestReply
|
||||
));
|
||||
assert!(matches!(machine.state, MachineState::Connected));
|
||||
assert!(matches!(no_test.state, MachineState::Connected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_test_non_close_reply_prepares_authenticated_cleanup_without_state_change() {
|
||||
let key = b"cleanup-key".to_vec();
|
||||
let machine = connected_machine(ClientConfig {
|
||||
run_mode: RunMode::NoTest,
|
||||
hmac_key: Some(key.clone()),
|
||||
..ClientConfig::default()
|
||||
});
|
||||
let token = 0x1020_3040_5060_7080;
|
||||
|
||||
let failure = machine
|
||||
.prepare_open_acceptance(
|
||||
normal_open_reply(&machine, token),
|
||||
timestamp(Instant::now()),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert!(matches!(
|
||||
failure.primary,
|
||||
ClientError::UnexpectedNoTestReply
|
||||
));
|
||||
let cleanup = failure.cleanup_close.as_deref().unwrap();
|
||||
assert_eq!(cleanup[3], flags::FLAG_CLOSE | flags::FLAG_HMAC);
|
||||
irtt_proto::verify_hmac(&key, cleanup, 4).unwrap();
|
||||
assert_eq!(
|
||||
u64::from_le_bytes(
|
||||
cleanup[4 + irtt_proto::HMAC_SIZE..12 + irtt_proto::HMAC_SIZE]
|
||||
.try_into()
|
||||
.unwrap()
|
||||
),
|
||||
token
|
||||
);
|
||||
drop(failure);
|
||||
assert!(matches!(machine.state, MachineState::Connected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_test_close_replies_do_not_prepare_cleanup() {
|
||||
let machine = connected_machine(ClientConfig {
|
||||
run_mode: RunMode::NoTest,
|
||||
..ClientConfig::default()
|
||||
});
|
||||
let reply = |token| OpenReply {
|
||||
flags: flags::FLAG_OPEN | flags::FLAG_REPLY | flags::FLAG_CLOSE,
|
||||
token,
|
||||
params: machine.requested.clone(),
|
||||
};
|
||||
|
||||
let prepared = machine
|
||||
.prepare_open_acceptance(reply(0), timestamp(Instant::now()))
|
||||
.unwrap();
|
||||
assert!(prepared.cleanup_close_packet().is_none());
|
||||
|
||||
let failure = machine
|
||||
.prepare_open_acceptance(reply(0x1020_3040_5060_7080), timestamp(Instant::now()))
|
||||
.unwrap_err();
|
||||
assert!(matches!(
|
||||
failure.primary,
|
||||
ClientError::NonZeroNoTestToken { .. }
|
||||
));
|
||||
assert!(failure.cleanup_close.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uncommitted_probe_preparation_changes_no_authoritative_state() {
|
||||
let mut machine = open_machine(4, Duration::from_secs(1));
|
||||
@@ -1417,30 +1209,6 @@ mod tests {
|
||||
assert_eq!(session.pending.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stale_prepared_probe_is_rejected_without_changing_state() {
|
||||
let mut machine = open_machine(4, Duration::from_secs(1));
|
||||
let stale = machine.prepare_probe().unwrap();
|
||||
let accepted = machine.prepare_probe().unwrap();
|
||||
let sent_at = timestamp(Instant::now());
|
||||
let preflight = machine.preflight_probe_commit(&accepted).unwrap();
|
||||
let commit = machine.finalize_probe_commit(preflight, sent_at).unwrap();
|
||||
machine.commit_probe_sent(commit, sent_at, accepted.bytes.len());
|
||||
|
||||
assert!(matches!(
|
||||
machine.preflight_probe_commit(&stale),
|
||||
Err(ClientError::StalePreparedProbe {
|
||||
prepared_seq: 0,
|
||||
next_wire_seq: 1,
|
||||
})
|
||||
));
|
||||
|
||||
let session = active(&machine);
|
||||
assert_eq!(session.next_wire_seq, 1);
|
||||
assert_eq!(session.packets_sent, 1);
|
||||
assert_eq!(session.pending.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_would_block_style_preflight_commits_once() {
|
||||
let mut machine = open_machine(4, Duration::from_secs(1));
|
||||
@@ -1462,23 +1230,6 @@ mod tests {
|
||||
assert_eq!(session.pending.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pending_capacity_exhaustion_is_detected_before_commit() {
|
||||
let mut machine = open_machine(1, Duration::from_secs(1));
|
||||
let first = machine.prepare_probe().unwrap();
|
||||
let sent_at = timestamp(Instant::now());
|
||||
let preflight = machine.preflight_probe_commit(&first).unwrap();
|
||||
let commit = machine.finalize_probe_commit(preflight, sent_at).unwrap();
|
||||
machine.commit_probe_sent(commit, sent_at, first.bytes.len());
|
||||
|
||||
let second = machine.prepare_probe().unwrap();
|
||||
assert!(matches!(
|
||||
machine.preflight_probe_commit(&second),
|
||||
Err(ClientError::PendingLimitExceeded { limit: 1 })
|
||||
));
|
||||
assert_eq!(active(&machine).packets_sent, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pending_sequence_collision_is_detected_before_commit() {
|
||||
let mut machine = open_machine(2, Duration::from_secs(1));
|
||||
@@ -1525,35 +1276,6 @@ mod tests {
|
||||
assert_eq!(active(&machine).pending.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exhaustive_timeout_polling_returns_every_due_loss() {
|
||||
let mut machine = open_machine(4, Duration::from_secs(1));
|
||||
let now = Instant::now();
|
||||
for seq in [2, 1, 0] {
|
||||
let sent_at = timestamp(now - Duration::from_secs(u64::from(seq) + 1));
|
||||
let session = active_mut(&mut machine);
|
||||
session.pending.preflight_insert(seq).unwrap();
|
||||
session.pending.commit_insert(PendingProbe {
|
||||
wire_seq: seq,
|
||||
sent_at,
|
||||
timeout_at: sent_at.mono + Duration::from_secs(1),
|
||||
tx_not_before_wall: sent_at.wall,
|
||||
kernel_tx_timestamp: None,
|
||||
});
|
||||
}
|
||||
|
||||
let events = machine.poll_timeouts_at(now).unwrap();
|
||||
assert!(matches!(
|
||||
events.as_slice(),
|
||||
[
|
||||
ClientEvent::EchoLoss { seq: 2, .. },
|
||||
ClientEvent::EchoLoss { seq: 1, .. },
|
||||
ClientEvent::EchoLoss { seq: 0, .. },
|
||||
]
|
||||
));
|
||||
assert_eq!(active(&machine).pending.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapping_sequence_from_max_to_zero_remains_valid() {
|
||||
let mut machine = open_machine(2, Duration::from_secs(1));
|
||||
|
||||
@@ -396,73 +396,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_rejects_changed_negotiated_fields() {
|
||||
let requested = default_params();
|
||||
|
||||
let mut returned = requested.clone();
|
||||
returned.length = 128;
|
||||
assert_eq!(
|
||||
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
|
||||
NegotiationRestriction::LengthReduced {
|
||||
requested: requested.length,
|
||||
negotiated: returned.length,
|
||||
}
|
||||
.message()
|
||||
);
|
||||
|
||||
let mut returned = requested.clone();
|
||||
returned.dscp = 0;
|
||||
assert_eq!(
|
||||
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
|
||||
NegotiationRestriction::DscpChanged {
|
||||
requested: requested.dscp >> 2,
|
||||
negotiated: returned.dscp >> 2,
|
||||
}
|
||||
.message()
|
||||
);
|
||||
|
||||
let mut returned = requested.clone();
|
||||
returned.received_stats = ReceivedStats::Count;
|
||||
assert_eq!(
|
||||
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
|
||||
NegotiationRestriction::ReceivedStatsChanged {
|
||||
requested: requested.received_stats,
|
||||
negotiated: returned.received_stats,
|
||||
}
|
||||
.message()
|
||||
);
|
||||
|
||||
let mut returned = requested.clone();
|
||||
returned.stamp_at = StampAt::Midpoint;
|
||||
assert_eq!(
|
||||
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
|
||||
NegotiationRestriction::StampAtChanged {
|
||||
requested: requested.stamp_at,
|
||||
negotiated: returned.stamp_at,
|
||||
}
|
||||
.message()
|
||||
);
|
||||
|
||||
let mut returned = requested.clone();
|
||||
returned.clock = Clock::Wall;
|
||||
assert_eq!(
|
||||
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
|
||||
NegotiationRestriction::ClockChanged {
|
||||
requested: requested.clock,
|
||||
negotiated: returned.clock,
|
||||
}
|
||||
.message()
|
||||
);
|
||||
|
||||
let mut returned = requested.clone();
|
||||
returned.server_fill = None;
|
||||
assert_eq!(
|
||||
rejection_reason(&requested, &returned, NegotiationPolicy::Strict),
|
||||
NegotiationRestriction::ServerFillChanged.message()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loose_duration_negotiation_uses_run_duration_semantics() {
|
||||
let requested = default_params();
|
||||
|
||||
@@ -174,25 +174,6 @@ mod tests {
|
||||
assert_eq!(normalize_server_addr("[::1]:1234"), "[::1]:1234");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn address_family_filtering_remains_unchanged() {
|
||||
let ipv4 = SocketAddr::from(([127, 0, 0, 1], DEFAULT_PORT));
|
||||
let ipv6 = SocketAddr::from(([0, 0, 0, 0, 0, 0, 0, 1], DEFAULT_PORT));
|
||||
let mut config = ClientConfig::default();
|
||||
|
||||
assert!(address_family_allowed(&config, ipv4));
|
||||
assert!(address_family_allowed(&config, ipv6));
|
||||
|
||||
config.socket_config.ipv4_only = true;
|
||||
assert!(address_family_allowed(&config, ipv4));
|
||||
assert!(!address_family_allowed(&config, ipv6));
|
||||
|
||||
config.socket_config.ipv4_only = false;
|
||||
config.socket_config.ipv6_only = true;
|
||||
assert!(!address_family_allowed(&config, ipv4));
|
||||
assert!(address_family_allowed(&config, ipv6));
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
#[test]
|
||||
fn tokio_ipv6_only_is_applied_before_bind() {
|
||||
|
||||
Reference in new issue
Block a user