Desktop Phase 1: add 77 tests to evdevd, udev-shim, firmware-loader

Phase 1 desktop substrate test coverage for the three runtime services
that must be runtime-trusted before compositor work begins:

- evdevd device.rs: 44 tests (input device constructors, capability
  bitmaps, key/led state tracking, abs_info defaults/overrides,
  bitmap_from_codes edge cases)
- udev-shim device_db.rs: 13 tests (DeviceInfo construction, subsystem
  naming, id_path formatting, input kind detection, property formatting
  for GPU/keyboard/mouse, device_info/uevent output, PCI fallback)
- firmware-loader scheme.rs: 20 tests (openat validation, read offsets,
  fstat/fsize, EROFS enforcement, mmap_prep bounds, mmap/munmap/on_close
  deferred cleanup lifecycle)

Total: 65 evdevd, 15 udev-shim, 24 firmware-loader — all passing.
This commit is contained in:
2026-04-25 01:05:14 +01:00
parent f7ffafa0c4
commit 3ec32b3351
3 changed files with 1071 additions and 1 deletions
@@ -230,3 +230,458 @@ fn bitmap_from_codes(codes: &[u16]) -> Vec<u8> {
}
bitmap
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{
ABS_MT_POSITION_X, ABS_MT_POSITION_Y, ABS_MT_SLOT, ABS_MT_TOUCH_MAJOR, ABS_MT_TRACKING_ID,
ABS_PRESSURE, ABS_X, ABS_Y, BTN_LEFT, BTN_TOUCH, BUS_VIRTUAL, EV_ABS, EV_KEY, EV_LED,
EV_MSC, EV_REL, EV_REP, EV_SYN, INPUT_PROP_POINTER, KEY_A, LED_CAPSL, LED_NUML,
LED_SCROLLL, MSC_SCAN, REL_HWHEEL, REL_WHEEL, REL_X, REL_Y,
};
// Helper: check that a bitmap has the expected bit set for a given code.
fn assert_bit_set(bitmap: &[u8], code: u16) {
let byte = (code / 8) as usize;
let bit = code % 8;
assert!(
byte < bitmap.len(),
"code {} (byte index {}) out of bitmap range (len {})",
code,
byte,
bitmap.len()
);
assert_eq!(
bitmap[byte] & (1 << bit),
1 << bit,
"bit {} in byte {} not set for code {}",
bit,
byte,
code
);
}
fn assert_bit_clear(bitmap: &[u8], code: u16) {
let byte = (code / 8) as usize;
let bit = code % 8;
if byte < bitmap.len() {
assert_eq!(
bitmap[byte] & (1 << bit),
0,
"bit {} in byte {} unexpectedly set for code {}",
bit,
byte,
code
);
}
}
// ---------------------------------------------------------------
// 1. InputDevice::new_keyboard
// ---------------------------------------------------------------
#[test]
fn new_keyboard_has_correct_kind() {
let dev = InputDevice::new_keyboard(3);
assert_eq!(dev.kind, DeviceKind::Keyboard);
}
#[test]
fn new_keyboard_name_format() {
let dev = InputDevice::new_keyboard(7);
assert_eq!(dev.name, "Redox Keyboard 7");
}
#[test]
fn new_keyboard_input_id_bus_virtual() {
let dev = InputDevice::new_keyboard(1);
assert_eq!(dev.input_id.bustype, BUS_VIRTUAL);
assert_eq!(dev.input_id.vendor, 0);
assert_eq!(dev.input_id.product, 1);
assert_eq!(dev.input_id.version, 1);
}
#[test]
fn new_keyboard_key_and_led_state_zeroed() {
let dev = InputDevice::new_keyboard(0);
assert!(dev.key_state.iter().all(|&b| b == 0));
assert!(dev.led_state.iter().all(|&b| b == 0));
}
// ---------------------------------------------------------------
// 2. InputDevice::new_mouse
// ---------------------------------------------------------------
#[test]
fn new_mouse_has_correct_kind() {
let dev = InputDevice::new_mouse(2);
assert_eq!(dev.kind, DeviceKind::Mouse);
}
#[test]
fn new_mouse_name_format() {
let dev = InputDevice::new_mouse(5);
assert_eq!(dev.name, "Redox Mouse 5");
}
#[test]
fn new_mouse_product_id_offset() {
let dev = InputDevice::new_mouse(3);
assert_eq!(dev.input_id.product, 3 + 0x10);
assert_eq!(dev.input_id.bustype, BUS_VIRTUAL);
assert_eq!(dev.input_id.version, 1);
}
#[test]
fn new_mouse_key_and_led_state_zeroed() {
let dev = InputDevice::new_mouse(0);
assert!(dev.key_state.iter().all(|&b| b == 0));
assert!(dev.led_state.iter().all(|&b| b == 0));
}
// ---------------------------------------------------------------
// 3. InputDevice::new_touchpad
// ---------------------------------------------------------------
#[test]
fn new_touchpad_has_correct_kind() {
let dev = InputDevice::new_touchpad(1);
assert_eq!(dev.kind, DeviceKind::Touchpad);
}
#[test]
fn new_touchpad_name_format() {
let dev = InputDevice::new_touchpad(4);
assert_eq!(dev.name, "Redox Touchpad 4");
}
#[test]
fn new_touchpad_product_id_offset() {
let dev = InputDevice::new_touchpad(2);
assert_eq!(dev.input_id.product, 2 + 0x20);
assert_eq!(dev.input_id.bustype, BUS_VIRTUAL);
assert_eq!(dev.input_id.version, 1);
}
// ---------------------------------------------------------------
// 4. supported_event_types
// ---------------------------------------------------------------
#[test]
fn keyboard_event_types() {
let dev = InputDevice::new_keyboard(0);
let bm = dev.supported_event_types();
assert_bit_set(&bm, EV_SYN);
assert_bit_set(&bm, EV_KEY);
assert_bit_set(&bm, EV_MSC);
assert_bit_set(&bm, EV_LED);
assert_bit_set(&bm, EV_REP);
}
#[test]
fn mouse_event_types() {
let dev = InputDevice::new_mouse(0);
let bm = dev.supported_event_types();
assert_bit_set(&bm, EV_SYN);
assert_bit_set(&bm, EV_KEY);
assert_bit_set(&bm, EV_REL);
// No EV_ABS for mouse
assert_bit_clear(&bm, EV_ABS);
}
#[test]
fn touchpad_event_types() {
let dev = InputDevice::new_touchpad(0);
let bm = dev.supported_event_types();
assert_bit_set(&bm, EV_SYN);
assert_bit_set(&bm, EV_KEY);
assert_bit_set(&bm, EV_ABS);
// No EV_REL for touchpad
assert_bit_clear(&bm, EV_REL);
}
// ---------------------------------------------------------------
// 5. supported_keys
// ---------------------------------------------------------------
#[test]
fn keyboard_has_key_a() {
let dev = InputDevice::new_keyboard(0);
let bm = dev.supported_keys();
assert_bit_set(&bm, KEY_A);
}
#[test]
fn mouse_has_btn_left() {
let dev = InputDevice::new_mouse(0);
let bm = dev.supported_keys();
assert_bit_set(&bm, BTN_LEFT);
}
#[test]
fn touchpad_has_btn_touch() {
let dev = InputDevice::new_touchpad(0);
let bm = dev.supported_keys();
assert_bit_set(&bm, BTN_TOUCH);
}
// ---------------------------------------------------------------
// 6. supported_rel
// ---------------------------------------------------------------
#[test]
fn mouse_rel_axes() {
let dev = InputDevice::new_mouse(0);
let bm = dev.supported_rel();
assert_bit_set(&bm, REL_X);
assert_bit_set(&bm, REL_Y);
assert_bit_set(&bm, REL_WHEEL);
assert_bit_set(&bm, REL_HWHEEL);
}
#[test]
fn keyboard_rel_empty() {
let dev = InputDevice::new_keyboard(0);
assert!(dev.supported_rel().is_empty());
}
#[test]
fn touchpad_rel_empty() {
let dev = InputDevice::new_touchpad(0);
assert!(dev.supported_rel().is_empty());
}
// ---------------------------------------------------------------
// 7. supported_abs
// ---------------------------------------------------------------
#[test]
fn touchpad_abs_axes() {
let dev = InputDevice::new_touchpad(0);
let bm = dev.supported_abs();
assert_bit_set(&bm, ABS_X);
assert_bit_set(&bm, ABS_Y);
assert_bit_set(&bm, ABS_PRESSURE);
assert_bit_set(&bm, ABS_MT_SLOT);
assert_bit_set(&bm, ABS_MT_TOUCH_MAJOR);
assert_bit_set(&bm, ABS_MT_POSITION_X);
assert_bit_set(&bm, ABS_MT_POSITION_Y);
assert_bit_set(&bm, ABS_MT_TRACKING_ID);
}
#[test]
fn keyboard_abs_empty() {
let dev = InputDevice::new_keyboard(0);
assert!(dev.supported_abs().is_empty());
}
#[test]
fn mouse_abs_empty() {
let dev = InputDevice::new_mouse(0);
assert!(dev.supported_abs().is_empty());
}
// ---------------------------------------------------------------
// 8. supported_msc
// ---------------------------------------------------------------
#[test]
fn keyboard_msc_has_scan() {
let dev = InputDevice::new_keyboard(0);
let bm = dev.supported_msc();
assert_bit_set(&bm, MSC_SCAN);
}
#[test]
fn mouse_msc_empty() {
let dev = InputDevice::new_mouse(0);
assert!(dev.supported_msc().is_empty());
}
#[test]
fn touchpad_msc_empty() {
let dev = InputDevice::new_touchpad(0);
assert!(dev.supported_msc().is_empty());
}
// ---------------------------------------------------------------
// 9. supported_leds
// ---------------------------------------------------------------
#[test]
fn keyboard_leds() {
let dev = InputDevice::new_keyboard(0);
let bm = dev.supported_leds();
assert_bit_set(&bm, LED_NUML);
assert_bit_set(&bm, LED_CAPSL);
assert_bit_set(&bm, LED_SCROLLL);
}
#[test]
fn mouse_leds_empty() {
let dev = InputDevice::new_mouse(0);
assert!(dev.supported_leds().is_empty());
}
#[test]
fn touchpad_leds_empty() {
let dev = InputDevice::new_touchpad(0);
assert!(dev.supported_leds().is_empty());
}
// ---------------------------------------------------------------
// 10. supported_props
// ---------------------------------------------------------------
#[test]
fn mouse_has_pointer_prop() {
let dev = InputDevice::new_mouse(0);
let bm = dev.supported_props();
assert_bit_set(&bm, INPUT_PROP_POINTER);
}
#[test]
fn touchpad_has_pointer_prop() {
let dev = InputDevice::new_touchpad(0);
let bm = dev.supported_props();
assert_bit_set(&bm, INPUT_PROP_POINTER);
}
#[test]
fn keyboard_props_empty() {
let dev = InputDevice::new_keyboard(0);
assert!(dev.supported_props().is_empty());
}
// ---------------------------------------------------------------
// 11. update_key_state
// ---------------------------------------------------------------
#[test]
fn update_key_state_press_sets_bit() {
let mut dev = InputDevice::new_keyboard(0);
// KEY_A = 30 → byte 3, bit 6
dev.update_key_state(KEY_A, true);
let byte = (KEY_A / 8) as usize;
let bit = KEY_A % 8;
assert_eq!(dev.key_state[byte] & (1 << bit), 1 << bit);
}
#[test]
fn update_key_state_release_clears_bit() {
let mut dev = InputDevice::new_keyboard(0);
dev.update_key_state(KEY_A, true);
assert_bit_set(&dev.key_state, KEY_A);
dev.update_key_state(KEY_A, false);
assert_bit_clear(&dev.key_state, KEY_A);
}
// ---------------------------------------------------------------
// 12. update_led_state
// ---------------------------------------------------------------
#[test]
fn update_led_state_set_capsl() {
let mut dev = InputDevice::new_keyboard(0);
// LED_CAPSL = 1 → byte 0, bit 1
dev.update_led_state(LED_CAPSL, true);
let byte = (LED_CAPSL / 8) as usize;
let bit = LED_CAPSL % 8;
assert_eq!(dev.led_state[byte] & (1 << bit), 1 << bit);
}
#[test]
fn update_led_state_clear_capsl() {
let mut dev = InputDevice::new_keyboard(0);
dev.update_led_state(LED_CAPSL, true);
assert_bit_set(&dev.led_state, LED_CAPSL);
dev.update_led_state(LED_CAPSL, false);
assert_bit_clear(&dev.led_state, LED_CAPSL);
}
// ---------------------------------------------------------------
// 13. abs_info default touchpad
// ---------------------------------------------------------------
#[test]
fn touchpad_abs_x_range() {
let dev = InputDevice::new_touchpad(0);
let info = dev.abs_info(ABS_X);
assert_eq!(info.minimum, 0);
assert_eq!(info.maximum, 65_535);
}
#[test]
fn touchpad_abs_pressure_max() {
let dev = InputDevice::new_touchpad(0);
let info = dev.abs_info(ABS_PRESSURE);
assert_eq!(info.maximum, 255);
}
#[test]
fn touchpad_abs_mt_slot_range() {
let dev = InputDevice::new_touchpad(0);
let info = dev.abs_info(ABS_MT_SLOT);
assert_eq!(info.minimum, 0);
assert_eq!(info.maximum, 9);
}
// ---------------------------------------------------------------
// 14. set_abs_info overrides default
// ---------------------------------------------------------------
#[test]
fn set_abs_info_override() {
let mut dev = InputDevice::new_touchpad(0);
let custom = AbsInfo {
value: 42,
minimum: -100,
maximum: 100,
fuzz: 1,
flat: 2,
resolution: 3,
};
dev.set_abs_info(ABS_X, custom);
let info = dev.abs_info(ABS_X);
assert_eq!(info.value, 42);
assert_eq!(info.minimum, -100);
assert_eq!(info.maximum, 100);
assert_eq!(info.fuzz, 1);
assert_eq!(info.flat, 2);
assert_eq!(info.resolution, 3);
}
#[test]
fn set_abs_info_does_not_affect_other_axes() {
let mut dev = InputDevice::new_touchpad(0);
let custom = AbsInfo {
value: 0,
minimum: -50,
maximum: 50,
..AbsInfo::default()
};
dev.set_abs_info(ABS_X, custom);
// ABS_Y should still return the default touchpad range
let info_y = dev.abs_info(ABS_Y);
assert_eq!(info_y.minimum, 0);
assert_eq!(info_y.maximum, 65_535);
}
// ---------------------------------------------------------------
// 15. bitmap_from_codes edge cases
// ---------------------------------------------------------------
#[test]
fn bitmap_from_codes_empty_input() {
let bm = bitmap_from_codes(&[]);
assert!(bm.is_empty());
}
#[test]
fn bitmap_from_codes_single_code() {
let bm = bitmap_from_codes(&[5u16]);
assert_eq!(bm.len(), 1); // (5/8)+1 = 1
assert_eq!(bm[0], 1 << 5);
}
#[test]
fn bitmap_from_codes_multiple_codes_same_byte() {
// REL_X=0, REL_Y=1, REL_WHEEL=8, REL_HWHEEL=6
// REL_X and REL_Y are in byte 0; REL_HWHEEL is in byte 0 too; REL_WHEEL is byte 1
let bm = bitmap_from_codes(&[REL_X, REL_Y, REL_HWHEEL, REL_WHEEL]);
assert_bit_set(&bm, REL_X);
assert_bit_set(&bm, REL_Y);
assert_bit_set(&bm, REL_WHEEL);
assert_bit_set(&bm, REL_HWHEEL);
// Byte 0 should have bits 0 (REL_X), 1 (REL_Y), 6 (REL_HWHEEL) set
assert_eq!(bm[0], (1 << 0) | (1 << 1) | (1 << 6));
// Byte 1 should have bit 0 (REL_WHEEL = 8 → byte 1, bit 0)
assert_eq!(bm[1], 1 << 0);
}
}