BragiResponse-Felder korrekt zugeordnet (endpoint=raw[1], command=raw[2], status=raw[3]) gemäß Protokoll-Doku. PropertyNotSupported durch DeviceError ersetzt, parse_response_validated in device.rs aktiviert, flush() mit Iterationslimit gegen Endlosschleifen, Sidetone-Range per clap validiert statt clamp, JSON-Escaping im hidpp-battery-waybar.sh, udev auf uaccess umgestellt. 52 Tests grün.
199 lines
5.8 KiB
Rust
199 lines
5.8 KiB
Rust
// ABOUTME: Unit-Tests für Bragi-Protokoll Packet-Bau und Response-Parsing.
|
|
// ABOUTME: Testet build_packet, parse_response und BragiResponse-Methoden.
|
|
|
|
use corsairctl::bragi::protocol::*;
|
|
use corsairctl::error::CorsairError;
|
|
|
|
#[test]
|
|
fn build_get_packet_has_correct_header() {
|
|
let packet = build_get_packet(ENDPOINT_HEADSET, 0x0F);
|
|
|
|
assert_eq!(packet[0], 0x00, "Report ID");
|
|
assert_eq!(packet[1], PROTOCOL_MARKER, "Protokoll-Marker");
|
|
assert_eq!(packet[2], ENDPOINT_HEADSET, "Endpoint");
|
|
assert_eq!(packet[3], CMD_GET, "Command");
|
|
assert_eq!(packet[4], 0x0F, "Property");
|
|
}
|
|
|
|
#[test]
|
|
fn build_get_packet_is_zero_padded() {
|
|
let packet = build_get_packet(ENDPOINT_RECEIVER, 0x13);
|
|
|
|
assert_eq!(packet.len(), PACKET_SIZE);
|
|
// Alle Bytes nach der Property sollten 0 sein
|
|
for &byte in &packet[5..] {
|
|
assert_eq!(byte, 0x00);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn build_set_packet_includes_data() {
|
|
let packet = build_set_packet(ENDPOINT_HEADSET, 0x03, &[0x00, 0x02]);
|
|
|
|
assert_eq!(packet[3], CMD_SET, "Command");
|
|
assert_eq!(packet[4], 0x03, "Property");
|
|
assert_eq!(packet[5], 0x00, "Data byte 0");
|
|
assert_eq!(packet[6], 0x02, "Data byte 1");
|
|
// Rest sollte 0 sein
|
|
assert_eq!(packet[7], 0x00);
|
|
}
|
|
|
|
#[test]
|
|
fn build_packet_total_size() {
|
|
let packet = build_packet(0x08, 0x01, 0x03, &[0x00, 0x02]);
|
|
assert_eq!(packet.len(), 65);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_extracts_fields() {
|
|
// Simulierte Response: [report_type, endpoint, command, status, data0, data1, ...]
|
|
// Laut Protokoll-Doku: raw[0]=0x01 Report-Typ, raw[1]=Endpoint, raw[2]=Command, raw[3]=Status
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let resp = parse_response(&raw).unwrap();
|
|
|
|
assert_eq!(resp.endpoint, RESPONSE_ENDPOINT_HEADSET);
|
|
assert_eq!(resp.command, CMD_GET);
|
|
assert_eq!(resp.status, STATUS_OK);
|
|
assert_eq!(resp.data[0], 0xE8);
|
|
assert_eq!(resp.data[1], 0x03);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_value_u16_little_endian() {
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let resp = parse_response(&raw).unwrap();
|
|
let value = resp.value_u16().unwrap();
|
|
|
|
// 0xE8 | (0x03 << 8) = 232 + 768 = 1000
|
|
assert_eq!(value, 1000);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_value_u8() {
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0x03];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let resp = parse_response(&raw).unwrap();
|
|
assert_eq!(resp.value_u8().unwrap(), 0x03);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_error_f0() {
|
|
let raw = vec![0x01, 0xF0, CMD_GET, STATUS_OK];
|
|
|
|
let result = parse_response(&raw);
|
|
assert!(result.is_err());
|
|
match result.unwrap_err() {
|
|
CorsairError::DeviceError { status } => assert_eq!(status, 0xF0),
|
|
other => panic!("Erwarteter DeviceError, bekommen: {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_error_f1() {
|
|
let raw = vec![0x01, 0xF1, CMD_GET, STATUS_OK];
|
|
|
|
let result = parse_response(&raw);
|
|
assert!(result.is_err());
|
|
match result.unwrap_err() {
|
|
CorsairError::DeviceError { status } => assert_eq!(status, 0xF1),
|
|
other => panic!("Erwarteter DeviceError, bekommen: {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_too_short() {
|
|
let raw = vec![0x01, 0x00];
|
|
|
|
let result = parse_response(&raw);
|
|
assert!(result.is_err());
|
|
assert!(matches!(
|
|
result.unwrap_err(),
|
|
CorsairError::ResponseTooShort { .. }
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn value_u16_on_empty_data_returns_error() {
|
|
let raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK]; // Keine Daten-Bytes
|
|
let resp = parse_response(&raw).unwrap();
|
|
|
|
let result = resp.value_u16();
|
|
assert!(result.is_err());
|
|
assert!(matches!(
|
|
result.unwrap_err(),
|
|
CorsairError::ResponseTooShort { expected: 2, got: 0 }
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn battery_level_full_is_1000_promille() {
|
|
// Battery Level = 1000 (voll) → 0xE8, 0x03 LE
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let resp = parse_response(&raw).unwrap();
|
|
let promille = resp.value_u16().unwrap();
|
|
assert_eq!(promille, 1000);
|
|
}
|
|
|
|
#[test]
|
|
fn battery_level_half_is_500_promille() {
|
|
// Battery Level = 500 (50%) → 0xF4, 0x01 LE
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xF4, 0x01];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let resp = parse_response(&raw).unwrap();
|
|
let promille = resp.value_u16().unwrap();
|
|
assert_eq!(promille, 500);
|
|
}
|
|
|
|
// --- Schritt 4: parse_response_validated Tests ---
|
|
|
|
#[test]
|
|
fn parse_response_validated_rejects_wrong_endpoint() {
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_RECEIVER, CMD_GET, STATUS_OK, 0x00];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let result = parse_response_validated(&raw, Some(RESPONSE_ENDPOINT_HEADSET), None);
|
|
assert!(result.is_err());
|
|
assert!(matches!(
|
|
result.unwrap_err(),
|
|
CorsairError::UnexpectedResponse { .. }
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_validated_rejects_wrong_command() {
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_SET, STATUS_OK, 0x00];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let result = parse_response_validated(&raw, None, Some(CMD_GET));
|
|
assert!(result.is_err());
|
|
assert!(matches!(
|
|
result.unwrap_err(),
|
|
CorsairError::UnexpectedResponse { .. }
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_response_validated_accepts_matching() {
|
|
let mut raw = vec![0x01, RESPONSE_ENDPOINT_HEADSET, CMD_GET, STATUS_OK, 0xE8, 0x03];
|
|
raw.resize(REPORT_SIZE, 0x00);
|
|
|
|
let resp = parse_response_validated(
|
|
&raw,
|
|
Some(RESPONSE_ENDPOINT_HEADSET),
|
|
Some(CMD_GET),
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(resp.endpoint, RESPONSE_ENDPOINT_HEADSET);
|
|
assert_eq!(resp.command, CMD_GET);
|
|
assert_eq!(resp.status, STATUS_OK);
|
|
}
|