use super::*;
fn make_node_addr(val: u8) -> NodeAddr {
let mut bytes = [0u8; 16];
bytes[0] = val;
NodeAddr::from_bytes(bytes)
}
fn make_coords(ids: &[u8]) -> TreeCoordinate {
TreeCoordinate::from_addrs(ids.iter().map(|&v| make_node_addr(v)).collect()).unwrap()
}
#[test]
fn test_session_message_type_roundtrip() {
let types = [
SessionMessageType::DataPacket,
SessionMessageType::SenderReport,
SessionMessageType::ReceiverReport,
SessionMessageType::PathMtuNotification,
SessionMessageType::CoordsWarmup,
SessionMessageType::EndpointData,
SessionMessageType::CoordsRequired,
SessionMessageType::PathBroken,
SessionMessageType::MtuExceeded,
];
for ty in types {
let byte = ty.to_byte();
let restored = SessionMessageType::from_byte(byte);
assert_eq!(restored, Some(ty));
}
}
#[test]
fn test_session_message_type_invalid() {
assert!(SessionMessageType::from_byte(0xFF).is_none());
assert!(SessionMessageType::from_byte(0x99).is_none());
}
#[test]
fn test_session_flags() {
let flags = SessionFlags::new().with_ack().bidirectional();
assert!(flags.request_ack);
assert!(flags.bidirectional);
let byte = flags.to_byte();
let restored = SessionFlags::from_byte(byte);
assert_eq!(flags, restored);
}
#[test]
fn test_session_flags_default() {
let flags = SessionFlags::new();
assert!(!flags.request_ack);
assert!(!flags.bidirectional);
assert_eq!(flags.to_byte(), 0);
}
#[test]
fn test_session_setup() {
let setup = SessionSetup::new(make_coords(&[1, 0]), make_coords(&[2, 0]))
.with_flags(SessionFlags::new().with_ack());
assert!(setup.flags.request_ack);
assert!(!setup.flags.bidirectional);
}
#[test]
fn test_coords_required() {
let err = CoordsRequired::new(make_node_addr(1), make_node_addr(2));
assert_eq!(err.dest_addr, make_node_addr(1));
assert_eq!(err.reporter, make_node_addr(2));
}
#[test]
fn test_path_broken() {
let err = PathBroken::new(make_node_addr(2), make_node_addr(3))
.with_last_coords(make_coords(&[2, 0]));
assert_eq!(err.dest_addr, make_node_addr(2));
assert_eq!(err.reporter, make_node_addr(3));
assert!(err.last_known_coords.is_some());
}
#[test]
fn test_session_setup_encode_decode() {
let handshake = vec![0xAA; 82]; let setup = SessionSetup::new(make_coords(&[1, 2, 0]), make_coords(&[3, 4, 0]))
.with_flags(SessionFlags::new().with_ack().bidirectional())
.with_handshake(handshake.clone());
let encoded = setup.encode();
assert_eq!(encoded[0], 0x01);
assert_eq!(encoded[1], 0x00); let payload_len = u16::from_le_bytes([encoded[2], encoded[3]]);
assert_eq!(payload_len as usize, encoded.len() - 4);
let decoded = SessionSetup::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.flags, setup.flags);
assert_eq!(decoded.src_coords, setup.src_coords);
assert_eq!(decoded.dest_coords, setup.dest_coords);
assert_eq!(decoded.handshake_payload, handshake);
}
#[test]
fn test_session_setup_no_handshake() {
let setup = SessionSetup::new(make_coords(&[5, 0]), make_coords(&[6, 0]));
let encoded = setup.encode();
let decoded = SessionSetup::decode(&encoded[4..]).unwrap();
assert!(decoded.handshake_payload.is_empty());
assert_eq!(decoded.src_coords, setup.src_coords);
assert_eq!(decoded.dest_coords, setup.dest_coords);
}
#[test]
fn test_session_ack_encode_decode() {
let handshake = vec![0xBB; 33]; let ack = SessionAck::new(make_coords(&[7, 8, 0]), make_coords(&[3, 4, 0]))
.with_handshake(handshake.clone());
let encoded = ack.encode();
assert_eq!(encoded[0], 0x02);
assert_eq!(encoded[1], 0x00);
let decoded = SessionAck::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.src_coords, ack.src_coords);
assert_eq!(decoded.dest_coords, ack.dest_coords);
assert_eq!(decoded.handshake_payload, handshake);
}
#[test]
fn test_coords_required_encode_decode() {
let err = CoordsRequired::new(make_node_addr(0xAA), make_node_addr(0xBB));
let encoded = err.encode();
assert_eq!(encoded.len(), 4 + COORDS_REQUIRED_SIZE);
assert_eq!(encoded[0], 0x00);
assert_eq!(encoded[1], 0x04); assert_eq!(encoded[4], 0x20);
let decoded = CoordsRequired::decode(&encoded[5..]).unwrap();
assert_eq!(decoded.dest_addr, err.dest_addr);
assert_eq!(decoded.reporter, err.reporter);
}
#[test]
fn test_path_broken_encode_decode_no_coords() {
let err = PathBroken::new(make_node_addr(0xCC), make_node_addr(0xDD));
let encoded = err.encode();
assert_eq!(encoded[0], 0x00);
assert_eq!(encoded[1], 0x04); assert_eq!(encoded[4], 0x21);
let decoded = PathBroken::decode(&encoded[5..]).unwrap();
assert_eq!(decoded.dest_addr, err.dest_addr);
assert_eq!(decoded.reporter, err.reporter);
assert!(decoded.last_known_coords.is_none());
}
#[test]
fn test_path_broken_encode_decode_with_coords() {
let coords = make_coords(&[0xCC, 0xDD, 0xEE]);
let err = PathBroken::new(make_node_addr(0x11), make_node_addr(0x22))
.with_last_coords(coords.clone());
let encoded = err.encode();
let decoded = PathBroken::decode(&encoded[5..]).unwrap();
assert_eq!(decoded.dest_addr, err.dest_addr);
assert_eq!(decoded.reporter, err.reporter);
assert_eq!(decoded.last_known_coords.unwrap(), coords);
}
#[test]
fn test_session_setup_decode_too_short() {
assert!(SessionSetup::decode(&[]).is_err());
}
#[test]
fn test_session_ack_decode_too_short() {
assert!(SessionAck::decode(&[]).is_err());
}
#[test]
fn test_coords_required_decode_too_short() {
assert!(CoordsRequired::decode(&[]).is_err());
assert!(CoordsRequired::decode(&[0x00; 10]).is_err());
}
#[test]
fn test_path_broken_decode_too_short() {
assert!(PathBroken::decode(&[]).is_err());
assert!(PathBroken::decode(&[0x00; 20]).is_err());
}
#[test]
fn test_session_setup_deep_coords() {
let addrs: Vec<u8> = (0..11).collect();
let src = make_coords(&addrs);
let dest = make_coords(&[20, 21, 22, 23, 24]);
let setup = SessionSetup::new(src.clone(), dest.clone()).with_handshake(vec![0x55; 82]);
let encoded = setup.encode();
let decoded = SessionSetup::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.src_coords, src);
assert_eq!(decoded.dest_coords, dest);
}
#[test]
fn test_fsp_flags_default() {
let flags = FspFlags::new();
assert!(!flags.coords_present);
assert!(!flags.key_epoch);
assert!(!flags.unencrypted);
assert_eq!(flags.to_byte(), 0x00);
}
#[test]
fn test_fsp_flags_roundtrip() {
for byte in 0u8..=0x07 {
let flags = FspFlags::from_byte(byte);
assert_eq!(flags.to_byte(), byte);
}
}
#[test]
fn test_fsp_flags_individual_bits() {
let cp = FspFlags::from_byte(0x01);
assert!(cp.coords_present);
assert!(!cp.key_epoch);
assert!(!cp.unencrypted);
let k = FspFlags::from_byte(0x02);
assert!(!k.coords_present);
assert!(k.key_epoch);
assert!(!k.unencrypted);
let u = FspFlags::from_byte(0x04);
assert!(!u.coords_present);
assert!(!u.key_epoch);
assert!(u.unencrypted);
}
#[test]
fn test_fsp_flags_ignores_reserved_bits() {
let flags = FspFlags::from_byte(0xFF);
assert!(flags.coords_present);
assert!(flags.key_epoch);
assert!(flags.unencrypted);
assert_eq!(flags.to_byte(), 0x07); }
#[test]
fn test_fsp_inner_flags_default() {
let flags = FspInnerFlags::new();
assert!(!flags.spin_bit);
assert_eq!(flags.to_byte(), 0x00);
}
#[test]
fn test_fsp_inner_flags_roundtrip() {
let flags = FspInnerFlags::from_byte(0x01);
assert!(flags.spin_bit);
assert_eq!(flags.to_byte(), 0x01);
let flags = FspInnerFlags::from_byte(0x00);
assert!(!flags.spin_bit);
assert_eq!(flags.to_byte(), 0x00);
}
#[test]
fn test_fsp_inner_flags_ignores_reserved() {
let flags = FspInnerFlags::from_byte(0xFE);
assert!(!flags.spin_bit);
assert_eq!(flags.to_byte(), 0x00);
let flags = FspInnerFlags::from_byte(0xFF);
assert!(flags.spin_bit);
assert_eq!(flags.to_byte(), 0x01);
}
#[test]
fn test_session_message_type_new_values() {
assert_eq!(SessionMessageType::SenderReport.to_byte(), 0x11);
assert_eq!(SessionMessageType::ReceiverReport.to_byte(), 0x12);
assert_eq!(SessionMessageType::PathMtuNotification.to_byte(), 0x13);
assert_eq!(SessionMessageType::CoordsWarmup.to_byte(), 0x14);
assert_eq!(SessionMessageType::EndpointData.to_byte(), 0x15);
}
#[test]
fn test_session_message_type_display() {
assert_eq!(
format!("{}", SessionMessageType::SenderReport),
"SenderReport"
);
assert_eq!(
format!("{}", SessionMessageType::ReceiverReport),
"ReceiverReport"
);
assert_eq!(
format!("{}", SessionMessageType::PathMtuNotification),
"PathMtuNotification"
);
assert_eq!(
format!("{}", SessionMessageType::CoordsWarmup),
"CoordsWarmup"
);
assert_eq!(
format!("{}", SessionMessageType::EndpointData),
"EndpointData"
);
}
fn sample_session_sender_report() -> SessionSenderReport {
SessionSenderReport {
interval_start_counter: 100,
interval_end_counter: 200,
interval_start_timestamp: 5000,
interval_end_timestamp: 6000,
interval_bytes_sent: 50_000,
cumulative_packets_sent: 10_000,
cumulative_bytes_sent: 5_000_000,
}
}
#[test]
fn test_session_sender_report_encode_size() {
let sr = sample_session_sender_report();
let encoded = sr.encode();
assert_eq!(encoded.len(), SESSION_SENDER_REPORT_SIZE);
}
#[test]
fn test_session_sender_report_roundtrip() {
let sr = sample_session_sender_report();
let encoded = sr.encode();
let decoded = SessionSenderReport::decode(&encoded).unwrap();
assert_eq!(sr, decoded);
}
#[test]
fn test_session_sender_report_too_short() {
assert!(SessionSenderReport::decode(&[0u8; 10]).is_err());
}
fn sample_session_receiver_report() -> SessionReceiverReport {
SessionReceiverReport {
highest_counter: 195,
cumulative_packets_recv: 9_500,
cumulative_bytes_recv: 4_750_000,
timestamp_echo: 5900,
dwell_time: 5,
max_burst_loss: 3,
mean_burst_loss: 384,
jitter: 1200,
ecn_ce_count: 0,
owd_trend: -50,
burst_loss_count: 2,
cumulative_reorder_count: 10,
interval_packets_recv: 95,
interval_bytes_recv: 47_500,
}
}
#[test]
fn test_session_receiver_report_encode_size() {
let rr = sample_session_receiver_report();
let encoded = rr.encode();
assert_eq!(encoded.len(), SESSION_RECEIVER_REPORT_SIZE);
}
#[test]
fn test_session_receiver_report_roundtrip() {
let rr = sample_session_receiver_report();
let encoded = rr.encode();
let decoded = SessionReceiverReport::decode(&encoded).unwrap();
assert_eq!(rr, decoded);
}
#[test]
fn test_session_receiver_report_too_short() {
assert!(SessionReceiverReport::decode(&[0u8; 10]).is_err());
}
#[test]
fn test_session_receiver_report_negative_owd_trend() {
let rr = SessionReceiverReport {
owd_trend: -12345,
..sample_session_receiver_report()
};
let encoded = rr.encode();
let decoded = SessionReceiverReport::decode(&encoded).unwrap();
assert_eq!(decoded.owd_trend, -12345);
}
#[test]
fn test_path_mtu_notification_encode_size() {
let n = PathMtuNotification::new(1400);
let encoded = n.encode();
assert_eq!(encoded.len(), PATH_MTU_NOTIFICATION_SIZE);
}
#[test]
fn test_path_mtu_notification_roundtrip() {
let n = PathMtuNotification::new(1400);
let encoded = n.encode();
let decoded = PathMtuNotification::decode(&encoded).unwrap();
assert_eq!(decoded.path_mtu, 1400);
}
#[test]
fn test_path_mtu_notification_too_short() {
assert!(PathMtuNotification::decode(&[]).is_err());
assert!(PathMtuNotification::decode(&[0x00]).is_err());
}
#[test]
fn test_path_mtu_notification_boundary_values() {
for mtu in [0u16, 1280, 1500, u16::MAX] {
let n = PathMtuNotification::new(mtu);
let encoded = n.encode();
let decoded = PathMtuNotification::decode(&encoded).unwrap();
assert_eq!(decoded.path_mtu, mtu);
}
}
#[test]
fn test_mtu_exceeded_encode_size() {
let err = MtuExceeded::new(make_node_addr(0xAA), make_node_addr(0xBB), 1400);
let encoded = err.encode();
assert_eq!(encoded.len(), 4 + MTU_EXCEEDED_SIZE);
}
#[test]
fn test_mtu_exceeded_encode_decode() {
let err = MtuExceeded::new(make_node_addr(0xAA), make_node_addr(0xBB), 1400);
let encoded = err.encode();
assert_eq!(encoded[0], 0x00);
assert_eq!(encoded[1], 0x04); assert_eq!(encoded[4], 0x22);
let decoded = MtuExceeded::decode(&encoded[5..]).unwrap();
assert_eq!(decoded.dest_addr, err.dest_addr);
assert_eq!(decoded.reporter, err.reporter);
assert_eq!(decoded.mtu, 1400);
}
#[test]
fn test_mtu_exceeded_decode_too_short() {
assert!(MtuExceeded::decode(&[]).is_err());
assert!(MtuExceeded::decode(&[0x00; 20]).is_err());
assert!(MtuExceeded::decode(&[0x00; 34]).is_err()); }
#[test]
fn test_mtu_exceeded_boundary_mtu_values() {
for mtu in [0u16, 1280, 1500, u16::MAX] {
let err = MtuExceeded::new(make_node_addr(1), make_node_addr(2), mtu);
let encoded = err.encode();
let decoded = MtuExceeded::decode(&encoded[5..]).unwrap();
assert_eq!(decoded.mtu, mtu);
}
}
#[test]
fn test_mtu_exceeded_message_type_value() {
assert_eq!(SessionMessageType::MtuExceeded.to_byte(), 0x22);
assert_eq!(
SessionMessageType::from_byte(0x22),
Some(SessionMessageType::MtuExceeded)
);
}
#[test]
fn test_mtu_exceeded_display() {
assert_eq!(
format!("{}", SessionMessageType::MtuExceeded),
"MtuExceeded"
);
}
#[test]
fn test_session_msg3_encode_decode() {
let handshake = vec![0xCC; 73]; let msg3 = SessionMsg3::new(handshake.clone());
let encoded = msg3.encode();
assert_eq!(encoded[0], 0x03);
assert_eq!(encoded[1], 0x00); let payload_len = u16::from_le_bytes([encoded[2], encoded[3]]);
assert_eq!(payload_len as usize, encoded.len() - 4);
let decoded = SessionMsg3::decode(&encoded[4..]).unwrap();
assert_eq!(decoded.flags, 0);
assert_eq!(decoded.handshake_payload, handshake);
}
#[test]
fn test_session_msg3_decode_too_short() {
assert!(SessionMsg3::decode(&[]).is_err());
assert!(SessionMsg3::decode(&[0x00]).is_err()); }
#[test]
fn test_session_msg3_empty_handshake() {
let msg3 = SessionMsg3::new(vec![]);
let encoded = msg3.encode();
let decoded = SessionMsg3::decode(&encoded[4..]).unwrap();
assert!(decoded.handshake_payload.is_empty());
}