test: cover timing, overflow, and target authentication contracts
This commit is contained in:
4 files changed
+150
-3
No files matched your search
@@ -1314,6 +1314,78 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// Kernel completion order relative to timeout cannot be forced reliably
|
||||
// through a real socket. Drive both transitions through production methods
|
||||
// and observe the selected endpoint on the emitted measurable LateReply.
|
||||
#[test]
|
||||
fn late_replies_use_tx_timestamps_attached_before_or_after_timeout() {
|
||||
for attach_before_timeout in [true, false] {
|
||||
let mut machine = opened();
|
||||
let anchor = ClientTimestamp {
|
||||
mono: Instant::now(),
|
||||
wall: UNIX_EPOCH + Duration::from_secs(10),
|
||||
};
|
||||
let sent_at = ClientTimestamp {
|
||||
mono: anchor.mono + Duration::from_millis(1),
|
||||
wall: anchor.wall + Duration::from_millis(1),
|
||||
};
|
||||
let kernel_tx = anchor.wall + Duration::from_micros(500);
|
||||
let probe = machine.prepare_probe().unwrap();
|
||||
let preflight = machine.preflight_probe_commit(&probe).unwrap();
|
||||
let commit = machine.finalize_probe_commit(preflight, anchor).unwrap();
|
||||
let sent = machine.commit_probe_sent(commit, sent_at, probe.bytes.len());
|
||||
if attach_before_timeout {
|
||||
machine.record_kernel_tx_timestamp(sent.seq, kernel_tx);
|
||||
}
|
||||
let deadline = machine.next_probe_timeout_deadline().unwrap();
|
||||
assert!(matches!(
|
||||
machine.poll_timeouts_at(deadline).unwrap()[0],
|
||||
ClientEvent::EchoLoss { seq: 0, .. }
|
||||
));
|
||||
if !attach_before_timeout {
|
||||
machine.record_kernel_tx_timestamp(sent.seq, kernel_tx);
|
||||
}
|
||||
let events = machine
|
||||
.process_echo_reply(
|
||||
EchoReply {
|
||||
flags: FLAG_REPLY,
|
||||
token: 1,
|
||||
sequence: sent.seq,
|
||||
recv_count: None,
|
||||
recv_window: None,
|
||||
timestamps: TimestampFields {
|
||||
recv_wall: Some(12_000_000_000),
|
||||
..TimestampFields::default()
|
||||
},
|
||||
payload: vec![],
|
||||
},
|
||||
probe.bytes.len(),
|
||||
ClientTimestamp {
|
||||
mono: deadline + Duration::from_millis(1),
|
||||
wall: anchor.wall + Duration::from_secs(10),
|
||||
},
|
||||
ReceiveMeta::default(),
|
||||
)
|
||||
.unwrap();
|
||||
let ClientEvent::LateReply {
|
||||
sent_at: Some(reported_send),
|
||||
server_timing: Some(timing),
|
||||
one_way: Some(one_way),
|
||||
..
|
||||
} = &events[0]
|
||||
else {
|
||||
panic!("expected measurable LateReply: {events:?}");
|
||||
};
|
||||
assert_eq!(*reported_send, sent_at);
|
||||
let selected = i128::from(timing.receive_wall_ns.unwrap())
|
||||
- one_way.client_to_server.unwrap().as_nanos();
|
||||
assert_eq!(
|
||||
selected, 10_000_500_000,
|
||||
"kernel TX endpoint must survive timeout"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// A recoverable send failure that consumes an OPT_ID, followed by a
|
||||
// plausible wrong-ID completion, cannot be induced reliably with public
|
||||
// sockets. Exercise that failure boundary directly, without a socket hook
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn probe_timeout_overflow_transmits_no_echo() {
|
||||
let params = default_params();
|
||||
let server = start_fake_server(move |socket, tx| {
|
||||
socket
|
||||
.set_read_timeout(Some(Duration::from_secs(2)))
|
||||
.unwrap();
|
||||
let (_, peer) = recv_request(&socket, &tx);
|
||||
socket
|
||||
.send_to(
|
||||
&open_reply(FLAG_OPEN | FLAG_REPLY, TOKEN, ¶ms, None),
|
||||
peer,
|
||||
)
|
||||
.unwrap();
|
||||
// CLOSE is a completion barrier: any incorrectly transmitted ECHO
|
||||
// precedes it on this connected loopback socket.
|
||||
let _ = recv_request(&socket, &tx);
|
||||
});
|
||||
let config = ClientConfig {
|
||||
probe_timeout: Duration::MAX,
|
||||
..default_test_config(server.addr)
|
||||
};
|
||||
let mut client = Client::connect(config).unwrap();
|
||||
assert_open_started(client.open().unwrap());
|
||||
assert!(matches!(
|
||||
client.send_probe(),
|
||||
Err(ClientError::DurationOverflow)
|
||||
));
|
||||
assert!(!client.has_pending_probes());
|
||||
client.close().unwrap();
|
||||
let packets: Vec<_> = server.rx.iter().collect();
|
||||
assert_eq!(packets.len(), 2);
|
||||
assert_eq!(
|
||||
packets[1][3],
|
||||
flags::FLAG_CLOSE,
|
||||
"overflow must not transmit an ECHO"
|
||||
);
|
||||
server.join();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poll_timeouts_emits_echo_loss() {
|
||||
let params = default_params();
|
||||
|
||||
@@ -132,9 +132,14 @@ fn assert_kernel_send_endpoint(event: &ClientEvent, before_send: SystemTime) {
|
||||
"TX timestamp was not observed; userspace fallback is insufficient"
|
||||
);
|
||||
assert!(selected <= ns(received_at.wall));
|
||||
assert!(
|
||||
packet_meta.kernel_rx_timestamp.is_some(),
|
||||
"Linux RX timestamp required"
|
||||
let selected_receive =
|
||||
i128::from(timing.send_wall_ns.unwrap()) + one_way.server_to_client.unwrap().as_nanos();
|
||||
assert_eq!(
|
||||
selected_receive,
|
||||
ns(packet_meta
|
||||
.kernel_rx_timestamp
|
||||
.expect("Linux RX timestamp required")),
|
||||
"downstream OWD must use the kernel receive endpoint"
|
||||
);
|
||||
assert_eq!(rtt.raw, received_at.mono.duration_since(sent_at.mono));
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user