use psdk_fruit_emapi::{
chunker::{chunk_print_data, file_transfer_payload_capacity, print_transfer_payload_capacity},
constants::*,
crc32::compute,
payload::EmapiPayload,
tlv::{Tlv, TlvEntry},
EmapiCommand, FrameCodec, FrameParser,
};
#[test]
fn test_dart_parity_constants_match_command_md() {
assert_eq!(FRAME_HEAD, 0x02);
assert_eq!(FRAME_TAIL, 0x03);
assert_eq!(RESERVED, 0x00);
assert_eq!(TYPE_REQUEST, 0x01);
assert_eq!(TYPE_RESPONSE, 0x02);
assert_eq!(TYPE_ERROR, 0x03);
assert_eq!(TYPE_PASSTHROUGH_REQUEST, 0x41);
assert_eq!(TYPE_PASSTHROUGH_RESPONSE, 0x42);
assert_eq!(TYPE_PASSTHROUGH_ERROR, 0x43);
assert_eq!(PARENT_SYSTEM, 0x01);
assert_eq!(PARENT_PRINTER, 0x02);
assert_eq!(PARENT_RFID, 0x05);
assert_eq!(PARENT_WIFI, 0x06);
assert_eq!(PARENT_REPORT, 0x09);
assert_eq!(PARENT_FILE, 0x0A);
assert_eq!(CHILD_DEVICE_INFO, 0x01);
assert_eq!(CHILD_SLEEP_SHUTDOWN, 0x04);
assert_eq!(CHILD_SET_SHUTDOWN_TIME, 0x05);
assert_eq!(CHILD_RFID_UID, 0x01);
assert_eq!(CHILD_RFID_CARD_INFO, 0x02);
assert_eq!(CHILD_RFID_PAPER_LENGTH, 0x03);
assert_eq!(CHILD_RFID_AUTH_FAILURE_HANDLING, 0x04);
assert_eq!(CHILD_WIFI_CONFIG, 0x01);
assert_eq!(CHILD_WIFI_CONNECTION_STATE, 0x02);
assert_eq!(CHILD_WIFI_HOTSPOT_INFO, 0x03);
assert_eq!(CHILD_WIFI_CONFIG_STATUS, 0x04);
assert_eq!(CHILD_WIFI_FILE_START, 0x05);
assert_eq!(CHILD_WIFI_FILE_TRANSFER, 0x06);
assert_eq!(CHILD_WIFI_FILE_END, 0x07);
assert_eq!(CHILD_PRINTER_PARAMS, 0x01);
assert_eq!(CHILD_PRINT_STATUS, 0x04);
assert_eq!(CHILD_TRANSFER_DATA, 0x06);
assert_eq!(CHILD_PRINT_SELF_TEST_PAGE, 0x0C);
assert_eq!(CHILD_REPORT_PRINT_RESULT, 0x01);
assert_eq!(CHILD_REPORT_PRINTER_STATUS, 0x02);
assert_eq!(CHILD_REPORT_FLOW_CONTROL, 0x03);
assert_eq!(CHILD_REPORT_UPGRADE_STATUS, 0x04);
assert_eq!(CHILD_REPORT_BLUETOOTH_CONNECTION, 0x05);
assert_eq!(CHILD_FILE_START, 0x01);
assert_eq!(CHILD_FILE_TRANSFER, 0x02);
assert_eq!(CHILD_FILE_END, 0x03);
assert_eq!(CHILD_FILE_UPGRADE, 0x04);
assert_eq!(TAG_MTU, 0x08);
assert_eq!(COMMAND_HEADER_LENGTH, 5);
assert_eq!(FRAME_OVERHEAD_LENGTH, 9);
assert_eq!(PRINT_TRANSFER_FRAME_OVERHEAD_LENGTH, 14);
assert_eq!(CRC_SEED, 0x35769521);
}
#[test]
fn test_dart_parity_crc32_vectors() {
assert_eq!(compute(&[0x01, 0x02, 0x06, 0x00, 0x00]), 0xD1B4ABE2);
assert_eq!(compute(&[0x01, 0x01, 0x01, 0x00, 0x00]), 0xC64E1289);
assert_eq!(
compute(&[0x01, 0x02, 0x06, 0x03, 0x00, 0x10, 0xFF, 0xFE]),
0x467F3473
);
}
#[test]
fn test_dart_parity_encodes_known_request_frames() {
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_PRINTER,
CHILD_TRANSFER_DATA,
[]
))
.unwrap(),
vec![0x02, 0x00, 0x05, 0x00, 0x01, 0x02, 0x06, 0x00, 0x00, 0xE2, 0xAB, 0xB4, 0xD1, 0x03,]
);
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_SYSTEM,
CHILD_DEVICE_INFO,
[]
))
.unwrap(),
vec![0x02, 0x00, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x89, 0x12, 0x4E, 0xC6, 0x03,]
);
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_PRINTER,
CHILD_TRANSFER_DATA,
[0x10, 0xFF, 0xFE]
))
.unwrap(),
vec![
0x02, 0x00, 0x08, 0x00, 0x01, 0x02, 0x06, 0x03, 0x00, 0x10, 0xFF, 0xFE, 0x73, 0x34, 0x7F,
0x46, 0x03,
]
);
}
#[test]
fn test_dart_parity_encodes_representative_full_protocol_frames() {
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_SYSTEM,
CHILD_SLEEP_SHUTDOWN,
[]
))
.unwrap(),
vec![0x02, 0x00, 0x05, 0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x62, 0xD0, 0x85, 0xC0, 0x03,]
);
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_SYSTEM,
CHILD_SET_SHUTDOWN_TIME,
[0x1E, 0x00]
))
.unwrap(),
vec![
0x02, 0x00, 0x07, 0x00, 0x01, 0x01, 0x05, 0x02, 0x00, 0x1E, 0x00, 0x6F, 0xD7, 0xE9, 0xD3,
0x03,
]
);
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_RFID,
CHILD_RFID_AUTH_FAILURE_HANDLING,
[0x01]
))
.unwrap(),
vec![0x02, 0x00, 0x06, 0x00, 0x01, 0x05, 0x04, 0x01, 0x00, 0x01, 0x48, 0x96, 0x3B, 0xF2, 0x03,]
);
let wifi_payload = Tlv::encode(&[
TlvEntry::string(0x01, "demo").unwrap(),
TlvEntry::string(0x02, "secret").unwrap(),
])
.unwrap();
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_PASSTHROUGH_REQUEST,
PARENT_WIFI,
CHILD_WIFI_CONFIG,
wifi_payload
))
.unwrap(),
vec![
0x02, 0x00, 0x17, 0x00, 0x41, 0x06, 0x01, 0x12, 0x00, 0x01, 0x05, 0x00, 0x64, 0x65, 0x6D,
0x6F, 0x00, 0x02, 0x07, 0x00, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x00, 0xCF, 0x76, 0x60,
0x27, 0x03,
]
);
let file_start_payload = Tlv::encode(&[
TlvEntry::uint16(0x01, 0x0001).unwrap(),
TlvEntry::uint32(0x02, 9).unwrap(),
])
.unwrap();
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_REQUEST,
PARENT_FILE,
CHILD_FILE_START,
file_start_payload
))
.unwrap(),
vec![
0x02, 0x00, 0x11, 0x00, 0x01, 0x0A, 0x01, 0x0C, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x02,
0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0xE4, 0x15, 0xEE, 0x03,
]
);
assert_eq!(
FrameCodec::encode(&EmapiCommand::new(
TYPE_RESPONSE,
PARENT_REPORT,
CHILD_REPORT_FLOW_CONTROL,
[]
))
.unwrap(),
vec![0x02, 0x00, 0x05, 0x00, 0x02, 0x09, 0x03, 0x00, 0x00, 0xD8, 0x94, 0xDE, 0x47, 0x03,]
);
}
#[test]
fn test_dart_parity_decodes_frames_and_rejects_bad_crc() {
let frame = vec![
0x02, 0x00, 0x08, 0x00, 0x01, 0x02, 0x06, 0x03, 0x00, 0x10, 0xFF, 0xFE, 0x73, 0x34, 0x7F, 0x46,
0x03,
];
let command = FrameCodec::decode(&frame).unwrap();
assert_eq!(command.command_type, TYPE_REQUEST);
assert_eq!(command.parent, PARENT_PRINTER);
assert_eq!(command.child, CHILD_TRANSFER_DATA);
assert_eq!(command.payload, vec![0x10, 0xFF, 0xFE]);
let mut bad = frame;
bad[6] ^= 0x01;
assert!(FrameCodec::decode(&bad).is_err());
}
#[test]
fn test_dart_parity_parser_buffers_split_frames_and_multiple_frames() {
let first = FrameCodec::encode(&EmapiCommand::new(
TYPE_RESPONSE,
PARENT_PRINTER,
CHILD_TRANSFER_DATA,
[],
))
.unwrap();
let second = FrameCodec::encode(&EmapiCommand::new(
TYPE_RESPONSE,
PARENT_SYSTEM,
CHILD_DEVICE_INFO,
[],
))
.unwrap();
let mut parser = FrameParser::new();
let mut first_start = vec![0x99, 0x88];
first_start.extend_from_slice(&first[..2]);
assert!(parser.add(&first_start).unwrap().is_empty());
assert!(parser.add(&first[2..7]).unwrap().is_empty());
let parsed = parser.add(&first[7..]).unwrap();
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0].command_type, TYPE_RESPONSE);
assert_eq!(parsed[0].parent, PARENT_PRINTER);
assert_eq!(parsed[0].child, CHILD_TRANSFER_DATA);
assert!(parsed[0].payload.is_empty());
let mut parser = FrameParser::new();
let mut combined = first;
combined.extend_from_slice(&second);
let parsed = parser.add(&combined).unwrap();
assert_eq!(parsed.len(), 2);
assert_eq!(parsed[0].parent, PARENT_PRINTER);
assert_eq!(parsed[1].parent, PARENT_SYSTEM);
}
#[test]
fn test_dart_parity_tlv_encodes_and_decodes_helper_value_types() {
let encoded = Tlv::encode(&[
TlvEntry::uint8(0x01, 0x7F).unwrap(),
TlvEntry::uint16(TAG_MTU, 0x1234).unwrap(),
TlvEntry::uint32(0x09, 0x12345678).unwrap(),
TlvEntry::string(0x02, "Q3").unwrap(),
])
.unwrap();
assert_eq!(
encoded,
vec![
0x01, 0x01, 0x00, 0x7F, 0x08, 0x02, 0x00, 0x34, 0x12, 0x09, 0x04, 0x00, 0x78, 0x56, 0x34,
0x12, 0x02, 0x03, 0x00, 0x51, 0x33, 0x00,
]
);
let tlv = Tlv::decode(&encoded).unwrap();
assert_eq!(tlv.uint8(0x01).unwrap(), Some(0x7F));
assert_eq!(tlv.uint16(TAG_MTU).unwrap(), Some(0x1234));
assert_eq!(tlv.uint32(0x09).unwrap(), Some(0x12345678));
assert_eq!(tlv.string(0x02).unwrap(), Some("Q3".to_string()));
assert_eq!(tlv.get(0x02).unwrap().value, vec![0x51, 0x33, 0x00]);
assert!(Tlv::decode(&[0x01, 0x02]).is_err());
assert!(Tlv::decode(&[0x01, 0x02, 0x00, 0xAA]).is_err());
}
#[test]
fn test_dart_parity_chunking_and_file_payload_helpers() {
assert_eq!(print_transfer_payload_capacity(17).unwrap(), 3);
assert_eq!(
chunk_print_data(&[1, 2, 3, 4], 17).unwrap(),
vec![vec![1, 2, 3], vec![4]]
);
assert!(chunk_print_data(&[], 17).unwrap().is_empty());
assert!(chunk_print_data(&[1], 14).is_err());
assert_eq!(file_transfer_payload_capacity(4096).unwrap(), 4064);
assert_eq!(file_transfer_payload_capacity(512).unwrap(), 480);
assert_eq!(file_transfer_payload_capacity(50).unwrap(), 32);
assert!(file_transfer_payload_capacity(49).is_err());
assert_eq!(
EmapiPayload::file_chunk(2, &[1, 2, 3]).unwrap(),
vec![0x02, 0x00, 0x03, 0x00, 1, 2, 3]
);
assert!(EmapiPayload::file_chunk(0, &[]).is_err());
assert!(EmapiPayload::file_chunk(0x10000, &[]).is_err());
}