Desktop Phase 1: add 42 tests to redox-drm scheme/driver and redbear-hwutils
This commit is contained in:
@@ -113,3 +113,90 @@ pub trait GpuDriver: Send + Sync {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::mem::{discriminant, offset_of, size_of};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn redox_private_cs_submit_size() {
|
||||
// src_handle(u32) + dst_handle(u32) + src_offset(u64) + dst_offset(u64) + byte_count(u64)
|
||||
// = 4 + 4 + 8 + 8 + 8 = 32 bytes
|
||||
assert_eq!(size_of::<RedoxPrivateCsSubmit>(), 32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redox_private_cs_submit_result_size() {
|
||||
// seqno(u64) = 8 bytes
|
||||
assert_eq!(size_of::<RedoxPrivateCsSubmitResult>(), 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redox_private_cs_wait_size() {
|
||||
// seqno(u64) + timeout_ns(u64) = 16 bytes
|
||||
assert_eq!(size_of::<RedoxPrivateCsWait>(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redox_private_cs_wait_result_size() {
|
||||
// completed(bool) + 7 padding + completed_seqno(u64) = 16 bytes
|
||||
assert_eq!(size_of::<RedoxPrivateCsWaitResult>(), 16);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn driver_event_vblank_size() {
|
||||
let event = DriverEvent::Vblank {
|
||||
crtc_id: 0xDEADBEEF,
|
||||
count: 0x1234_5678_9ABC_DEF0,
|
||||
};
|
||||
match event {
|
||||
DriverEvent::Vblank { crtc_id, count } => {
|
||||
assert_eq!(crtc_id, 0xDEADBEEF);
|
||||
assert_eq!(count, 0x1234_5678_9ABC_DEF0);
|
||||
}
|
||||
DriverEvent::Hotplug { .. } => panic!("expected Vblank, got Hotplug"),
|
||||
}
|
||||
let enum_size = size_of::<DriverEvent>();
|
||||
assert!(enum_size > 0, "DriverEvent must be non-zero-sized");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn driver_event_hotplug_size() {
|
||||
let event = DriverEvent::Hotplug {
|
||||
connector_id: 0xCAFEBABE,
|
||||
};
|
||||
match event {
|
||||
DriverEvent::Hotplug { connector_id } => {
|
||||
assert_eq!(connector_id, 0xCAFEBABE);
|
||||
}
|
||||
DriverEvent::Vblank { .. } => panic!("expected Hotplug, got Vblank"),
|
||||
}
|
||||
let vblank = DriverEvent::Vblank {
|
||||
crtc_id: 0,
|
||||
count: 0,
|
||||
};
|
||||
let hotplug = DriverEvent::Hotplug { connector_id: 0 };
|
||||
assert_ne!(
|
||||
discriminant(&vblank),
|
||||
discriminant(&hotplug),
|
||||
"Vblank and Hotplug must have distinct discriminants"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redox_private_cs_submit_is_repr_c() {
|
||||
assert_eq!(offset_of!(RedoxPrivateCsSubmit, src_handle), 0);
|
||||
assert_eq!(offset_of!(RedoxPrivateCsSubmit, dst_handle), 4);
|
||||
assert_eq!(offset_of!(RedoxPrivateCsSubmit, src_offset), 8);
|
||||
assert_eq!(offset_of!(RedoxPrivateCsSubmit, dst_offset), 16);
|
||||
assert_eq!(offset_of!(RedoxPrivateCsSubmit, byte_count), 24);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redox_private_cs_wait_is_repr_c() {
|
||||
assert_eq!(offset_of!(RedoxPrivateCsWait, seqno), 0);
|
||||
assert_eq!(offset_of!(RedoxPrivateCsWait, timeout_ns), 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2168,4 +2168,364 @@ mod tests {
|
||||
|
||||
assert_eq!(err.errno, EINVAL);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_has_other_refs_returns_false_when_only_current_handle_owns_gem() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 4096,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
|
||||
let gem_handle = created.handle;
|
||||
assert!(
|
||||
!scheme.gem_has_other_refs(card, gem_handle),
|
||||
"only one handle owns the GEM, so gem_has_other_refs should be false"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_has_other_refs_returns_true_when_another_handle_owns_gem() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card_a = open_card(&mut scheme);
|
||||
let card_b = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 4096,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card_a, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card_a);
|
||||
let gem_handle = created.handle;
|
||||
|
||||
let export = DrmPrimeHandleToFdWire {
|
||||
handle: gem_handle,
|
||||
flags: 0,
|
||||
};
|
||||
write_ioctl(&mut scheme, card_a, DRM_IOCTL_PRIME_HANDLE_TO_FD, &export).unwrap();
|
||||
let exported = read_response::<DrmPrimeHandleToFdResponseWire>(&mut scheme, card_a);
|
||||
|
||||
let import = DrmPrimeFdToHandleWire {
|
||||
fd: exported.fd,
|
||||
_pad: 0,
|
||||
};
|
||||
write_ioctl(&mut scheme, card_b, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import).unwrap();
|
||||
let imported = read_response::<DrmPrimeFdToHandleResponseWire>(&mut scheme, card_b);
|
||||
|
||||
assert!(
|
||||
scheme.gem_has_other_refs(card_a, imported.handle),
|
||||
"card_b owns the same GEM, so gem_has_other_refs from card_a should be true"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_is_mapped_returns_false_initially() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 4096,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
|
||||
assert!(
|
||||
!scheme.gem_is_mapped(created.handle),
|
||||
"freshly created GEM should not be mapped"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_is_mapped_returns_true_after_pin() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 4096,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
let gem_handle = created.handle;
|
||||
|
||||
scheme.pin_mapped_gem(card, gem_handle).unwrap();
|
||||
|
||||
assert!(
|
||||
scheme.gem_is_mapped(gem_handle),
|
||||
"GEM should be mapped after pin_mapped_gem"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_export_refcount_starts_at_zero() {
|
||||
let scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
|
||||
assert_eq!(
|
||||
scheme.gem_export_refcount(9999),
|
||||
0,
|
||||
"unknown GEM should have refcount 0"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bump_export_ref_increments_from_zero_to_one() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let gem_handle: GemHandle = 42;
|
||||
|
||||
scheme.bump_export_ref(gem_handle);
|
||||
|
||||
assert_eq!(
|
||||
scheme.gem_export_refcount(gem_handle),
|
||||
1,
|
||||
"bumping an unknown GEM should set its refcount to 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bump_export_ref_saturates_on_overflow() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let gem_handle: GemHandle = 42;
|
||||
|
||||
scheme.gem_export_refs.insert(gem_handle, usize::MAX);
|
||||
|
||||
scheme.bump_export_ref(gem_handle);
|
||||
|
||||
assert_eq!(
|
||||
scheme.gem_export_refcount(gem_handle),
|
||||
usize::MAX,
|
||||
"saturating add should keep refcount at usize::MAX"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_export_ref_decrements_count() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let gem_handle: GemHandle = 42;
|
||||
|
||||
scheme.bump_export_ref(gem_handle);
|
||||
scheme.bump_export_ref(gem_handle);
|
||||
assert_eq!(scheme.gem_export_refcount(gem_handle), 2);
|
||||
|
||||
scheme.drop_export_ref(gem_handle);
|
||||
|
||||
assert_eq!(
|
||||
scheme.gem_export_refcount(gem_handle),
|
||||
1,
|
||||
"dropping once should decrement from 2 to 1"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_export_ref_removes_entry_when_count_reaches_zero() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let gem_handle: GemHandle = 42;
|
||||
|
||||
scheme.bump_export_ref(gem_handle);
|
||||
assert_eq!(scheme.gem_export_refcount(gem_handle), 1);
|
||||
|
||||
scheme.drop_export_ref(gem_handle);
|
||||
|
||||
assert_eq!(
|
||||
scheme.gem_export_refcount(gem_handle),
|
||||
0,
|
||||
"dropping the last ref should remove the entry"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_export_ref_cleans_up_prime_exports() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let gem_handle: GemHandle = 77;
|
||||
let export_token: u32 = 100;
|
||||
|
||||
scheme.prime_exports.insert(export_token, gem_handle);
|
||||
scheme.bump_export_ref(gem_handle);
|
||||
assert_eq!(scheme.gem_export_refcount(gem_handle), 1);
|
||||
assert!(scheme.prime_exports.contains_key(&export_token));
|
||||
|
||||
scheme.drop_export_ref(gem_handle);
|
||||
|
||||
assert_eq!(scheme.gem_export_refcount(gem_handle), 0);
|
||||
assert!(
|
||||
!scheme.prime_exports.values().any(|&h| h == gem_handle),
|
||||
"drop_export_ref should clean up prime_exports entries for this GEM"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_can_close_returns_false_when_gem_backs_framebuffer() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 16384,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
let gem_handle = created.handle;
|
||||
|
||||
let addfb = DrmAddFbWire {
|
||||
width: 64,
|
||||
height: 64,
|
||||
pitch: 256,
|
||||
bpp: 32,
|
||||
depth: 24,
|
||||
handle: gem_handle,
|
||||
fb_id: 0,
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_MODE_ADDFB, &addfb).unwrap();
|
||||
let fb_resp = read_response::<DrmAddFbWire>(&mut scheme, card);
|
||||
|
||||
if let Some(handle) = scheme.handles.get_mut(&card) {
|
||||
handle.owned_gems.retain(|&h| h != gem_handle);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!scheme.gem_can_close(gem_handle),
|
||||
"GEM backing a framebuffer should not be closeable"
|
||||
);
|
||||
assert!(scheme.fb_registry.contains_key(&fb_resp.fb_id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_can_close_returns_false_when_gem_is_mapped() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 4096,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
let gem_handle = created.handle;
|
||||
|
||||
scheme.pin_mapped_gem(card, gem_handle).unwrap();
|
||||
|
||||
if let Some(handle) = scheme.handles.get_mut(&card) {
|
||||
handle.owned_gems.retain(|&h| h != gem_handle);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!scheme.gem_can_close(gem_handle),
|
||||
"mapped GEM should not be closeable"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gem_can_close_returns_true_when_unreferenced() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 4096,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
let gem_handle = created.handle;
|
||||
|
||||
if let Some(handle) = scheme.handles.get_mut(&card) {
|
||||
handle.owned_gems.retain(|&h| h != gem_handle);
|
||||
}
|
||||
|
||||
assert!(
|
||||
scheme.gem_can_close(gem_handle),
|
||||
"unreferenced, unmapped GEM with no FB or export refs should be closeable"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allocate_handle_returns_sequential_ids() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
|
||||
let id_a = scheme.allocate_handle(NodeKind::Card);
|
||||
let id_b = scheme.allocate_handle(NodeKind::Card);
|
||||
|
||||
assert!(
|
||||
id_b > id_a,
|
||||
"second allocated handle ID ({id_b}) should be greater than first ({id_a})"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_fb_active_returns_false_for_unknown_fb() {
|
||||
let scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
|
||||
assert!(
|
||||
!scheme.is_fb_active(12345),
|
||||
"unknown fb_id should not be active"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_fb_active_returns_true_for_active_crtc_fb() {
|
||||
let mut scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
let card = open_card(&mut scheme);
|
||||
|
||||
let create = DrmGemCreateWire {
|
||||
size: 640 * 480 * 4,
|
||||
..DrmGemCreateWire::default()
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_GEM_CREATE, &create).unwrap();
|
||||
let created = read_response::<DrmGemCreateWire>(&mut scheme, card);
|
||||
let gem_handle = created.handle;
|
||||
|
||||
let addfb = DrmAddFbWire {
|
||||
width: 640,
|
||||
height: 480,
|
||||
pitch: 2560,
|
||||
bpp: 32,
|
||||
depth: 24,
|
||||
handle: gem_handle,
|
||||
fb_id: 0,
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_MODE_ADDFB, &addfb).unwrap();
|
||||
let fb_resp = read_response::<DrmAddFbWire>(&mut scheme, card);
|
||||
let fb_id = fb_resp.fb_id;
|
||||
|
||||
let mode = DrmModeWire {
|
||||
clock: 25200,
|
||||
hdisplay: 640,
|
||||
hsync_start: 656,
|
||||
hsync_end: 752,
|
||||
htotal: 800,
|
||||
vdisplay: 480,
|
||||
vsync_start: 490,
|
||||
vsync_end: 492,
|
||||
vtotal: 525,
|
||||
vrefresh: 60,
|
||||
..DrmModeWire::default()
|
||||
};
|
||||
let setcrtc = DrmSetCrtcWire {
|
||||
crtc_id: 0,
|
||||
fb_handle: fb_id,
|
||||
connector_count: 0,
|
||||
connectors: [0; 8],
|
||||
mode,
|
||||
};
|
||||
write_ioctl(&mut scheme, card, DRM_IOCTL_MODE_SETCRTC, &setcrtc).unwrap();
|
||||
|
||||
assert!(
|
||||
scheme.is_fb_active(fb_id),
|
||||
"FB programmed on a CRTC should be active"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_gem_create_size_rejects_zero() {
|
||||
let scheme = DrmScheme::new(Arc::new(FakeDriver::new(false)));
|
||||
|
||||
let err = scheme
|
||||
.validate_gem_create_size(0, "test-zero-size")
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(err.errno, EINVAL, "zero-sized GEM creation should return EINVAL");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user