Implement generated packet wire codecs (#47)
This commit is contained in:
178
crates/libremetaverse/tests/packet_wire.rs
Normal file
178
crates/libremetaverse/tests/packet_wire.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use libremetaverse::packets::{
|
||||
AgentThrottlePacket, Header, Packet, PacketType, UseCircuitCodePacket,
|
||||
};
|
||||
use libremetaverse::{Error, Helpers, PacketFrequency};
|
||||
use libremetaverse_types::UUID;
|
||||
|
||||
fn header(frequency: PacketFrequency, id: u16) -> Header {
|
||||
Header {
|
||||
ack_list: None,
|
||||
appended_acks: false,
|
||||
frequency,
|
||||
id,
|
||||
reliable: true,
|
||||
resent: false,
|
||||
sequence: 0x0102_0304,
|
||||
zerocoded: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn header_bytes(header: &Header) -> Vec<u8> {
|
||||
let length = match header.frequency {
|
||||
PacketFrequency::High => 7,
|
||||
PacketFrequency::Medium => 8,
|
||||
PacketFrequency::Low => 10,
|
||||
};
|
||||
let mut bytes = vec![0_u8; length];
|
||||
let mut position = 0;
|
||||
header
|
||||
.to_bytes(&mut bytes, &mut position)
|
||||
.expect("encode header");
|
||||
assert_eq!(usize::try_from(position).unwrap(), bytes.len());
|
||||
bytes
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn header_frequencies_match_the_reference_wire_bytes() {
|
||||
// Golden source: codegen/inputs/message_template.msg frequency rules and
|
||||
// LibreMetaverse.Tests/PacketTests.cs::HeaderFlags (translated in
|
||||
// tests/compat/tests/wire_semantics.rs::packet_header_flags).
|
||||
assert_eq!(
|
||||
header_bytes(&header(PacketFrequency::High, 0x2a)),
|
||||
[0x40, 1, 2, 3, 4, 0, 0x2a]
|
||||
);
|
||||
assert_eq!(
|
||||
header_bytes(&header(PacketFrequency::Medium, 0x2a)),
|
||||
[0x40, 1, 2, 3, 4, 0, 0xff, 0x2a]
|
||||
);
|
||||
assert_eq!(
|
||||
header_bytes(&header(PacketFrequency::Low, 0x1234)),
|
||||
[0x40, 1, 2, 3, 4, 0, 0xff, 0xff, 0x12, 0x34]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn appended_acks_are_big_endian_and_removed_from_the_payload_boundary() {
|
||||
let mut value = header(PacketFrequency::High, 0x2a);
|
||||
value.appended_acks = true;
|
||||
value.ack_list = Some(vec![0x0102_0304, 0xa0b0_c0d0]);
|
||||
let mut bytes = header_bytes(&value);
|
||||
let mut ack_bytes = [0_u8; 9];
|
||||
let mut ack_position = 0;
|
||||
value
|
||||
.acks_to_bytes(&mut ack_bytes, &mut ack_position)
|
||||
.expect("encode ACKs");
|
||||
assert_eq!(ack_bytes, [1, 2, 3, 4, 0xa0, 0xb0, 0xc0, 0xd0, 2]);
|
||||
bytes.extend_from_slice(&ack_bytes);
|
||||
|
||||
let mut position = 0;
|
||||
let mut packet_end = i32::try_from(bytes.len()).unwrap() - 1;
|
||||
let decoded = Header::build_header(bytes, &mut position, &mut packet_end).expect("decode");
|
||||
assert!(decoded.appended_acks);
|
||||
assert_eq!(decoded.ack_list, value.ack_list);
|
||||
assert_eq!(position, 7);
|
||||
assert_eq!(packet_end, 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn use_circuit_code_matches_the_reference_golden_packet() {
|
||||
// Golden schema: codegen/inputs/message_template.msg::UseCircuitCode
|
||||
// (Low 3, CircuitCode/U32 + SessionID/LLUUID + ID/LLUUID). Header behavior
|
||||
// is linked to the translated HeaderFlags test above.
|
||||
let session = (0_u8..16).collect::<Vec<_>>();
|
||||
let agent = (16_u8..32).collect::<Vec<_>>();
|
||||
let mut packet = UseCircuitCodePacket::new_with_constructor().expect("packet constructor");
|
||||
packet.circuit_code.code = 0x1122_3344;
|
||||
packet.circuit_code.session_id =
|
||||
UUID::new_with_bytes_int32(session.clone(), 0).expect("session UUID");
|
||||
packet.circuit_code.id = UUID::new_with_bytes_int32(agent.clone(), 0).expect("agent UUID");
|
||||
|
||||
let encoded = packet.to_bytes_with_method().expect("encode packet");
|
||||
let mut expected = vec![
|
||||
0x40, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 3, 0x44, 0x33, 0x22, 0x11,
|
||||
];
|
||||
expected.extend_from_slice(&session);
|
||||
expected.extend_from_slice(&agent);
|
||||
assert_eq!(encoded, expected);
|
||||
|
||||
let mut position = 0;
|
||||
let decoded =
|
||||
UseCircuitCodePacket::new_with_bytes_int32(encoded.clone(), &mut position).expect("decode");
|
||||
assert_eq!(usize::try_from(position).unwrap(), encoded.len());
|
||||
assert_eq!(decoded.circuit_code.code, 0x1122_3344);
|
||||
assert_eq!(
|
||||
decoded.circuit_code.session_id.get_bytes().unwrap(),
|
||||
session
|
||||
);
|
||||
assert_eq!(decoded.circuit_code.id.get_bytes().unwrap(), agent);
|
||||
assert_eq!(decoded.to_bytes_with_method().unwrap(), encoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_and_unknown_packets_return_errors_without_panicking() {
|
||||
let valid = [0x40, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 3];
|
||||
for length in 0..valid.len() {
|
||||
let bytes = valid[..length].to_vec();
|
||||
let outcome = std::panic::catch_unwind(|| {
|
||||
let mut end = i32::try_from(bytes.len()).unwrap() - 1;
|
||||
Packet::build_packet_with_bytes_int32_bytes(bytes.clone(), &mut end, vec![0; 64])
|
||||
});
|
||||
assert!(outcome.is_ok(), "truncation at {length} panicked");
|
||||
assert!(
|
||||
outcome.unwrap().is_err(),
|
||||
"truncation at {length} was accepted"
|
||||
);
|
||||
}
|
||||
|
||||
let missing_payload = valid.to_vec();
|
||||
let mut end = i32::try_from(missing_payload.len()).unwrap() - 1;
|
||||
assert!(
|
||||
Packet::build_packet_with_bytes_int32_bytes(missing_payload, &mut end, vec![0; 64])
|
||||
.is_err()
|
||||
);
|
||||
|
||||
let unknown = vec![0x40, 0, 0, 0, 0, 0, 0xfe];
|
||||
let mut end = i32::try_from(unknown.len()).unwrap() - 1;
|
||||
let Err(error) = Packet::build_packet_with_bytes_int32_bytes(unknown, &mut end, vec![0; 64])
|
||||
else {
|
||||
panic!("unknown packet ID was accepted");
|
||||
};
|
||||
assert!(matches!(error, Error::Parse { .. }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packet_factory_decodes_zerocoded_payloads() {
|
||||
let mut packet = AgentThrottlePacket::new_with_constructor().expect("packet constructor");
|
||||
packet.agent_data.circuit_code = 0x1122_3344;
|
||||
packet.throttle.gen_counter = 0x5566_7788;
|
||||
packet.throttle.throttles = vec![1, 0, 0, 2, 3];
|
||||
let raw = packet.to_bytes_with_method().expect("raw packet");
|
||||
let mut encoded = vec![0_u8; raw.len() * 2 + 2];
|
||||
let encoded_length = Helpers::zero_encode(
|
||||
Some(&raw),
|
||||
i32::try_from(raw.len()).unwrap(),
|
||||
Some(&mut encoded),
|
||||
)
|
||||
.expect("zero encode");
|
||||
encoded.truncate(usize::try_from(encoded_length).unwrap());
|
||||
|
||||
let mut end = encoded_length - 1;
|
||||
let built =
|
||||
Packet::build_packet_with_bytes_int32_bytes(encoded, &mut end, vec![0_u8; raw.len()])
|
||||
.expect("packet factory");
|
||||
assert_eq!(built.type_, PacketType::AgentThrottle);
|
||||
assert_eq!(end, i32::try_from(raw.len()).unwrap() - 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packet_id_dispatch_covers_known_and_unknown_ids() {
|
||||
assert_eq!(
|
||||
Packet::get_type(3, PacketFrequency::Low).expect("packet type"),
|
||||
PacketType::UseCircuitCode
|
||||
);
|
||||
assert_eq!(
|
||||
Packet::get_type(u16::MAX, PacketFrequency::High).expect("unknown type"),
|
||||
PacketType::Default
|
||||
);
|
||||
assert_eq!(Helpers::MSG_RELIABLE, 0x40);
|
||||
}
|
||||
Reference in New Issue
Block a user