mod common;
use common::*;
use km003c_lib::{AdcQueueData, GraphSampleRate};
#[test]
fn test_rawpacket_to_bytes_ctrl() {
let raw_packet = RawPacket::Ctrl {
header: CtrlHeader::new()
.with_packet_type(2)
.with_reserved_flag(false)
.with_id(1)
.with_attribute(0),
payload: Bytes::new().to_vec(),
};
let bytes = Bytes::from(raw_packet);
let expected_bytes = [0x02, 0x01, 0x00, 0x00];
assert_eq!(
bytes.as_ref(),
&expected_bytes,
"Ctrl packet should convert to [02, 01, 00, 00], got {:02x?}",
bytes.as_ref()
);
}
#[test]
fn test_pd_monitor_commands_use_documented_wire_parameters() {
let enable = Bytes::from(Packet::EnablePdMonitor.to_raw_packet(3).unwrap());
let disable = Bytes::from(Packet::DisablePdMonitor.to_raw_packet(4).unwrap());
assert_eq!(enable.as_ref(), &[0x10, 0x03, 0x02, 0x00]);
assert_eq!(disable.as_ref(), &[0x11, 0x04, 0x00, 0x00]);
}
#[test]
fn auth_requests_use_their_special_wire_headers() {
let memory = Bytes::from(
Packet::MemoryRead {
address: 0x420,
size: 64,
}
.to_raw_packet(2)
.unwrap(),
);
let hardware_id = km003c_lib::auth::HardwareId::from_bytes([
0x30, 0x37, 0x31, 0x4b, 0x42, 0x50, 0x0d, 0xff, 0x11, 0x0a, 0xff, 0xff,
]);
let streaming_auth = Bytes::from(
Packet::StreamingAuth {
credential: (&hardware_id).into(),
}
.to_raw_packet(6)
.unwrap(),
);
assert_eq!(&memory[..4], &[0x44, 0x02, 0x01, 0x01]);
assert_eq!(&streaming_auth[..4], &[0x4c, 0x06, 0x00, 0x02]);
}
#[test]
fn pd_monitor_commands_round_trip_through_the_parser() {
for (command, bytes) in [
(Packet::EnablePdMonitor, [0x10, 0x03, 0x02, 0x00]),
(Packet::DisablePdMonitor, [0x11, 0x04, 0x00, 0x00]),
] {
let parsed = Packet::try_from(RawPacket::try_from(Bytes::from(bytes.to_vec())).unwrap()).unwrap();
assert_eq!(parsed, command, "{bytes:02x?} must parse back into {command:?}");
}
}
#[test]
fn start_graph_round_trips_every_documented_rate() {
for rate in [
GraphSampleRate::Sps2,
GraphSampleRate::Sps10,
GraphSampleRate::Sps50,
GraphSampleRate::Sps1000,
] {
let bytes = Bytes::from(Packet::StartGraph { rate }.to_raw_packet(7).unwrap());
let parsed = Packet::try_from(RawPacket::try_from(bytes).unwrap()).unwrap();
assert_eq!(parsed, Packet::StartGraph { rate });
}
}
#[test]
fn start_graph_with_an_unknown_rate_index_stays_generic() {
let parsed = Packet::try_from(RawPacket::try_from(Bytes::from(vec![0x0e, 0x07, 0x40, 0x00])).unwrap()).unwrap();
assert!(
matches!(parsed, Packet::Generic(_)),
"an unknown rate index must be preserved verbatim, got {parsed:?}"
);
}
#[test]
fn test_unsupported_semantic_payload_is_not_silently_dropped() {
let packet = Packet::DataResponse {
payloads: vec![PayloadData::AdcQueue(AdcQueueData {
rate: GraphSampleRate::Sps50,
samples: Vec::new(),
})],
};
assert!(matches!(
packet.to_raw_packet(0),
Err(KMError::UnsupportedSerialization { packet: "AdcQueue" })
));
}
#[test]
fn semantic_adc_response_uses_recorded_header_encoding() {
let recorded = Bytes::from(REAL_ADC_RESPONSE.to_vec());
let packet = Packet::try_from(RawPacket::try_from(recorded.clone()).unwrap()).unwrap();
let serialized = Bytes::from(packet.to_raw_packet(0).unwrap());
assert_eq!(&serialized[..8], &recorded[..8]);
assert_eq!(
serialized, recorded,
"semantic ADC parsing must preserve every wire field"
);
assert_eq!(serialized[0], 0x41, "PutData does not set the reserved bit");
assert_eq!(&serialized[2..4], &[0x80, 0x02], "recorded ADC obj_count is 10 words");
}
#[test]
fn unknown_adc_sample_rate_is_preserved_without_a_fallback() {
let mut recorded = REAL_ADC_RESPONSE.to_vec();
recorded[8 + 36] = 0xfe;
let packet = Packet::try_from(RawPacket::try_from(Bytes::from(recorded.clone())).unwrap()).unwrap();
let adc = packet.get_adc().unwrap();
assert_eq!(adc.sample_rate, None);
assert_eq!(adc.sample_rate_raw, 0xfe);
let serialized = Bytes::from(packet.to_raw_packet(0).unwrap());
assert_eq!(serialized.as_ref(), recorded);
}
#[test]
fn test_rawpacket_to_bytes_simple_data() {
let payload = Bytes::from_static(&[0xAA, 0xBB, 0xCC, 0xDD]).to_vec();
let raw_packet = RawPacket::SimpleData {
header: DataHeader::new()
.with_packet_type(64)
.with_reserved_flag(false)
.with_id(1)
.with_obj_count_words(4),
payload,
};
let bytes = Bytes::from(raw_packet);
let expected_bytes = [0x40, 0x01, 0x00, 0x01, 0xAA, 0xBB, 0xCC, 0xDD];
assert_eq!(
bytes.as_ref(),
&expected_bytes,
"SimpleData packet should convert to [40, 01, 00, 01, AA, BB, CC, DD], got {:02x?}",
bytes.as_ref()
);
}
#[test]
fn test_rawpacket_to_bytes_data_with_logical_packet() {
let payload = Bytes::from_static(&[0xAA, 0xBB, 0xCC, 0xDD]).to_vec();
let logical_packet = LogicalPacket {
attribute: Attribute::Adc,
next: false,
chunk: 0,
size: 4,
payload,
};
let raw_packet = RawPacket::Data {
header: DataHeader::new()
.with_packet_type(65)
.with_reserved_flag(true)
.with_id(12)
.with_obj_count_words(2), logical_packets: vec![logical_packet],
};
let bytes = Bytes::from(raw_packet);
assert_eq!(bytes.len(), 12, "Data packet should be 12 bytes total");
let parsed = RawPacket::try_from(bytes.clone()).expect("Failed to parse generated bytes");
match parsed {
RawPacket::Data {
header,
logical_packets,
} => {
assert_eq!(header.packet_type(), 65);
assert_eq!(logical_packets.len(), 1);
let lp = &logical_packets[0];
assert!(!lp.next);
assert_eq!(lp.attribute, Attribute::Adc);
assert_eq!(lp.size, 4);
assert_eq!(&lp.payload, &[0xAA, 0xBB, 0xCC, 0xDD]);
}
_ => panic!("Expected Data after parsing serialized bytes"),
}
}
#[test]
fn test_rawpacket_to_bytes_ctrl_with_payload() {
let payload = Bytes::from_static(&[0x12, 0x34]).to_vec();
let raw_packet = RawPacket::Ctrl {
header: CtrlHeader::new()
.with_packet_type(5)
.with_reserved_flag(true)
.with_id(7)
.with_attribute(0x123),
payload,
};
let bytes = Bytes::from(raw_packet.clone());
let original_bytes = bytes.clone();
let parsed_packet = RawPacket::try_from(original_bytes).expect("Failed to parse generated bytes");
match parsed_packet {
RawPacket::Ctrl {
header,
payload: parsed_payload,
} => {
assert_eq!(header.packet_type(), 5, "Packet type should be 5");
assert!(header.reserved_flag(), "Reserved flag should be true");
assert_eq!(header.id(), 7, "ID should be 7");
assert_eq!(header.attribute(), 0x123, "Attribute should be 0x123");
assert_eq!(&parsed_payload, &[0x12, 0x34], "Payload should match");
}
_ => panic!("Expected Ctrl packet"),
}
assert_eq!(bytes.len(), 6, "Total packet should be 6 bytes");
assert_eq!(&bytes[4..], &[0x12, 0x34], "Payload should be correctly appended");
}
#[test]
fn test_rawpacket_to_bytes_empty_payload() {
let ctrl_packet = RawPacket::Ctrl {
header: CtrlHeader::new()
.with_packet_type(12)
.with_reserved_flag(false)
.with_id(0)
.with_attribute(1),
payload: Bytes::new().to_vec(),
};
let data_packet = RawPacket::SimpleData {
header: DataHeader::new()
.with_packet_type(64)
.with_reserved_flag(false)
.with_id(0)
.with_obj_count_words(0),
payload: Bytes::new().to_vec(),
};
let ctrl_bytes = Bytes::from(ctrl_packet);
let data_bytes = Bytes::from(data_packet);
assert_eq!(ctrl_bytes.len(), 4, "Ctrl packet with empty payload should be 4 bytes");
assert_eq!(data_bytes.len(), 4, "Data packet with empty payload should be 4 bytes");
}
#[test]
fn test_rawpacket_to_bytes_large_payload() {
let large_payload: Vec<u8> = (0..=255u8).collect();
let raw_packet = RawPacket::SimpleData {
header: DataHeader::new()
.with_packet_type(65)
.with_reserved_flag(true)
.with_id(42)
.with_obj_count_words(64), payload: Bytes::from(large_payload.clone()).to_vec(),
};
let bytes = Bytes::from(raw_packet);
assert_eq!(bytes.len(), 260, "Large payload packet should be 260 bytes total");
assert_eq!(
&bytes[4..],
&large_payload,
"Payload should be correctly appended after header"
);
}